titiler-extensions 0.17.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- titiler.extensions-0.17.3/PKG-INFO +185 -0
- titiler.extensions-0.17.3/README.md +156 -0
- titiler.extensions-0.17.3/pyproject.toml +80 -0
- titiler.extensions-0.17.3/titiler/extensions/__init__.py +8 -0
- titiler.extensions-0.17.3/titiler/extensions/cogeo.py +43 -0
- titiler.extensions-0.17.3/titiler/extensions/py.typed +0 -0
- titiler.extensions-0.17.3/titiler/extensions/stac.py +166 -0
- titiler.extensions-0.17.3/titiler/extensions/templates/cog_viewer.html +912 -0
- titiler.extensions-0.17.3/titiler/extensions/templates/stac_viewer.html +1031 -0
- titiler.extensions-0.17.3/titiler/extensions/templates/wms_1.0.0.xml +64 -0
- titiler.extensions-0.17.3/titiler/extensions/templates/wms_1.1.1.xml +74 -0
- titiler.extensions-0.17.3/titiler/extensions/templates/wms_1.3.0.xml +88 -0
- titiler.extensions-0.17.3/titiler/extensions/viewer.py +63 -0
- titiler.extensions-0.17.3/titiler/extensions/wms.py +598 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: titiler.extensions
|
|
3
|
+
Version: 0.17.3
|
|
4
|
+
Summary: Extensions for TiTiler Factories.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: COG,STAC,MosaicJSON,Fastapi,Dynamic tile server,GDAL,Rasterio,OGC
|
|
7
|
+
Author-email: Vincent Sarago <vincent@developmentseed.com>
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Classifier: Intended Audience :: Information Technology
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
19
|
+
Provides-Extra: cogeo
|
|
20
|
+
Provides-Extra: stac
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Project-URL: Changelog, https://developmentseed.org/titiler/release-notes/
|
|
23
|
+
Project-URL: Documentation, https://developmentseed.org/titiler/
|
|
24
|
+
Project-URL: Homepage, https://developmentseed.org/titiler/
|
|
25
|
+
Project-URL: Issues, https://github.com/developmentseed/titiler/issues
|
|
26
|
+
Project-URL: Source, https://github.com/developmentseed/titiler
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
## titiler.extensions
|
|
30
|
+
|
|
31
|
+
Extent TiTiler Tiler Factories
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
$ python -m pip install -U pip
|
|
37
|
+
|
|
38
|
+
# From Pypi
|
|
39
|
+
$ python -m pip install titiler.extensions
|
|
40
|
+
|
|
41
|
+
# Or from sources
|
|
42
|
+
$ git clone https://github.com/developmentseed/titiler.git
|
|
43
|
+
$ cd titiler && python -m pip install -e src/titiler/core -e src/titiler/extensions
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Available extensions
|
|
47
|
+
|
|
48
|
+
#### cogValidateExtension
|
|
49
|
+
|
|
50
|
+
- Goal: adds a `/validate` endpoint which return the content of rio-cogeo `info` method
|
|
51
|
+
- Additional requirements: `titiler.extensions["cogeo"]`
|
|
52
|
+
|
|
53
|
+
#### cogViewerExtension
|
|
54
|
+
|
|
55
|
+
- Goal: adds a `/viewer` endpoint which return an HTML viewer for simple COGs
|
|
56
|
+
|
|
57
|
+
#### stacViewerExtension
|
|
58
|
+
|
|
59
|
+
- Goal: adds a `/viewer` endpoint which return an HTML viewer for STAC item
|
|
60
|
+
|
|
61
|
+
#### wmsExtension
|
|
62
|
+
|
|
63
|
+
- Goal: adds a `/wms` endpoint to support OGC Web Map Service (`GetTile` and `GetCapabilities`) specification
|
|
64
|
+
|
|
65
|
+
## Use extensions
|
|
66
|
+
|
|
67
|
+
Extensions must be set at TilerFactory's creation using the `extensions=` options.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from fastapi import FastAPI
|
|
71
|
+
from titiler.core.factory import TilerFactory
|
|
72
|
+
from titiler.extensions import cogValidateExtension
|
|
73
|
+
|
|
74
|
+
# Create a FastAPI application
|
|
75
|
+
app = FastAPI(description="A lightweight Cloud Optimized GeoTIFF tile server")
|
|
76
|
+
|
|
77
|
+
# Create a set of endpoints using TiTiler TilerFactory
|
|
78
|
+
tiler = TilerFactory(
|
|
79
|
+
router_prefix="/cog",
|
|
80
|
+
extensions=[
|
|
81
|
+
cogValidateExtension() # the cogeoExtension will add a rio-cogeo /validate endpoint
|
|
82
|
+
]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Register endpoints to the application
|
|
86
|
+
app.include_router(tiler.router, prefix="/cog")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
See [titiler.application](../application) for a full example.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## Create your own
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from dataclasses import dataclass, field
|
|
96
|
+
from typing import Tuple, List, Optional
|
|
97
|
+
|
|
98
|
+
from starlette.responses import Response
|
|
99
|
+
from fastapi import Depends, FastAPI, Query
|
|
100
|
+
from titiler.core.factory import BaseTilerFactory, FactoryExtension, TilerFactory
|
|
101
|
+
from titiler.core.dependencies import RescalingParams
|
|
102
|
+
from titiler.core.factory import TilerFactory
|
|
103
|
+
from titiler.core.resources.enums import ImageType
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class thumbnailExtension(FactoryExtension):
|
|
108
|
+
"""Add endpoint to a TilerFactory."""
|
|
109
|
+
|
|
110
|
+
# Set some options
|
|
111
|
+
max_size: int = field(default=128)
|
|
112
|
+
|
|
113
|
+
# Register method is mandatory and must take a BaseTilerFactory object as input
|
|
114
|
+
def register(self, factory: BaseTilerFactory):
|
|
115
|
+
"""Register endpoint to the tiler factory."""
|
|
116
|
+
|
|
117
|
+
# register an endpoint to the factory's router
|
|
118
|
+
@factory.router.get(
|
|
119
|
+
"/thumbnail",
|
|
120
|
+
responses={
|
|
121
|
+
200: {
|
|
122
|
+
"content": {
|
|
123
|
+
"image/png": {},
|
|
124
|
+
"image/jpeg": {},
|
|
125
|
+
},
|
|
126
|
+
"description": "Return an image.",
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
response_class=Response,
|
|
130
|
+
)
|
|
131
|
+
def thumbnail(
|
|
132
|
+
# we can reuse the factory dependency
|
|
133
|
+
src_path: str = Depends(factory.path_dependency),
|
|
134
|
+
layer_params=Depends(factory.layer_dependency),
|
|
135
|
+
dataset_params=Depends(factory.dataset_dependency),
|
|
136
|
+
post_process=Depends(factory.process_dependency),
|
|
137
|
+
rescale: Optional[List[Tuple[float, ...]]] = Depends(RescalingParams),
|
|
138
|
+
color_formula: Optional[str] = Query(
|
|
139
|
+
None,
|
|
140
|
+
title="Color Formula",
|
|
141
|
+
description="rio-color formula (info: https://github.com/mapbox/rio-color)",
|
|
142
|
+
),
|
|
143
|
+
colormap=Depends(factory.colormap_dependency),
|
|
144
|
+
render_params=Depends(factory.render_dependency),
|
|
145
|
+
reader_params=Depends(factory.reader_dependency),
|
|
146
|
+
env=Depends(factory.environment_dependency),
|
|
147
|
+
):
|
|
148
|
+
with rasterio.Env(**env):
|
|
149
|
+
with factory.reader(src_path, **reader_params) as src:
|
|
150
|
+
image = src.preview(
|
|
151
|
+
max_size=self.max_size,
|
|
152
|
+
**layer_params,
|
|
153
|
+
**dataset_params,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if post_process:
|
|
157
|
+
image = post_process(image)
|
|
158
|
+
|
|
159
|
+
if rescale:
|
|
160
|
+
image.rescale(rescale)
|
|
161
|
+
|
|
162
|
+
if color_formula:
|
|
163
|
+
image.apply_color_formula(color_formula)
|
|
164
|
+
|
|
165
|
+
format = ImageType.jpeg if image.mask.all() else ImageType.png
|
|
166
|
+
|
|
167
|
+
content = image.render(
|
|
168
|
+
img_format=format.driver,
|
|
169
|
+
colormap=colormap,
|
|
170
|
+
**format.profile,
|
|
171
|
+
**render_params,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return Response(content, media_type=format.mediatype)
|
|
175
|
+
|
|
176
|
+
# Use it
|
|
177
|
+
app = FastAPI()
|
|
178
|
+
tiler = TilerFactory(
|
|
179
|
+
extensions=[
|
|
180
|
+
thumbnailExtension(max_size=64)
|
|
181
|
+
]
|
|
182
|
+
)
|
|
183
|
+
app.include_router(tiler.router)
|
|
184
|
+
```
|
|
185
|
+
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
## titiler.extensions
|
|
2
|
+
|
|
3
|
+
Extent TiTiler Tiler Factories
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
$ python -m pip install -U pip
|
|
9
|
+
|
|
10
|
+
# From Pypi
|
|
11
|
+
$ python -m pip install titiler.extensions
|
|
12
|
+
|
|
13
|
+
# Or from sources
|
|
14
|
+
$ git clone https://github.com/developmentseed/titiler.git
|
|
15
|
+
$ cd titiler && python -m pip install -e src/titiler/core -e src/titiler/extensions
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Available extensions
|
|
19
|
+
|
|
20
|
+
#### cogValidateExtension
|
|
21
|
+
|
|
22
|
+
- Goal: adds a `/validate` endpoint which return the content of rio-cogeo `info` method
|
|
23
|
+
- Additional requirements: `titiler.extensions["cogeo"]`
|
|
24
|
+
|
|
25
|
+
#### cogViewerExtension
|
|
26
|
+
|
|
27
|
+
- Goal: adds a `/viewer` endpoint which return an HTML viewer for simple COGs
|
|
28
|
+
|
|
29
|
+
#### stacViewerExtension
|
|
30
|
+
|
|
31
|
+
- Goal: adds a `/viewer` endpoint which return an HTML viewer for STAC item
|
|
32
|
+
|
|
33
|
+
#### wmsExtension
|
|
34
|
+
|
|
35
|
+
- Goal: adds a `/wms` endpoint to support OGC Web Map Service (`GetTile` and `GetCapabilities`) specification
|
|
36
|
+
|
|
37
|
+
## Use extensions
|
|
38
|
+
|
|
39
|
+
Extensions must be set at TilerFactory's creation using the `extensions=` options.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from fastapi import FastAPI
|
|
43
|
+
from titiler.core.factory import TilerFactory
|
|
44
|
+
from titiler.extensions import cogValidateExtension
|
|
45
|
+
|
|
46
|
+
# Create a FastAPI application
|
|
47
|
+
app = FastAPI(description="A lightweight Cloud Optimized GeoTIFF tile server")
|
|
48
|
+
|
|
49
|
+
# Create a set of endpoints using TiTiler TilerFactory
|
|
50
|
+
tiler = TilerFactory(
|
|
51
|
+
router_prefix="/cog",
|
|
52
|
+
extensions=[
|
|
53
|
+
cogValidateExtension() # the cogeoExtension will add a rio-cogeo /validate endpoint
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Register endpoints to the application
|
|
58
|
+
app.include_router(tiler.router, prefix="/cog")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
See [titiler.application](../application) for a full example.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## Create your own
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from dataclasses import dataclass, field
|
|
68
|
+
from typing import Tuple, List, Optional
|
|
69
|
+
|
|
70
|
+
from starlette.responses import Response
|
|
71
|
+
from fastapi import Depends, FastAPI, Query
|
|
72
|
+
from titiler.core.factory import BaseTilerFactory, FactoryExtension, TilerFactory
|
|
73
|
+
from titiler.core.dependencies import RescalingParams
|
|
74
|
+
from titiler.core.factory import TilerFactory
|
|
75
|
+
from titiler.core.resources.enums import ImageType
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class thumbnailExtension(FactoryExtension):
|
|
80
|
+
"""Add endpoint to a TilerFactory."""
|
|
81
|
+
|
|
82
|
+
# Set some options
|
|
83
|
+
max_size: int = field(default=128)
|
|
84
|
+
|
|
85
|
+
# Register method is mandatory and must take a BaseTilerFactory object as input
|
|
86
|
+
def register(self, factory: BaseTilerFactory):
|
|
87
|
+
"""Register endpoint to the tiler factory."""
|
|
88
|
+
|
|
89
|
+
# register an endpoint to the factory's router
|
|
90
|
+
@factory.router.get(
|
|
91
|
+
"/thumbnail",
|
|
92
|
+
responses={
|
|
93
|
+
200: {
|
|
94
|
+
"content": {
|
|
95
|
+
"image/png": {},
|
|
96
|
+
"image/jpeg": {},
|
|
97
|
+
},
|
|
98
|
+
"description": "Return an image.",
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
response_class=Response,
|
|
102
|
+
)
|
|
103
|
+
def thumbnail(
|
|
104
|
+
# we can reuse the factory dependency
|
|
105
|
+
src_path: str = Depends(factory.path_dependency),
|
|
106
|
+
layer_params=Depends(factory.layer_dependency),
|
|
107
|
+
dataset_params=Depends(factory.dataset_dependency),
|
|
108
|
+
post_process=Depends(factory.process_dependency),
|
|
109
|
+
rescale: Optional[List[Tuple[float, ...]]] = Depends(RescalingParams),
|
|
110
|
+
color_formula: Optional[str] = Query(
|
|
111
|
+
None,
|
|
112
|
+
title="Color Formula",
|
|
113
|
+
description="rio-color formula (info: https://github.com/mapbox/rio-color)",
|
|
114
|
+
),
|
|
115
|
+
colormap=Depends(factory.colormap_dependency),
|
|
116
|
+
render_params=Depends(factory.render_dependency),
|
|
117
|
+
reader_params=Depends(factory.reader_dependency),
|
|
118
|
+
env=Depends(factory.environment_dependency),
|
|
119
|
+
):
|
|
120
|
+
with rasterio.Env(**env):
|
|
121
|
+
with factory.reader(src_path, **reader_params) as src:
|
|
122
|
+
image = src.preview(
|
|
123
|
+
max_size=self.max_size,
|
|
124
|
+
**layer_params,
|
|
125
|
+
**dataset_params,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if post_process:
|
|
129
|
+
image = post_process(image)
|
|
130
|
+
|
|
131
|
+
if rescale:
|
|
132
|
+
image.rescale(rescale)
|
|
133
|
+
|
|
134
|
+
if color_formula:
|
|
135
|
+
image.apply_color_formula(color_formula)
|
|
136
|
+
|
|
137
|
+
format = ImageType.jpeg if image.mask.all() else ImageType.png
|
|
138
|
+
|
|
139
|
+
content = image.render(
|
|
140
|
+
img_format=format.driver,
|
|
141
|
+
colormap=colormap,
|
|
142
|
+
**format.profile,
|
|
143
|
+
**render_params,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
return Response(content, media_type=format.mediatype)
|
|
147
|
+
|
|
148
|
+
# Use it
|
|
149
|
+
app = FastAPI()
|
|
150
|
+
tiler = TilerFactory(
|
|
151
|
+
extensions=[
|
|
152
|
+
thumbnailExtension(max_size=64)
|
|
153
|
+
]
|
|
154
|
+
)
|
|
155
|
+
app.include_router(tiler.router)
|
|
156
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "titiler.extensions"
|
|
3
|
+
description = "Extensions for TiTiler Factories."
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
requires-python = ">=3.8"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Vincent Sarago", email = "vincent@developmentseed.com" },
|
|
8
|
+
]
|
|
9
|
+
keywords = [
|
|
10
|
+
"COG",
|
|
11
|
+
"STAC",
|
|
12
|
+
"MosaicJSON",
|
|
13
|
+
"Fastapi",
|
|
14
|
+
"Dynamic tile server",
|
|
15
|
+
"GDAL",
|
|
16
|
+
"Rasterio",
|
|
17
|
+
"OGC",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Intended Audience :: Information Technology",
|
|
21
|
+
"Intended Audience :: Science/Research",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.8",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
30
|
+
]
|
|
31
|
+
dynamic = []
|
|
32
|
+
dependencies = [
|
|
33
|
+
"titiler.core==0.17.3",
|
|
34
|
+
]
|
|
35
|
+
version = "0.17.3"
|
|
36
|
+
|
|
37
|
+
[project.license]
|
|
38
|
+
text = "MIT"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
test = [
|
|
42
|
+
"pytest",
|
|
43
|
+
"pytest-cov",
|
|
44
|
+
"pytest-asyncio",
|
|
45
|
+
"httpx",
|
|
46
|
+
"pystac[validation]>=1.0.0,<2.0.0",
|
|
47
|
+
]
|
|
48
|
+
cogeo = [
|
|
49
|
+
"rio-cogeo>=5.0,<6.0",
|
|
50
|
+
]
|
|
51
|
+
stac = [
|
|
52
|
+
"rio-stac>=0.8,<0.9",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[project.urls]
|
|
56
|
+
Homepage = "https://developmentseed.org/titiler/"
|
|
57
|
+
Documentation = "https://developmentseed.org/titiler/"
|
|
58
|
+
Issues = "https://github.com/developmentseed/titiler/issues"
|
|
59
|
+
Source = "https://github.com/developmentseed/titiler"
|
|
60
|
+
Changelog = "https://developmentseed.org/titiler/release-notes/"
|
|
61
|
+
|
|
62
|
+
[build-system]
|
|
63
|
+
requires = [
|
|
64
|
+
"pdm-pep517",
|
|
65
|
+
]
|
|
66
|
+
build-backend = "pdm.pep517.api"
|
|
67
|
+
|
|
68
|
+
[tool.pdm.version]
|
|
69
|
+
source = "file"
|
|
70
|
+
path = "titiler/extensions/__init__.py"
|
|
71
|
+
|
|
72
|
+
[tool.pdm.build]
|
|
73
|
+
includes = [
|
|
74
|
+
"titiler/extensions",
|
|
75
|
+
]
|
|
76
|
+
excludes = [
|
|
77
|
+
"tests/",
|
|
78
|
+
"**/.mypy_cache",
|
|
79
|
+
"**/.DS_Store",
|
|
80
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""rio-cogeo Extension."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends, Query
|
|
6
|
+
from typing_extensions import Annotated
|
|
7
|
+
|
|
8
|
+
from titiler.core.factory import BaseTilerFactory, FactoryExtension
|
|
9
|
+
from titiler.core.resources.responses import JSONResponse
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from rio_cogeo.cogeo import cog_info
|
|
13
|
+
from rio_cogeo.models import Info
|
|
14
|
+
except ImportError: # pragma: nocover
|
|
15
|
+
cog_info = None # type: ignore
|
|
16
|
+
Info = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class cogValidateExtension(FactoryExtension):
|
|
21
|
+
"""Add /validate endpoint to a COG TilerFactory."""
|
|
22
|
+
|
|
23
|
+
def register(self, factory: BaseTilerFactory):
|
|
24
|
+
"""Register endpoint to the tiler factory."""
|
|
25
|
+
|
|
26
|
+
assert (
|
|
27
|
+
cog_info is not None
|
|
28
|
+
), "'rio-cogeo' must be installed to use CogValidateExtension"
|
|
29
|
+
|
|
30
|
+
@factory.router.get(
|
|
31
|
+
"/validate",
|
|
32
|
+
response_model=Info,
|
|
33
|
+
response_class=JSONResponse,
|
|
34
|
+
)
|
|
35
|
+
def validate(
|
|
36
|
+
src_path=Depends(factory.path_dependency),
|
|
37
|
+
strict: Annotated[
|
|
38
|
+
bool,
|
|
39
|
+
Query(description="Treat warnings as errors"),
|
|
40
|
+
] = False,
|
|
41
|
+
):
|
|
42
|
+
"""Validate a COG"""
|
|
43
|
+
return cog_info(src_path, strict=strict)
|
|
File without changes
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""rio-stac Extension."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
5
|
+
|
|
6
|
+
from fastapi import Depends, Query
|
|
7
|
+
from typing_extensions import Annotated, TypedDict
|
|
8
|
+
|
|
9
|
+
from titiler.core.factory import BaseTilerFactory, FactoryExtension
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import pystac
|
|
13
|
+
from pystac.utils import datetime_to_str, str_to_datetime
|
|
14
|
+
from rio_stac.stac import create_stac_item
|
|
15
|
+
except ImportError: # pragma: nocover
|
|
16
|
+
create_stac_item = None # type: ignore
|
|
17
|
+
pystac = None # type: ignore
|
|
18
|
+
str_to_datetime = datetime_to_str = None # type: ignore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Item(TypedDict, total=False):
|
|
22
|
+
"""STAC Item."""
|
|
23
|
+
|
|
24
|
+
type: str
|
|
25
|
+
stac_version: str
|
|
26
|
+
stac_extensions: Optional[List[str]]
|
|
27
|
+
id: str
|
|
28
|
+
geometry: Dict[str, Any]
|
|
29
|
+
bbox: List[float]
|
|
30
|
+
properties: Dict[str, Any]
|
|
31
|
+
links: List[Dict[str, Any]]
|
|
32
|
+
assets: Dict[str, Any]
|
|
33
|
+
collection: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class stacExtension(FactoryExtension):
|
|
38
|
+
"""Add /stac endpoint to a COG TilerFactory."""
|
|
39
|
+
|
|
40
|
+
def register(self, factory: BaseTilerFactory):
|
|
41
|
+
"""Register endpoint to the tiler factory."""
|
|
42
|
+
|
|
43
|
+
assert (
|
|
44
|
+
create_stac_item is not None
|
|
45
|
+
), "'rio-stac' must be installed to use stacExtension"
|
|
46
|
+
assert pystac is not None, "'pystac' must be installed to use stacExtension"
|
|
47
|
+
|
|
48
|
+
media = [m.value for m in pystac.MediaType] + ["auto"]
|
|
49
|
+
|
|
50
|
+
@factory.router.get("/stac", response_model=Item, name="Create STAC Item")
|
|
51
|
+
def create_stac(
|
|
52
|
+
src_path=Depends(factory.path_dependency),
|
|
53
|
+
datetime: Annotated[
|
|
54
|
+
Optional[str],
|
|
55
|
+
Query(
|
|
56
|
+
description="The date and time of the assets, in UTC (e.g 2020-01-01, 2020-01-01T01:01:01).",
|
|
57
|
+
),
|
|
58
|
+
] = None,
|
|
59
|
+
extensions: Annotated[
|
|
60
|
+
Optional[List[str]],
|
|
61
|
+
Query(description="STAC extension URL the Item implements."),
|
|
62
|
+
] = None,
|
|
63
|
+
collection: Annotated[
|
|
64
|
+
Optional[str],
|
|
65
|
+
Query(description="The Collection ID that this item belongs to."),
|
|
66
|
+
] = None,
|
|
67
|
+
collection_url: Annotated[
|
|
68
|
+
Optional[str],
|
|
69
|
+
Query(description="Link to the STAC Collection."),
|
|
70
|
+
] = None,
|
|
71
|
+
# properties: Optional[Dict] = Query(None, description="Additional properties to add in the item."),
|
|
72
|
+
id: Annotated[
|
|
73
|
+
Optional[str],
|
|
74
|
+
Query(
|
|
75
|
+
description="Id to assign to the item (default to the source basename)."
|
|
76
|
+
),
|
|
77
|
+
] = None,
|
|
78
|
+
asset_name: Annotated[
|
|
79
|
+
Optional[str],
|
|
80
|
+
Query(description="asset name for the source (default to 'data')."),
|
|
81
|
+
] = "data",
|
|
82
|
+
asset_roles: Annotated[
|
|
83
|
+
Optional[List[str]],
|
|
84
|
+
Query(description="list of asset's roles."),
|
|
85
|
+
] = None,
|
|
86
|
+
asset_media_type: Annotated[ # type: ignore
|
|
87
|
+
Optional[Literal[tuple(media)]],
|
|
88
|
+
Query(description="Asset's media type"),
|
|
89
|
+
] = "auto",
|
|
90
|
+
asset_href: Annotated[
|
|
91
|
+
Optional[str],
|
|
92
|
+
Query(description="Asset's URI (default to source's path)"),
|
|
93
|
+
] = None,
|
|
94
|
+
with_proj: Annotated[
|
|
95
|
+
Optional[bool],
|
|
96
|
+
Query(description="Add the `projection` extension and properties."),
|
|
97
|
+
] = True,
|
|
98
|
+
with_raster: Annotated[
|
|
99
|
+
Optional[bool],
|
|
100
|
+
Query(description="Add the `raster` extension and properties."),
|
|
101
|
+
] = True,
|
|
102
|
+
with_eo: Annotated[
|
|
103
|
+
Optional[bool],
|
|
104
|
+
Query(description="Add the `eo` extension and properties."),
|
|
105
|
+
] = True,
|
|
106
|
+
max_size: Annotated[
|
|
107
|
+
Optional[int],
|
|
108
|
+
Query(
|
|
109
|
+
gt=0,
|
|
110
|
+
description="Limit array size from which to get the raster statistics.",
|
|
111
|
+
),
|
|
112
|
+
] = 1024,
|
|
113
|
+
geom_densify_pts: Annotated[
|
|
114
|
+
Optional[int],
|
|
115
|
+
Query(
|
|
116
|
+
alias="geometry_densify",
|
|
117
|
+
ge=0,
|
|
118
|
+
description="Number of points to add to each edge to account for nonlinear edges transformation.",
|
|
119
|
+
),
|
|
120
|
+
] = 0,
|
|
121
|
+
geom_precision: Annotated[
|
|
122
|
+
Optional[int],
|
|
123
|
+
Query(
|
|
124
|
+
alias="geometry_precision",
|
|
125
|
+
ge=-1,
|
|
126
|
+
description="Round geometry coordinates to this number of decimal.",
|
|
127
|
+
),
|
|
128
|
+
] = -1,
|
|
129
|
+
):
|
|
130
|
+
"""Create STAC item."""
|
|
131
|
+
properties = (
|
|
132
|
+
{}
|
|
133
|
+
) # or properties = properties or {} if we add properties in Query
|
|
134
|
+
|
|
135
|
+
dt = None
|
|
136
|
+
if datetime:
|
|
137
|
+
if "/" in datetime:
|
|
138
|
+
start_datetime, end_datetime = datetime.split("/")
|
|
139
|
+
properties["start_datetime"] = datetime_to_str(
|
|
140
|
+
str_to_datetime(start_datetime)
|
|
141
|
+
)
|
|
142
|
+
properties["end_datetime"] = datetime_to_str(
|
|
143
|
+
str_to_datetime(end_datetime)
|
|
144
|
+
)
|
|
145
|
+
else:
|
|
146
|
+
dt = str_to_datetime(datetime)
|
|
147
|
+
|
|
148
|
+
return create_stac_item(
|
|
149
|
+
src_path,
|
|
150
|
+
input_datetime=dt,
|
|
151
|
+
extensions=extensions,
|
|
152
|
+
collection=collection,
|
|
153
|
+
collection_url=collection_url,
|
|
154
|
+
properties=properties,
|
|
155
|
+
id=id,
|
|
156
|
+
asset_name=asset_name,
|
|
157
|
+
asset_roles=asset_roles,
|
|
158
|
+
asset_media_type=asset_media_type,
|
|
159
|
+
asset_href=asset_href or src_path,
|
|
160
|
+
with_proj=with_proj,
|
|
161
|
+
with_raster=with_raster,
|
|
162
|
+
with_eo=with_eo,
|
|
163
|
+
raster_max_size=max_size,
|
|
164
|
+
geom_densify_pts=geom_densify_pts,
|
|
165
|
+
geom_precision=geom_precision,
|
|
166
|
+
).to_dict()
|