Skip to content

Commit 11ec621

Browse files
add coord-crs options (#632)
* add coord-crs options * fix test * avoid test to fail for encoding reason
1 parent 3fc1fd7 commit 11ec621

File tree

9 files changed

+61
-8
lines changed

9 files changed

+61
-8
lines changed

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Release Notes
22

3-
## Next
3+
## 0.11.6 (2023-04-14)
44

55
* Allow a default `rescale` parameter to be set via a dependency (author @samn, https://github.com/developmentseed/titiler/pull/619)
6+
* add `coord-crs` parameter for `/point`, `/part` and `/feature` endpoints
67

78
## 0.11.5 (2023-03-22)
89

docs/src/endpoints/cog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ Example:
113113
- **url** (str): Cloud Optimized GeoTIFF URL. **Required**
114114
- **bidx** (array[int]): Dataset band indexes (e.g `bidx=1`, `bidx=1&bidx=2&bidx=3`).
115115
- **expression** (str): rio-tiler's band math expression (e.g B1/B2).
116+
- **coord-crs** (str): Coordinate Reference System of the input coordinates. Default to `epsg:4326`.
116117
- **max_size** (int): Max image size, default is 1024.
117118
- **nodata** (str, int, float): Overwrite internal Nodata value.
118119
- **unscale** (bool): Apply dataset internal Scale/Offset.
@@ -148,6 +149,7 @@ Example:
148149
- **url** (str): Cloud Optimized GeoTIFF URL. **Required**
149150
- **bidx** (array[int]): Dataset band indexes (e.g `bidx=1`, `bidx=1&bidx=2&bidx=3`).
150151
- **expression** (str): rio-tiler's band math expression (e.g B1/B2).
152+
- **coord-crs** (str): Coordinate Reference System of the input geometry coordinates. Default to `epsg:4326`.
151153
- **max_size** (int): Max image size, default is 1024.
152154
- **nodata** (str, int, float): Overwrite internal Nodata value.
153155
- **unscale** (bool): Apply dataset internal Scale/Offset.
@@ -182,6 +184,7 @@ Note: if `height` and `width` are provided `max_size` will be ignored.
182184
- **url** (str): Cloud Optimized GeoTIFF URL. **Required**
183185
- **bidx** (array[int]): Dataset band indexes (e.g `bidx=1`, `bidx=1&bidx=2&bidx=3`).
184186
- **expression** (str): rio-tiler's band math expression (e.g B1/B2).
187+
- **coord-crs** (str): Coordinate Reference System of the input coordinates. Default to `epsg:4326`.
185188
- **nodata** (str, int, float): Overwrite internal Nodata value.
186189
- **unscale** (bool): Apply dataset internal Scale/Offset.
187190
- **resampling** (str): rasterio resampling method. Default is `nearest`.
@@ -320,6 +323,7 @@ Example:
320323
- **url** (str): Cloud Optimized GeoTIFF URL. **Required**
321324
- **bidx** (array[int]): Dataset band indexes (e.g `bidx=1`, `bidx=1&bidx=2&bidx=3`).
322325
- **expression** (str): rio-tiler's band math expression (e.g B1/B2).
326+
- **coord-crs** (str): Coordinate Reference System of the input geometry coordinates. Default to `epsg:4326`.
323327
- **max_size** (int): Max image size from which to calculate statistics, default is 1024.
324328
- **height** (int): Force image height from which to calculate statistics.
325329
- **width** (int): Force image width from which to calculate statistics.

docs/src/endpoints/stac.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ Example:
124124
- **expression** (str): rio-tiler's math expression with asset names (e.g `Asset1_b1/Asset2_b1`).
125125
- **asset_as_band** (bool): tell rio-tiler that each asset is a 1 band dataset, so expression `Asset1/Asset2` can be passed.
126126
- **asset_bidx** (array[str]): Per asset band math expression (e.g `Asset1|1,2,3`).
127+
- **coord-crs** (str): Coordinate Reference System of the input coordinates. Default to `epsg:4326`.
127128
- **max_size** (int): Max image size, default is 1024.
128129
- **nodata** (str, int, float): Overwrite internal Nodata value.
129130
- **unscale** (bool): Apply dataset internal Scale/Offset.
@@ -162,6 +163,7 @@ Example:
162163
- **expression** (str): rio-tiler's math expression with asset names (e.g `Asset1_b1/Asset2_b1`).
163164
- **asset_as_band** (bool): tell rio-tiler that each asset is a 1 band dataset, so expression `Asset1/Asset2` can be passed.
164165
- **asset_bidx** (array[str]): Per asset band math expression (e.g `Asset1|1,2,3`).
166+
- **coord-crs** (str): Coordinate Reference System of the input geometry coordinates. Default to `epsg:4326`.
165167
- **max_size** (int): Max image size, default is 1024.
166168
- **nodata** (str, int, float): Overwrite internal Nodata value.
167169
- **unscale** (bool): Apply dataset internal Scale/Offset.
@@ -198,6 +200,7 @@ Example:
198200
- **expression** (str): rio-tiler's math expression with asset names (e.g `Asset1_b1/Asset2_b1`).
199201
- **asset_as_band** (bool): tell rio-tiler that each asset is a 1 band dataset, so expression `Asset1/Asset2` can be passed.
200202
- **asset_bidx** (array[str]): Per asset band math expression (e.g `Asset1|1,2,3`).
203+
- **coord-crs** (str): Coordinate Reference System of the input coordinates. Default to `epsg:4326`.
201204
- **nodata** (str, int, float): Overwrite internal Nodata value.
202205
- **unscale** (bool): Apply dataset internal Scale/Offset.
203206
- **resampling** (str): rasterio resampling method. Default is `nearest`.
@@ -389,6 +392,7 @@ Example:
389392
- **expression** (str): rio-tiler's math expression with asset names (e.g `Asset1_b1/Asset2_b1`).
390393
- **asset_as_band** (bool): tell rio-tiler that each asset is a 1 band dataset, so expression `Asset1/Asset2` can be passed.
391394
- **asset_bidx** (array[str]): Per asset band math expression (e.g `Asset1|1,2,3`).
395+
- **coord-crs** (str): Coordinate Reference System of the input geometry coordinates. Default to `epsg:4326`.
392396
- **max_size** (int): Max image size from which to calculate statistics, default is 1024.
393397
- **height** (int): Force image height from which to calculate statistics.
394398
- **width** (int): Force image width from which to calculate statistics.

src/titiler/application/tests/routes/test_cog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def test_wmts(rio, app):
5959
assert response.headers["content-type"] == "application/xml"
6060
assert response.headers["Cache-Control"] == "private, max-age=3600"
6161
assert (
62-
"http://testserver/cog/WMTSCapabilities.xml?url=https://myurl.com/cog.tif"
62+
"http://testserver/cog/WMTSCapabilities.xml?url=https"
6363
in response.content.decode()
6464
)
6565
assert "<ows:Identifier>cogeo</ows:Identifier>" in response.content.decode()

src/titiler/core/tests/test_dependencies.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,10 @@ def _assets_bidx(params=Depends(dependencies.AssetsBidxParams)):
250250
assert response.json()["asset_indexes"] == {"data": [1, 2, 3], "image": [1]}
251251

252252
response = client.get(
253-
"/third?assets=data&assets=image&asset_expression=data|b1\b2&asset_expression=image|b1*b2"
253+
"/third?assets=data&assets=image&asset_expression=data|b1/b2&asset_expression=image|b1*b2"
254254
)
255255
assert response.json()["assets"] == ["data", "image"]
256-
assert response.json()["asset_expression"] == {"data": "b1\b2", "image": "b1*b2"}
256+
assert response.json()["asset_expression"] == {"data": "b1/b2", "image": "b1*b2"}
257257

258258

259259
def test_bands():

src/titiler/core/tests/test_factories.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ def test_TilerFactory():
189189
assert len(response.json()["values"]) == 1
190190
assert response.json()["band_names"] == ["b1"]
191191

192+
response = client.get(
193+
f"/point/-6259272.328324187,12015838.020930404?url={DATA_DIR}/cog.tif&coord-crs=EPSG:3857"
194+
)
195+
assert response.status_code == 200
196+
assert response.headers["content-type"] == "application/json"
197+
assert len(response.json()["values"]) == 1
198+
assert response.json()["band_names"] == ["b1"]
199+
192200
response = client.get(f"/point/-56.228,72.715?url={DATA_DIR}/cog.tif&bidx=1&bidx=1")
193201
assert len(response.json()["values"]) == 2
194202
assert response.json()["band_names"] == ["b1", "b1"]

src/titiler/core/titiler/core/dependencies.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import numpy
99
from fastapi import HTTPException, Query
10+
from rasterio.crs import CRS
1011
from rasterio.enums import Resampling
1112
from rio_tiler.colormap import cmap, parse_color
1213
from rio_tiler.errors import MissingAssets, MissingBands
@@ -448,3 +449,17 @@ def __post_init__(self):
448449

449450
if self.range:
450451
self.range = list(map(float, self.range.split(","))) # type: ignore
452+
453+
454+
def CRSParams(
455+
crs: str = Query(
456+
None,
457+
alias="coord-crs",
458+
description="Coordinate Reference System of the input coords. Default to `epsg:4326`.",
459+
)
460+
) -> Optional[CRS]:
461+
"""Coordinate Reference System Coordinates Param."""
462+
if crs:
463+
return CRS.from_user_input(crs)
464+
465+
return None

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from morecantile import TileMatrixSet
1515
from morecantile import tms as morecantile_tms
1616
from morecantile.defaults import TileMatrixSets
17+
from rasterio.crs import CRS
18+
from rio_tiler.constants import WGS84_CRS
1719
from rio_tiler.io import BaseReader, MultiBandReader, MultiBaseReader, Reader
1820
from rio_tiler.models import BandStatistics, Bounds, Info
1921
from rio_tiler.types import ColorMapType
@@ -35,6 +37,7 @@
3537
BandsParams,
3638
BidxExprParams,
3739
ColorMapParams,
40+
CRSParams,
3841
DatasetParams,
3942
DatasetPathParams,
4043
DefaultDependency,
@@ -367,6 +370,7 @@ def info_geojson(
367370
with rasterio.Env(**env):
368371
with self.reader(src_path, **reader_params) as src_dst:
369372
return Feature(
373+
type="Feature",
370374
geometry=Polygon.from_bounds(*src_dst.geographic_bounds),
371375
properties=src_dst.info(),
372376
)
@@ -428,6 +432,7 @@ def geojson_statistics(
428432
..., description="GeoJSON Feature or FeatureCollection."
429433
),
430434
src_path=Depends(self.path_dependency),
435+
coord_crs: Optional[CRS] = Depends(CRSParams),
431436
layer_params=Depends(self.layer_dependency),
432437
dataset_params=Depends(self.dataset_dependency),
433438
image_params=Depends(self.img_dependency),
@@ -439,13 +444,14 @@ def geojson_statistics(
439444
"""Get Statistics from a geojson feature or featureCollection."""
440445
fc = geojson
441446
if isinstance(fc, Feature):
442-
fc = FeatureCollection(features=[geojson])
447+
fc = FeatureCollection(type="FeatureCollection", features=[geojson])
443448

444449
with rasterio.Env(**env):
445450
with self.reader(src_path, **reader_params) as src_dst:
446451
for feature in fc:
447452
data = src_dst.feature(
448453
feature.dict(exclude_none=True),
454+
shape_crs=coord_crs or WGS84_CRS,
449455
**layer_params,
450456
**image_params,
451457
**dataset_params,
@@ -845,17 +851,20 @@ def point(
845851
lon: float = Path(..., description="Longitude"),
846852
lat: float = Path(..., description="Latitude"),
847853
src_path=Depends(self.path_dependency),
854+
coord_crs: Optional[CRS] = Depends(CRSParams),
848855
layer_params=Depends(self.layer_dependency),
849856
dataset_params=Depends(self.dataset_dependency),
850857
reader_params=Depends(self.reader_dependency),
851858
env=Depends(self.environment_dependency),
852859
):
853860
"""Get Point value for a dataset."""
861+
854862
with rasterio.Env(**env):
855863
with self.reader(src_path, **reader_params) as src_dst:
856864
pts = src_dst.point(
857865
lon,
858866
lat,
867+
coord_crs=coord_crs or WGS84_CRS,
859868
**layer_params,
860869
**dataset_params,
861870
)
@@ -949,6 +958,7 @@ def part(
949958
maxy: float = Path(..., description="Bounding box max Y"),
950959
format: ImageType = Query(..., description="Output image type."),
951960
src_path=Depends(self.path_dependency),
961+
coord_crs: Optional[CRS] = Depends(CRSParams),
952962
layer_params=Depends(self.layer_dependency),
953963
dataset_params=Depends(self.dataset_dependency),
954964
image_params=Depends(self.img_dependency),
@@ -969,6 +979,7 @@ def part(
969979
with self.reader(src_path, **reader_params) as src_dst:
970980
image = src_dst.part(
971981
[minx, miny, maxx, maxy],
982+
bounds_crs=coord_crs or WGS84_CRS,
972983
**layer_params,
973984
**image_params,
974985
**dataset_params,
@@ -1014,6 +1025,7 @@ def geojson_crop(
10141025
None, description="Output image type. Default is auto."
10151026
),
10161027
src_path=Depends(self.path_dependency),
1028+
coord_crs: Optional[CRS] = Depends(CRSParams),
10171029
layer_params=Depends(self.layer_dependency),
10181030
dataset_params=Depends(self.dataset_dependency),
10191031
image_params=Depends(self.img_dependency),
@@ -1034,6 +1046,7 @@ def geojson_crop(
10341046
with self.reader(src_path, **reader_params) as src_dst:
10351047
image = src_dst.feature(
10361048
geojson.dict(exclude_none=True),
1049+
shape_crs=coord_crs or WGS84_CRS,
10371050
**layer_params,
10381051
**image_params,
10391052
**dataset_params,
@@ -1135,6 +1148,7 @@ def info_geojson(
11351148
with rasterio.Env(**env):
11361149
with self.reader(src_path, **reader_params) as src_dst:
11371150
return Feature(
1151+
type="Feature",
11381152
geometry=Polygon.from_bounds(*src_dst.geographic_bounds),
11391153
properties={
11401154
asset: asset_info
@@ -1254,6 +1268,7 @@ def geojson_statistics(
12541268
..., description="GeoJSON Feature or FeatureCollection."
12551269
),
12561270
src_path=Depends(self.path_dependency),
1271+
coord_crs: Optional[CRS] = Depends(CRSParams),
12571272
layer_params=Depends(AssetsBidxExprParamsOptional),
12581273
dataset_params=Depends(self.dataset_dependency),
12591274
image_params=Depends(self.img_dependency),
@@ -1265,7 +1280,7 @@ def geojson_statistics(
12651280
"""Get Statistics from a geojson feature or featureCollection."""
12661281
fc = geojson
12671282
if isinstance(fc, Feature):
1268-
fc = FeatureCollection(features=[geojson])
1283+
fc = FeatureCollection(type="FeatureCollection", features=[geojson])
12691284

12701285
with rasterio.Env(**env):
12711286
with self.reader(src_path, **reader_params) as src_dst:
@@ -1276,6 +1291,7 @@ def geojson_statistics(
12761291
for feature in fc:
12771292
data = src_dst.feature(
12781293
feature.dict(exclude_none=True),
1294+
shape_crs=coord_crs or WGS84_CRS,
12791295
**layer_params,
12801296
**image_params,
12811297
**dataset_params,
@@ -1370,6 +1386,7 @@ def info_geojson(
13701386
with rasterio.Env(**env):
13711387
with self.reader(src_path, **reader_params) as src_dst:
13721388
return Feature(
1389+
type="Feature",
13731390
geometry=Polygon.from_bounds(*src_dst.geographic_bounds),
13741391
properties=src_dst.info(**bands_params),
13751392
)
@@ -1444,6 +1461,7 @@ def geojson_statistics(
14441461
..., description="GeoJSON Feature or FeatureCollection."
14451462
),
14461463
src_path=Depends(self.path_dependency),
1464+
coord_crs: Optional[CRS] = Depends(CRSParams),
14471465
bands_params=Depends(BandsExprParamsOptional),
14481466
dataset_params=Depends(self.dataset_dependency),
14491467
image_params=Depends(self.img_dependency),
@@ -1455,7 +1473,7 @@ def geojson_statistics(
14551473
"""Get Statistics from a geojson feature or featureCollection."""
14561474
fc = geojson
14571475
if isinstance(fc, Feature):
1458-
fc = FeatureCollection(features=[geojson])
1476+
fc = FeatureCollection(type="FeatureCollection", features=[geojson])
14591477

14601478
with rasterio.Env(**env):
14611479
with self.reader(src_path, **reader_params) as src_dst:
@@ -1466,6 +1484,7 @@ def geojson_statistics(
14661484
for feature in fc:
14671485
data = src_dst.feature(
14681486
feature.dict(exclude_none=True),
1487+
shape_crs=coord_crs or WGS84_CRS,
14691488
**bands_params,
14701489
**image_params,
14711490
**dataset_params,

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ def info_geojson(
205205
) as src_dst:
206206
info = src_dst.info()
207207
return Feature(
208-
geometry=Polygon.from_bounds(*info.bounds), properties=info
208+
type="Feature",
209+
geometry=Polygon.from_bounds(*info.bounds),
210+
properties=info,
209211
)
210212

211213
############################################################################

0 commit comments

Comments
 (0)