From 286eaa8611ac5840b93174c4c2ac92fd5aa09991 Mon Sep 17 00:00:00 2001 From: vincentsarago Date: Wed, 23 Oct 2024 12:25:51 +0200 Subject: [PATCH] [backport] alternate asset and vrt string --- CHANGES.md | 6 + rio_tiler/io/stac.py | 39 ++- tests/fixtures/gfs.t06z.pgrb2.10p0.f010.grib2 | Bin 0 -> 32154 bytes tests/fixtures/stac_alternate.json | 85 +++++ tests/fixtures/stac_grib.json | 304 ++++++++++++++++++ tests/test_io_stac.py | 52 +++ 6 files changed, 481 insertions(+), 5 deletions(-) create mode 100644 tests/fixtures/gfs.t06z.pgrb2.10p0.f010.grib2 create mode 100644 tests/fixtures/stac_alternate.json create mode 100644 tests/fixtures/stac_grib.json diff --git a/CHANGES.md b/CHANGES.md index 90b08ae5..50c852c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # unreleased +# 6.8.0 (2024-10-23) + +* Enable **Alternate** asset's HREF for STAC by using `RIO_TILER_STAC_ALTERNATE_KEY` environment variable [Backported from `7.0`] + +* Adding support for GDAL VRT Connection string for STAC Assets [Backported from `7.0`] + # 6.7.0 (2024-09-05) * raise `MissingCRS` or `InvalidGeographicBounds` errors when Xarray datasets have wrong geographic metadata diff --git a/rio_tiler/io/stac.py b/rio_tiler/io/stac.py index e5ac2a98..a3a7bc5a 100644 --- a/rio_tiler/io/stac.py +++ b/rio_tiler/io/stac.py @@ -3,7 +3,7 @@ import json import os import warnings -from typing import Any, Dict, Iterator, Optional, Set, Type, Union +from typing import Any, Dict, Iterator, Optional, Set, Tuple, Type, Union from urllib.parse import urlparse import attr @@ -41,6 +41,8 @@ "application/x-hdf", } +STAC_ALTERNATE_KEY = os.environ.get("RIO_TILER_STAC_ALTERNATE_KEY", None) + def aws_get_object( bucket: str, @@ -275,16 +277,34 @@ def _minzoom(self): def _maxzoom(self): return self.tms.maxzoom + def _parse_vrt_asset(self, asset: str) -> Tuple[str, Optional[str]]: + if asset.startswith("vrt://") and asset not in self.assets: + parsed = urlparse(asset) + if not parsed.netloc: + raise InvalidAssetName( + f"'{asset}' is not valid, couldn't find valid asset" + ) + + if parsed.netloc not in self.assets: + raise InvalidAssetName( + f"'{parsed.netloc}' is not valid, should be one of {self.assets}" + ) + + return parsed.netloc, parsed.query + + return asset, None + def _get_asset_info(self, asset: str) -> AssetInfo: - """Validate asset names and return asset's url. + """Validate asset names and return asset's info. Args: asset (str): STAC asset name. Returns: - str: STAC asset href. + AssetInfo: STAC asset info. """ + asset, vrt_options = self._parse_vrt_asset(asset) if asset not in self.assets: raise InvalidAssetName( f"'{asset}' is not valid, should be one of {self.assets}" @@ -295,13 +315,19 @@ def _get_asset_info(self, asset: str) -> AssetInfo: info = AssetInfo( url=asset_info.get_absolute_href() or asset_info.href, - metadata=extras, + metadata=extras if not vrt_options else None, ) + if STAC_ALTERNATE_KEY and extras.get("alternate"): + if alternate := extras["alternate"].get(STAC_ALTERNATE_KEY): + info["url"] = alternate["href"] + + # https://github.com/stac-extensions/file if head := extras.get("file:header_size"): info["env"] = {"GDAL_INGESTED_BYTES_AT_OPEN": head} - if bands := extras.get("raster:bands"): + # https://github.com/stac-extensions/raster + if (bands := extras.get("raster:bands")) and not vrt_options: stats = [ (b["statistics"]["minimum"], b["statistics"]["maximum"]) for b in bands @@ -319,4 +345,7 @@ def _get_asset_info(self, asset: str) -> AssetInfo: "Some statistics data in STAC are invalid, they will be ignored." ) + if vrt_options: + info["url"] = f"vrt://{info['url']}?{vrt_options}" + return info diff --git a/tests/fixtures/gfs.t06z.pgrb2.10p0.f010.grib2 b/tests/fixtures/gfs.t06z.pgrb2.10p0.f010.grib2 new file mode 100644 index 0000000000000000000000000000000000000000..af97027f0433dd24ba9714c8ccbe269c77874e8e GIT binary patch literal 32154 zcmeHP3wYdBm7l(wP};m-O_Rxc@*quS+McVn_EQhIfAmzN{njVU4XvpV(LjMt(cEJ2;{+Tz+-)N@Ub@psMU2?)4wImc0J$?sHQ4Z~A zpBUD1^ET$|Khxybz4^Y3@uAAMb$G2xr+=vQ4)^+w_4{NB{7L<3dE=c&T1WhOX?-{{ zVCY5XY!Cy3{C3~iml$t8i1#LqrFzXGH6A~)h7lQOURsX(_mr70&3{i^L#f+&t{F>F z%|+C0A#O8D-Z&3B0mZXYnPalsj8oZP*#%Rm(y^!bqJ-;ECF zvCP>}WT0x!?@4LsJv02JgoZM=_eldC-73qE6lC%xGlu^yZtwwB!Qg-EVMxOH$EFOd zggj>U{NsQb7vvx7!_Gc?&PA4`J2Refg^99X2lfZp{L-E{HE3yH+Rky!UGUy9YFs?!P`GrJ?HBwZPT#oqFvhJH2J*4V}*P3-wy(%SUOuVWCex1az>gek;cy1in$J?{+ zYife;+17F1dw%Gz^zxAR;+m?Ip;OfR^niiQTb|ay?JGUktn%jr`>W#7P22O~{Svt5~X#_W3wzJR2kGh0R!tJqoSb-BX6{#erko?R>h*`yA3$inCGt+Y^H9tpX!dM({$jX4kxsXxH{-f;ux8}ewNCCD?(l~0_NFR{>@(jFU%S(`QKiAQ(~>W&dOWP@tC(#MOY}THeaP&d^Al#i$zH#_ z-T6zE9?RTO<Ch{!Tr}Oj{n0X?NDzvQ#Ptw`+7=cBB1O(EHSY9!sZf zuL#?n&zD?e@T8{X;*^HD+nwi^h`|f!L-H-(lWuob2HJ;X+MVaEiJN^I(C+AX&cB{Y z!^CM&qBr)1&AMirHob=V%MeqdX59odvu5R z-|J7ySNcD^XNNvuCe)FrN5ipLLXV|3@O&ab$L)8R=Dim-|0-qp=y>U-_a z7-9TO8tM6T*xrTJm4ZYLuJOp)S zHE*;#We$FopqVru?sn&}Z{At?;J$Aa?5G)1Q;=`gc4r%YEo`%|`P5>#dGK5Z`??da zBM!G5v%c;OmM4`XM})PU__|ZVvuebQbG`1AkF7Q9R6D|59bWxGE@NMJTKSEA-HCSR zeEgb^U-P8Gwu_``-8z4t*kdE2_mkl3&gio?9OsS9^}pwM-6^&96_%9S^e&6<>^JnK zCjZ#Dfh`!fU1HnZm9{OK^>ydcm44Zj99{POYiR zT=u=y%HZ*J=X++=#PwL3SKOR1^JsUmgh%$WCSpKE6(%KpKErOlIE{LWyUeQ}vq`y3_g}@g%xsm!$3Ey@H=3!X^GIH<>;7U^wkN z*X4rJ9M~tIuUzX@5z5-Y>rQF!AB<0eAKJCF*KAY<{~%v?G9Hp&sr&ZE)Fk#Neh~6k z{V}gQn=vdcchyQ9{zUc;PwHZ|Ng;*hFM>C#_+l+$Y=29+BfQTXB0or zasRH*j{h?;zfTcLx&7N2l)HN+13FWE-RV7Lubr|O^-qWWnVa+vH8ZS~mI(Xah|4b!QW<&Gq(`E#)H75A+<0 zoJc1KW$70(<5Aa&4PxYn^GH47`N6h33o>7=l5SS`f9!RqwAg11!ShJhO258JKCz*C zJiSTBd8-pf&V1cjg`dl5N3qk_Df69raIZUGNu|%b@9nom6B=Kd-_;v8Fw5&sXKo37lJ&kKgX(-@-Z6*PY%j++RBEYthN-Val&NV>T{JBlKNv z|JI5uxAkyvU#Z(rqRmAAZNkiWD3oh7^Jf&UcX=!Dx-%_ndzj;O6K(}AO?%wwge@>Y1 z?_d6FAMd=#NqRUI|GL#yA9r+EKDU-xy?yr!Lq7gbj|cytfB#KSiJ^1BeaVD@!To=5 zowbAOVwT_Q7g{(5?^vssOVslzL+8{P_l6CO-}QLHz#x9M@rIABP8t0BMwk0=%eN-h zlPa=!+30cpmJ99q`~PNqIe+w%-2-Od&m=7WTo;62-EaLyec9^GeLwf)gjs(I=(u^v z`J&J0fII~`1lQ*pZ~eZt&s=!hPPO{Zg?G5WKX8_n59w|_=aVuSd2F4YiI*&~eoi{u z`zN*!;{9?(roOg)aB}Oo{&2%zuGestLQy(Ba4=c4c z%x5E4P0n8LzklL5c=uzO{+5i|{5Dnn+`n?-^K31%d~Dm78ILxt&)lE;{`BR^`d8+@ z_sq2MhIDelUp<`Z4`cC<)?w@Qy#Cv*F& z^_Zh`Wv1Tl|I19dIK066`~LY&l1pIUZ2TPR{7`A8U2fZC{qW{fR;MN<|FxNNJXC&i zt^dV`a@qG`vdcF~{_Fq2-Zz5hesImfon!dE@%=eS|BWAy@7KTSovixy(|-bCm&yU#@>rBxZ{cm=@Eb1<>Tj@p)z}K*}B`>+ubuJ=8yD( z@Oyvj|LiVX2d8F-K{~=GOhT zUb+VTs12vt{?+=p?N1CQdk2jChjv^1?H}8yhp*QY+aI-lb2j=%=cA8sgLl_c zmfx%YD{sJzum9Hg@h^BzH(qGZbtme_A3cTo?jpPIFSpqEa_yaYaYO&VHd=mf#yKuc z*#6hXKD+K4J(iEPSKpK{_*YHaZq~iU?at}b)7qWB;wEl)CSUEj-LRj0a>I>=eKl`u zfd=}u(Qg(d3~c}SM)Bb{pNbpUx@5Hm`Y(Ih@7{}}7bo;sW*qgLKVN+9F{1xN6))YE zGO%UZ$v&(D41-=6^s7EGSEGeg$#2h(8(1*?x`ct`xqI02{t>V#f_FT-N~mfa~Fu&|47KU zyJC+S)4sjOYMSGE?7bSfDW##4H|5#_IfY^8utVyf>s83gSv6WcNjc8J^DjB6(d;8( zoU0vjISTv|cAxt*ji&!!D?hkAWne4tqmSG9PMo(H>*rWM6WEJT9&P(?5wm{jX1i}E z=rm!z2y&#og+aF-uod*FpL*&M{6;T1En(;{2HjfdZ!x}u4{!HVdkr6IF61*4@uVxX zKCb6!Z`7;z^hTwpL*A3CuSjX=>}Cn}YX|=eA>V48BLY4ez$fi-4*azo{N-DJc64J0 z>^T~-j(S;Y?L;c=I_Iz8PwK!2?Y7X`vzm+Z69K)(ucFKqD`pPhVRH{Ko_N5}ZZ0UOV~z1Ba3=Pk2x z_lEYwMC(a4!aus6uyKz3Qr_LPXYfURFrG55(EmYST5vw<9dTEv-%=}go-28WA9i)# zKpv@t{K_z<9hP8Ri@a2i_+1CcJP^o-+{fiJ=D$Ujj^Ml=<+lF}W5%!Ni)!SvSvJqm zKZJUOeY_q|AlEv;Qk-YLjn9hoN^y>6#H%^TJIKGu$4(T(hvNE1%W+J|_ZbBSNKE30igx0$tels5`+7ta2byhM}Ixl7HgnmK*v8tK(3gxbsa%{jsz6bf16LM~qkSFPUZYI?qWtyL^H= zOUe-^m{(x0as=`N=(sqKx{UlLU%nx%#iIYGEbyxe=!^2Gf*xoGj3f21BgU5+;IaNi zoK&dO+&+w7^f&q?;)ZHMoGJl5_*3$MGfzrsK3Ufa@}GjeQQoeuMI3Z-s0R6h^%CRr zbelJweek@r3*$(oLnGJ)}yT3SZ5=?d4G6tXWZz4bv5P3djsW0 zz8K%>Pq^23f422M>sr?33h~H;-6@>Q-7`?X(k_@s=~oW!wf886^E0n9Z%{v~4EiG; z?jgw+a34XvM7xv<=x2VWKBzwhdC(uYk2Ko4EQxsH=)=zx?HT$ZJwcu5?239}y@`69 z=k$>8TwR=p`xNe3l$WBPATEY_W0M-Fs0_Dfm12W}b5K8*xGwp}uZIy;hGpjQ2p+uQAkh9Hahr z_Re~X`KAl^*A5#OS$Dd7FYkw_gPdN_S77{d{s%uM9^)G8Kw*8z`!@B9dkOcay_2r0 zME>GEoBnz3JZm?+FCKw>0DESgMSc|X;X=@Z9Fh-hx=pnG(Vv}PvhRZWh4Zu*7k|-@ zp*{1y&N1ypC`ZZ<{_E~ptm~m~*7*wYmVDALDKF+L)~OQ0y&ruaXP4-=kd8t<$a9fD zsl&aI_XXy8#uLP2(s6Ya>O0!AVtz!tR|}DcP=`>zl#inWIgoG0DaKvKC)bbS{hFZtt^?`}#Myw*qdr8IPO3+hkR!%*^(|4|02g5!(g z29|yIvnd113;rMmRz7l*2Cu_~7&kn--Jfr~>{AKzy%zI}u&(%`6U=<_PnW)Ipp;y8 zN8G@sT z%~;4E&mX~gyB|C|rN1W+{L`%(gp$ub6E{$x7YPHe z5a;B4$?kB<;8o+?MfN;ucHPh7X1>MB)s1OCVaWSP$f*W&!qzTS_wh#?y6&9Sumhg2 z*6LF&wECsp$dOhrf_>C<%ZDn4JygT4W}Uhxq3M$^uBY79^k=^!`n$}Az1M?Z@R=Na zq%5w#d%yeHLkWY2eZA+te6k)oJC?at{}Qo!SM9LV=AT@j(s(?dKyE_0w8Ku8)ORK{ zo+96|Gp(L+e((AEC;ji;KlkOO+CaY&a(Vnizl`hk&ORIAcl&n#*vwNtQepMSvFg5R zX2N{$1id^fFIi;$*!g?c`L7x}v=3Ep&!wuMuf^65y;m%sg8iiv@|a`oTyVj~v<(eob8rH+LCkp5!CzjpZhBAjzco0V(5<Z}wG)C&?FVoM#+oo=pDYuPY6n z`6Hjb9ycIQ=3B;h#={!K<@q)qdaz^0?Ihv^<3Ht1|8U=faw&q}Q~_36yJ7xN<*-NO zb!Qj%{n3|)iwgx>0BQI1Z-M_&uL6CrUZGuj zqwrhE+v$P)QhrCE&VwI&ca@)%(ss=AJO5x_hg}QSNj3D!bFltky;PqW?_52F`ig$z z_Mv>~M~$dU$hX_a?yII{=6Qv@=<2Xq_#xtlDn!0PU8C~hFQ|VhZ=rl(UrrvdYeBqp z_JMktb)c$1e#CyN#O5OxxAENrz2u9i%UGYm4_)1YI+^i6m4H6-fuIgmGcRj2`lEk4 zzos9czGYn{^HKjW?n3`gzpR5%k23$Mxpp7YXZ&S8RWoef7se0NKa3lULn>tHur6WU z#klX{F5(OCAM{t^JG*7wQ;2+nI#snG9~U5>ppJ9-i}eiT=lDY$p7idUWv= z_alC%9+}q!@zB-(^egCr@k_BTrkyfQA&xN4F#q!Yqr#|%+TcI)P`B`2$Gn6*qpDCZ zG0!sIA+FJG>8}cMboyc5V111H1>-u;NxNlTq3XbAE8-#Ir`v~h1>4C0=+vXWX1$Gj8U0KZSvzobXEEY8@~Ok4-{L%KA?yl#u}-Fb>7TrZDasf1 zDC;$zpHR3z{e|~R*7tRYqtGYqO|@D1kuTb*szW`|g6{!;O}=LDH^Tc}1>!00MT-72 zAN3>Sd>!+%wJR4-881l>_aaq?IuvrFycO>Suv5kpVco>K9eSsqGTzYtNr&rX$+Pyp zf_$Ost$olh8NaA6#&!BB>p8|P>c_=(#9Q_&=+7!_>p% zTpk7;#x2^T%loYRkXID#vkCH}oVx72SfaRpvMxk_iSeHDb^Rpv4REg{U$h_QW!CX( z9`X(XSBQ@Ll6XPcR$Hi0J$6P+d{nqJ=br|&E r>Ol5aSSO)R=j24cCVj?l#wF-2xyRmj=pQ@}?VoXjbq4*`v^)P7`j{{4 literal 0 HcmV?d00001 diff --git a/tests/fixtures/stac_alternate.json b/tests/fixtures/stac_alternate.json new file mode 100644 index 00000000..0bd8f999 --- /dev/null +++ b/tests/fixtures/stac_alternate.json @@ -0,0 +1,85 @@ +{ + "stac_version": "0.9.0", + "stac_extensions": [ + "https://stac-extensions.github.io/file/v2.1.0/schema.json", + "https://stac-extensions.github.io/alternate-assets/v1.2.0/schema.json" + ], + "type": "Feature", + "id": "JQT-123456789", + "bbox": [-81.3085227080129, 32.10817938759764, -78.81735409341113, 34.22870275071835], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -81.3085227080129, + 32.10817938759764 + ], + [ + -78.81735409341113, + 32.10817938759764 + ], + [ + -78.81735409341113, + 34.22870275071835 + ], + [ + -81.3085227080129, + 34.22870275071835 + ], + [ + -81.3085227080129, + 32.10817938759764 + ] + ] + ] + }, + "properties": { + "datetime": "2016-05-03T13:21:30.040Z", + "collection": "JQT" + }, + "links": [ + { + "rel": "self", + "href": "http://cool-sat.com/catalog/JQT/a-fake-item.json" + }, + { + "rel": "collection", + "href": "http://cool-sat.com/catalog.json" + } + ], + "assets": { + "red": { + "href": "http://somewhere-over-the-rainbow.io/red.tif", + "title": "red", + "file:header_size": 16384, + "alternate:name": "HTTPS", + "alternate": { + "s3": { + "href": "s3://somewhere-over-the-rainbow.io/red.tif", + "alternate:name": "S3" + } + } + }, + "green": { + "href": "http://somewhere-over-the-rainbow.io/green.tif", + "title": "green", + "file:header_size": 30000 + }, + "blue": { + "href": "http://somewhere-over-the-rainbow.io/blue.tif", + "title": "blue", + "file:header_size": 20000 + }, + "lowres": { + "href": "http://somewhere-over-the-rainbow.io/lowres.tif", + "title": "lowres" + }, + "thumbnail": { + "href": "http://cool-sat.com/catalog/a-fake-item/thumbnail.png", + "title": "Thumbnail", + "type": "image/png", + "roles": [ "thumbnail" ] + } + } +} diff --git a/tests/fixtures/stac_grib.json b/tests/fixtures/stac_grib.json new file mode 100644 index 00000000..09b5096d --- /dev/null +++ b/tests/fixtures/stac_grib.json @@ -0,0 +1,304 @@ +{ + "type": "Feature", + "stac_version": "1.0.0", + "id": "gribfile", + "properties": { + "proj:geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -180.125, + -89.875 + ], + [ + 179.875, + -89.875 + ], + [ + 179.875, + 90.125 + ], + [ + -180.125, + 90.125 + ], + [ + -180.125, + -89.875 + ] + ] + ] + }, + "proj:bbox": [ + -180.125, + -89.875, + 179.875, + 90.125 + ], + "proj:shape": [ + 18, + 36 + ], + "proj:transform": [ + 10.0, + 0.0, + -180.125, + 0.0, + -10.0, + 90.125, + 0.0, + 0.0, + 1.0 + ], + "proj:wkt2": "GEOGCS[\"Coordinate System imported from GRIB file\",DATUM[\"unnamed\",SPHEROID[\"Sphere\",6371229,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST]]", + "datetime": "2024-09-13T11:08:36.893626Z" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -180.125, + -89.875 + ], + [ + 179.875, + -89.875 + ], + [ + 179.875, + 90.125 + ], + [ + -180.125, + 90.125 + ], + [ + -180.125, + -89.875 + ] + ] + ] + }, + "links": [], + "assets": { + "asset": { + "href": "gfs.t06z.pgrb2.10p0.f010.grib2", + "raster:bands": [ + { + "data_type": "float64", + "scale": 1.0, + "offset": 0.0, + "statistics": { + "mean": -12.643815520808767, + "minimum": -20.000003814697266, + "maximum": 32.659996032714844, + "stddev": 12.448285782027321, + "valid_percent": 0.15432098765432098 + }, + "histogram": { + "count": 11, + "min": -20.000003814697266, + "max": 32.659996032714844, + "buckets": [ + 449, + 27, + 29, + 31, + 35, + 29, + 15, + 21, + 9, + 3 + ] + } + }, + { + "data_type": "float64", + "scale": 1.0, + "offset": 0.0, + "statistics": { + "mean": -12.609587132580073, + "minimum": -20.000003814697266, + "maximum": 32.66999435424805, + "stddev": 12.450450829609293, + "valid_percent": 0.15432098765432098 + }, + "histogram": { + "count": 11, + "min": -20.000003814697266, + "max": 32.66999435424805, + "buckets": [ + 447, + 27, + 31, + 29, + 39, + 28, + 14, + 21, + 9, + 3 + ] + } + }, + { + "data_type": "float64", + "scale": 1.0, + "offset": 0.0, + "statistics": { + "mean": -10.566084066483503, + "minimum": -20.000003814697266, + "maximum": 32.76999282836914, + "stddev": 13.981093035778388, + "valid_percent": 0.15432098765432098 + }, + "histogram": { + "count": 11, + "min": -20.000003814697266, + "max": 32.76999282836914, + "buckets": [ + 412, + 25, + 31, + 27, + 34, + 49, + 32, + 20, + 11, + 7 + ] + } + }, + { + "data_type": "float64", + "scale": 1.0, + "offset": 0.0, + "statistics": { + "mean": 20302.916205571022, + "minimum": 24.859272003173828, + "maximum": 24134.859375, + "stddev": 7731.471190491879, + "valid_percent": 0.15432098765432098 + }, + "histogram": { + "count": 11, + "min": 24.859272003173828, + "max": 24134.859375, + "buckets": [ + 49, + 24, + 17, + 11, + 8, + 6, + 11, + 11, + 11, + 500 + ] + } + }, + { + "data_type": "float64", + "scale": 1.0, + "offset": 0.0, + "statistics": { + "mean": -0.13451403068101184, + "minimum": -18.402570724487305, + "maximum": 29.097431182861328, + "stddev": 7.346453975073525, + "valid_percent": 0.15432098765432098 + }, + "histogram": { + "count": 11, + "min": -18.402570724487305, + "max": 29.097431182861328, + "buckets": [ + 16, + 47, + 120, + 191, + 153, + 64, + 32, + 15, + 8, + 2 + ] + } + }, + { + "data_type": "float64", + "scale": 1.0, + "offset": 0.0, + "statistics": { + "mean": 0.3850667698676755, + "minimum": -27.066476821899414, + "maximum": 20.933523178100586, + "stddev": 6.63843088754233, + "valid_percent": 0.15432098765432098 + }, + "histogram": { + "count": 11, + "min": -27.066476821899414, + "max": 20.933523178100586, + "buckets": [ + 2, + 7, + 15, + 34, + 106, + 212, + 175, + 64, + 25, + 8 + ] + } + } + ], + "eo:bands": [ + { + "name": "b1", + "description": "1[-] HYBL=\"Hybrid level\"" + }, + { + "name": "b2", + "description": "2[-] HYBL=\"Hybrid level\"" + }, + { + "name": "b3", + "description": "0[-] EATM=\"Entire Atmosphere\"" + }, + { + "name": "b4", + "description": "0[-] SFC=\"Ground or water surface\"" + }, + { + "name": "b5", + "description": "0[-] RESERVED(220) (Reserved for local use)" + }, + { + "name": "b6", + "description": "0[-] RESERVED(220) (Reserved for local use)" + } + ], + "roles": [] + } + }, + "bbox": [ + -180.125, + -89.875, + 179.875, + 90.125 + ], + "stac_extensions": [ + "https://stac-extensions.github.io/projection/v1.1.0/schema.json", + "https://stac-extensions.github.io/raster/v1.1.0/schema.json", + "https://stac-extensions.github.io/eo/v1.1.0/schema.json" + ] +} diff --git a/tests/test_io_stac.py b/tests/test_io_stac.py index e4f1b2cf..2f070000 100644 --- a/tests/test_io_stac.py +++ b/tests/test_io_stac.py @@ -19,6 +19,7 @@ TileOutsideBounds, ) from rio_tiler.io import Reader, STACReader +from rio_tiler.io.stac import DEFAULT_VALID_TYPE from rio_tiler.models import BandStatistics PREFIX = os.path.join(os.path.dirname(__file__), "fixtures") @@ -27,6 +28,8 @@ STAC_GDAL_PATH = os.path.join(PREFIX, "stac_headers.json") STAC_RASTER_PATH = os.path.join(PREFIX, "stac_raster.json") STAC_WRONGSTATS_PATH = os.path.join(PREFIX, "stac_wrong_stats.json") +STAC_ALTERNATE_PATH = os.path.join(PREFIX, "stac_alternate.json") +STAC_GRIB_PATH = os.path.join(PREFIX, "stac_grib.json") with open(STAC_PATH) as f: item = json.loads(f.read()) @@ -890,3 +893,52 @@ def test_expression_with_wrong_stac_stats(rio): expression="where((wrongstat>0.5),1,0)", asset_as_band=True, ) + + +@patch("rio_tiler.io.stac.STAC_ALTERNATE_KEY", "s3") +def test_alternate_assets(): + """Should return the alternate key""" + with STACReader(STAC_ALTERNATE_PATH) as stac: + assert stac._get_asset_info("red")["url"].startswith("s3://") + # fall back to href when alternate doesn't exist + assert stac._get_asset_info("blue")["url"].startswith("http://") + + +def test_vrt_string_assets(): + """Should work with VRT connection string""" + VALID_TYPE = { + *DEFAULT_VALID_TYPE, + "application/wmo-GRIB2", + } + + with STACReader(STAC_GRIB_PATH, include_asset_types=VALID_TYPE) as stac: + assert stac.assets == ["asset"] + info = stac._get_asset_info("asset") + assert info["url"] + + info_vrt = stac._get_asset_info("vrt://asset") + # without any option there is no need to use the vrt prefix + assert info["url"] == info_vrt["url"] + + info_vrt = stac._get_asset_info("vrt://asset?bands=1") + assert not info["url"] == info_vrt["url"] + assert info_vrt["url"].startswith("vrt://") and info_vrt["url"].endswith( + "?bands=1" + ) + + with pytest.raises(InvalidAssetName): + stac._get_asset_info("vrt://somthing?bands=1") + + with pytest.raises(InvalidAssetName): + stac._get_asset_info("vrt://?bands=1") + + info = stac.info(assets="vrt://asset?bands=1") + assert info["vrt://asset?bands=1"] + assert len(info["vrt://asset?bands=1"].band_metadata) == 1 + + info = stac.info(assets="vrt://asset?bands=1,2") + assert info["vrt://asset?bands=1,2"] + assert len(info["vrt://asset?bands=1,2"].band_metadata) == 2 + + img = stac.preview(assets="vrt://asset?bands=1") + assert img.count == 1