Skip to content

Commit f978c3e

Browse files
refactor: add core.utils.bounds_to_geometry to reuse in factories (#1047)
* refactor: add core.utils.bounds_to_geometry to reuse in factories * Update src/titiler/core/titiler/core/utils.py * update changelog * lint --------- Co-authored-by: vincentsarago <[email protected]>
1 parent defc468 commit f978c3e

File tree

5 files changed

+28
-52
lines changed

5 files changed

+28
-52
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
* update `/healthz` endpoint to return dependencies versions (titiler, rasterio, gdal, ...) (author @scottyhq, https://github.com/developmentseed/titiler/pull/1056)
1313
* migrate `templates/index.html` to bootstrap5, remove unused css, reuse bs classes (author @PratapVardhan, https://github.com/developmentseed/titiler/pull/1048)
14+
* add `titiler.core.utils.bounds_to_geometry` and reduce code duplication in factories (author @PratapVardhan, https://github.com/developmentseed/titiler/pull/1047)
1415

1516
## 0.19.2 (2024-11-28)
1617

src/titiler/core/titiler/core/factory.py

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from fastapi.dependencies.utils import get_parameterless_sub_dependant
2424
from fastapi.params import Depends as DependsFunc
2525
from geojson_pydantic.features import Feature, FeatureCollection
26-
from geojson_pydantic.geometries import MultiPolygon, Polygon
2726
from morecantile import TileMatrixSet
2827
from morecantile import tms as morecantile_tms
2928
from morecantile.defaults import TileMatrixSets
@@ -85,7 +84,7 @@
8584
from titiler.core.resources.enums import ImageType
8685
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse, XMLResponse
8786
from titiler.core.routing import EndpointScope
88-
from titiler.core.utils import render_image
87+
from titiler.core.utils import bounds_to_geometry, render_image
8988

9089
jinja2_env = jinja2.Environment(
9190
loader=jinja2.ChoiceLoader([jinja2.PackageLoader(__package__, "templates")])
@@ -398,15 +397,7 @@ def info_geojson(
398397
with rasterio.Env(**env):
399398
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
400399
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
401-
if bounds[0] > bounds[2]:
402-
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
403-
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
404-
geometry = MultiPolygon(
405-
type="MultiPolygon",
406-
coordinates=[pl.coordinates, pr.coordinates],
407-
)
408-
else:
409-
geometry = Polygon.from_bounds(*bounds)
400+
geometry = bounds_to_geometry(bounds)
410401

411402
return Feature(
412403
type="Feature",
@@ -1446,15 +1437,7 @@ def info_geojson(
14461437
with rasterio.Env(**env):
14471438
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
14481439
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
1449-
if bounds[0] > bounds[2]:
1450-
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
1451-
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
1452-
geometry = MultiPolygon(
1453-
type="MultiPolygon",
1454-
coordinates=[pl.coordinates, pr.coordinates],
1455-
)
1456-
else:
1457-
geometry = Polygon.from_bounds(*bounds)
1440+
geometry = bounds_to_geometry(bounds)
14581441

14591442
return Feature(
14601443
type="Feature",
@@ -1699,15 +1682,7 @@ def info_geojson(
16991682
with rasterio.Env(**env):
17001683
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
17011684
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
1702-
if bounds[0] > bounds[2]:
1703-
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
1704-
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
1705-
geometry = MultiPolygon(
1706-
type="MultiPolygon",
1707-
coordinates=[pl.coordinates, pr.coordinates],
1708-
)
1709-
else:
1710-
geometry = Polygon.from_bounds(*bounds)
1685+
geometry = bounds_to_geometry(bounds)
17111686

17121687
return Feature(
17131688
type="Feature",

src/titiler/core/titiler/core/utils.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
from typing import Any, Optional, Sequence, Tuple, Union
55

66
import numpy
7+
from geojson_pydantic.geometries import MultiPolygon, Polygon
78
from rasterio.dtypes import dtype_ranges
89
from rio_tiler.colormap import apply_cmap
910
from rio_tiler.errors import InvalidDatatypeWarning
1011
from rio_tiler.models import ImageData
11-
from rio_tiler.types import ColorMapType, IntervalTuple
12+
from rio_tiler.types import BBox, ColorMapType, IntervalTuple
1213
from rio_tiler.utils import linear_rescale, render
1314

1415
from titiler.core.resources.enums import ImageType
@@ -119,3 +120,19 @@ def render_image(
119120
),
120121
output_format.mediatype,
121122
)
123+
124+
125+
def bounds_to_geometry(bounds: BBox) -> Union[Polygon, MultiPolygon]:
126+
"""Convert bounds to geometry.
127+
128+
Note: if bounds are crossing the dateline separation line, a MultiPolygon geometry will be returned.
129+
130+
"""
131+
if bounds[0] > bounds[2]:
132+
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
133+
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
134+
return MultiPolygon(
135+
type="MultiPolygon",
136+
coordinates=[pl.coordinates, pr.coordinates],
137+
)
138+
return Polygon.from_bounds(*bounds)

src/titiler/mosaic/titiler/mosaic/factory.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from cogeo_mosaic.mosaic import MosaicJSON
1212
from fastapi import Depends, HTTPException, Path, Query
1313
from geojson_pydantic.features import Feature
14-
from geojson_pydantic.geometries import MultiPolygon, Polygon
14+
from geojson_pydantic.geometries import Polygon
1515
from morecantile import tms as morecantile_tms
1616
from morecantile.defaults import TileMatrixSets
1717
from pydantic import Field
@@ -48,7 +48,7 @@
4848
from titiler.core.models.OGC import TileSet, TileSetList
4949
from titiler.core.resources.enums import ImageType, OptionalHeader
5050
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse, XMLResponse
51-
from titiler.core.utils import render_image
51+
from titiler.core.utils import bounds_to_geometry, render_image
5252
from titiler.mosaic.models.responses import Point
5353

5454
MOSAIC_THREADS = int(os.getenv("MOSAIC_CONCURRENCY", MAX_THREADS))
@@ -257,15 +257,7 @@ def info_geojson(
257257
**backend_params.as_dict(),
258258
) as src_dst:
259259
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
260-
if bounds[0] > bounds[2]:
261-
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
262-
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
263-
geometry = MultiPolygon(
264-
type="MultiPolygon",
265-
coordinates=[pl.coordinates, pr.coordinates],
266-
)
267-
else:
268-
geometry = Polygon.from_bounds(*bounds)
260+
geometry = bounds_to_geometry(bounds)
269261

270262
return Feature(
271263
type="Feature",

src/titiler/xarray/titiler/xarray/factory.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from attrs import define, field
77
from fastapi import Body, Depends, Query
88
from geojson_pydantic.features import Feature, FeatureCollection
9-
from geojson_pydantic.geometries import MultiPolygon, Polygon
109
from rio_tiler.constants import WGS84_CRS
1110
from rio_tiler.models import Info
1211
from typing_extensions import Annotated
@@ -23,6 +22,7 @@
2322
from titiler.core.factory import TilerFactory as BaseTilerFactory
2423
from titiler.core.models.responses import InfoGeoJSON, StatisticsGeoJSON
2524
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse
25+
from titiler.core.utils import bounds_to_geometry
2626
from titiler.xarray.dependencies import DatasetParams, PartFeatureParams, XarrayParams
2727
from titiler.xarray.io import Reader
2828

@@ -116,16 +116,7 @@ def info_geojson(
116116
with rasterio.Env(**env):
117117
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
118118
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
119-
if bounds[0] > bounds[2]:
120-
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
121-
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
122-
geometry = MultiPolygon(
123-
type="MultiPolygon",
124-
coordinates=[pl.coordinates, pr.coordinates],
125-
)
126-
else:
127-
geometry = Polygon.from_bounds(*bounds)
128-
119+
geometry = bounds_to_geometry(bounds)
129120
info = src_dst.info().model_dump()
130121
if show_times and "time" in src_dst.input.dims:
131122
times = [str(x.data) for x in src_dst.input.time]

0 commit comments

Comments
 (0)