Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: add core.utils.bounds_to_geometry to reuse in factories #1047

Merged
merged 6 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

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

## 0.19.2 (2024-11-28)

Expand Down
33 changes: 4 additions & 29 deletions src/titiler/core/titiler/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from fastapi.dependencies.utils import get_parameterless_sub_dependant
from fastapi.params import Depends as DependsFunc
from geojson_pydantic.features import Feature, FeatureCollection
from geojson_pydantic.geometries import MultiPolygon, Polygon
from morecantile import TileMatrixSet
from morecantile import tms as morecantile_tms
from morecantile.defaults import TileMatrixSets
Expand Down Expand Up @@ -85,7 +84,7 @@
from titiler.core.resources.enums import ImageType
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse, XMLResponse
from titiler.core.routing import EndpointScope
from titiler.core.utils import render_image
from titiler.core.utils import bounds_to_geometry, render_image

jinja2_env = jinja2.Environment(
loader=jinja2.ChoiceLoader([jinja2.PackageLoader(__package__, "templates")])
Expand Down Expand Up @@ -398,15 +397,7 @@ def info_geojson(
with rasterio.Env(**env):
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
if bounds[0] > bounds[2]:
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
geometry = MultiPolygon(
type="MultiPolygon",
coordinates=[pl.coordinates, pr.coordinates],
)
else:
geometry = Polygon.from_bounds(*bounds)
geometry = bounds_to_geometry(bounds)

return Feature(
type="Feature",
Expand Down Expand Up @@ -1446,15 +1437,7 @@ def info_geojson(
with rasterio.Env(**env):
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
if bounds[0] > bounds[2]:
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
geometry = MultiPolygon(
type="MultiPolygon",
coordinates=[pl.coordinates, pr.coordinates],
)
else:
geometry = Polygon.from_bounds(*bounds)
geometry = bounds_to_geometry(bounds)

return Feature(
type="Feature",
Expand Down Expand Up @@ -1699,15 +1682,7 @@ def info_geojson(
with rasterio.Env(**env):
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
if bounds[0] > bounds[2]:
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
geometry = MultiPolygon(
type="MultiPolygon",
coordinates=[pl.coordinates, pr.coordinates],
)
else:
geometry = Polygon.from_bounds(*bounds)
geometry = bounds_to_geometry(bounds)

return Feature(
type="Feature",
Expand Down
19 changes: 18 additions & 1 deletion src/titiler/core/titiler/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
from typing import Any, Optional, Sequence, Tuple, Union

import numpy
from geojson_pydantic.geometries import MultiPolygon, Polygon
from rasterio.dtypes import dtype_ranges
from rio_tiler.colormap import apply_cmap
from rio_tiler.errors import InvalidDatatypeWarning
from rio_tiler.models import ImageData
from rio_tiler.types import ColorMapType, IntervalTuple
from rio_tiler.types import BBox, ColorMapType, IntervalTuple
from rio_tiler.utils import linear_rescale, render

from titiler.core.resources.enums import ImageType
Expand Down Expand Up @@ -119,3 +120,19 @@ def render_image(
),
output_format.mediatype,
)


def bounds_to_geometry(bounds: BBox) -> Union[Polygon, MultiPolygon]:
"""Convert bounds to geometry.

Note: if bounds are crossing the dateline separation line, a MultiPolygon geometry will be returned.

"""
if bounds[0] > bounds[2]:
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
return MultiPolygon(
type="MultiPolygon",
coordinates=[pl.coordinates, pr.coordinates],
)
return Polygon.from_bounds(*bounds)
14 changes: 3 additions & 11 deletions src/titiler/mosaic/titiler/mosaic/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from cogeo_mosaic.mosaic import MosaicJSON
from fastapi import Depends, HTTPException, Path, Query
from geojson_pydantic.features import Feature
from geojson_pydantic.geometries import MultiPolygon, Polygon
from geojson_pydantic.geometries import Polygon
from morecantile import tms as morecantile_tms
from morecantile.defaults import TileMatrixSets
from pydantic import Field
Expand Down Expand Up @@ -48,7 +48,7 @@
from titiler.core.models.OGC import TileSet, TileSetList
from titiler.core.resources.enums import ImageType, OptionalHeader
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse, XMLResponse
from titiler.core.utils import render_image
from titiler.core.utils import bounds_to_geometry, render_image
from titiler.mosaic.models.responses import Point

MOSAIC_THREADS = int(os.getenv("MOSAIC_CONCURRENCY", MAX_THREADS))
Expand Down Expand Up @@ -257,15 +257,7 @@ def info_geojson(
**backend_params.as_dict(),
) as src_dst:
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
if bounds[0] > bounds[2]:
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
geometry = MultiPolygon(
type="MultiPolygon",
coordinates=[pl.coordinates, pr.coordinates],
)
else:
geometry = Polygon.from_bounds(*bounds)
geometry = bounds_to_geometry(bounds)

return Feature(
type="Feature",
Expand Down
13 changes: 2 additions & 11 deletions src/titiler/xarray/titiler/xarray/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from attrs import define, field
from fastapi import Body, Depends, Query
from geojson_pydantic.features import Feature, FeatureCollection
from geojson_pydantic.geometries import MultiPolygon, Polygon
from rio_tiler.constants import WGS84_CRS
from rio_tiler.models import Info
from typing_extensions import Annotated
Expand All @@ -23,6 +22,7 @@
from titiler.core.factory import TilerFactory as BaseTilerFactory
from titiler.core.models.responses import InfoGeoJSON, StatisticsGeoJSON
from titiler.core.resources.responses import GeoJSONResponse, JSONResponse
from titiler.core.utils import bounds_to_geometry
from titiler.xarray.dependencies import DatasetParams, PartFeatureParams, XarrayParams
from titiler.xarray.io import Reader

Expand Down Expand Up @@ -116,16 +116,7 @@ def info_geojson(
with rasterio.Env(**env):
with self.reader(src_path, **reader_params.as_dict()) as src_dst:
bounds = src_dst.get_geographic_bounds(crs or WGS84_CRS)
if bounds[0] > bounds[2]:
pl = Polygon.from_bounds(-180, bounds[1], bounds[2], bounds[3])
pr = Polygon.from_bounds(bounds[0], bounds[1], 180, bounds[3])
geometry = MultiPolygon(
type="MultiPolygon",
coordinates=[pl.coordinates, pr.coordinates],
)
else:
geometry = Polygon.from_bounds(*bounds)

geometry = bounds_to_geometry(bounds)
info = src_dst.info().model_dump()
if show_times and "time" in src_dst.input.dims:
times = [str(x.data) for x in src_dst.input.time]
Expand Down
Loading