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.
@@ -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,8 @@
1
+ """titiler.extensions"""
2
+
3
+ __version__ = "0.17.3"
4
+
5
+ from .cogeo import cogValidateExtension # noqa
6
+ from .stac import stacExtension # noqa
7
+ from .viewer import cogViewerExtension, stacViewerExtension # noqa
8
+ from .wms import wmsExtension # noqa
@@ -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()