22"""STAC item augmentation: add CRS metadata and preview links."""
33
44import argparse
5+ import logging
56import os
67import sys
78import urllib .parse
1112from pystac import Item , Link
1213from pystac .extensions .projection import ProjectionExtension
1314
15+ logging .basicConfig (
16+ level = logging .INFO , format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
17+ )
18+ logger = logging .getLogger (__name__ )
19+
1420EXPLORER_BASE = os .getenv ("EXPLORER_BASE_URL" , "https://explorer.eopf.copernicus.eu" )
1521
1622
@@ -60,8 +66,8 @@ def add_visualization(item: Item, raster_base: str, collection_id: str) -> None:
6066 _add_tile_links (item , base_url , query , "Sentinel-1 GRD VH" )
6167
6268 elif coll_lower .startswith (("sentinel-2" , "sentinel2" )):
63- # S2: Static quicklook path
64- var_path = "/quality/l2a_quicklook/r10m:tci"
69+ # S2: Quicklook path (with /0/ for native resolution when using crs-groups)
70+ var_path = "/quality/l2a_quicklook/r10m/0 :tci"
6571 query = (
6672 f"variables={ urllib .parse .quote (var_path , safe = '' )} &bidx=1&bidx=2&bidx=3&assets=TCI_10m"
6773 )
@@ -96,10 +102,17 @@ def _add_tile_links(item: Item, base_url: str, query: str, title: str) -> None:
96102 )
97103
98104
99- def augment (item : Item , * , raster_base : str , collection_id : str , verbose : bool ) -> Item :
100- """Augment STAC item with extensions and links."""
101- if verbose :
102- print (f"[augment] { item .id } " )
105+ def augment (item : Item , * , raster_base : str , collection_id : str ) -> Item :
106+ """Augment STAC item with extensions and links.
107+
108+ Args:
109+ item: STAC item to augment
110+ raster_base: TiTiler raster API base URL
111+ collection_id: Collection ID for viewer links
112+
113+ Returns:
114+ Augmented item (modified in place)
115+ """
103116 add_projection (item )
104117 add_visualization (item , raster_base , collection_id )
105118 return item
@@ -108,45 +121,42 @@ def augment(item: Item, *, raster_base: str, collection_id: str, verbose: bool)
108121def main (argv : list [str ] | None = None ) -> int :
109122 """Main entry point."""
110123 p = argparse .ArgumentParser (description = "Augment STAC item" )
111- p .add_argument ("--stac" , required = True , help = "STAC API base" )
112- p .add_argument ("--collection" , required = True , help = "Collection ID" )
124+ p .add_argument ("--stac-api-url " , required = True , help = "STAC API base URL " )
125+ p .add_argument ("--collection-id " , required = True , help = "Collection ID" )
113126 p .add_argument ("--item-id" , required = True , help = "Item ID" )
114- p .add_argument ("--bearer" , default = "" , help = "Bearer token" )
127+ p .add_argument ("--bearer" , default = "" , help = "Bearer token (optional) " )
115128 p .add_argument (
116- "--raster-base " ,
129+ "--raster-api-url " ,
117130 default = "https://api.explorer.eopf.copernicus.eu/raster" ,
118- help = "TiTiler base" ,
131+ help = "TiTiler raster API base URL " ,
119132 )
120- p .add_argument ("--verbose" , action = "store_true" )
133+ p .add_argument ("--verbose" , action = "store_true" , help = "Enable verbose logging" )
121134 args = p .parse_args (argv )
122135
136+ if args .verbose :
137+ logger .setLevel (logging .DEBUG )
138+
123139 headers = {"Authorization" : f"Bearer { args .bearer } " } if args .bearer else {}
124- item_url = f"{ args .stac .rstrip ('/' )} /collections/{ args .collection } /items/{ args .item_id } "
140+ item_url = (
141+ f"{ args .stac_api_url .rstrip ('/' )} /collections/{ args .collection_id } /items/{ args .item_id } "
142+ )
125143
126- # Fetch item
144+ # Fetch, augment, and update item
127145 try :
128146 with httpx .Client () as client :
147+ # Fetch item
129148 r = client .get (item_url , headers = headers , timeout = 30.0 )
130149 r .raise_for_status ()
131150 item = Item .from_dict (r .json ())
132- except Exception as e :
133- print (f"ERROR: GET failed: { e } " , file = sys .stderr )
134- return 1
135151
136- # Augment with CRS + preview links
137- target_collection = item .collection_id or args .collection
152+ # Augment with CRS + preview links
153+ target_collection = item .collection_id or args .collection_id
154+ augment (item , raster_base = args .raster_api_url , collection_id = target_collection )
138155
139- augment (
140- item ,
141- raster_base = args .raster_base ,
142- collection_id = target_collection ,
143- verbose = args .verbose ,
144- )
145-
146- # Update item via PUT
147- target_url = f"{ args .stac .rstrip ('/' )} /collections/{ target_collection } /items/{ item .id } "
148- try :
149- with httpx .Client () as client :
156+ # Update item via PUT
157+ target_url = (
158+ f"{ args .stac_api_url .rstrip ('/' )} /collections/{ target_collection } /items/{ item .id } "
159+ )
150160 r = client .put (
151161 target_url ,
152162 json = item .to_dict (),
@@ -155,13 +165,15 @@ def main(argv: list[str] | None = None) -> int:
155165 )
156166 r .raise_for_status ()
157167 if args .verbose :
158- print (f"PUT { target_url } → { r .status_code } " )
168+ logger .debug (f"PUT { target_url } → { r .status_code } " )
169+
170+ logger .info (f"✅ Augmented { item .id } in { target_collection } " )
171+ return 0
172+
159173 except Exception as e :
160- print (f"ERROR: PUT failed : { e } " , file = sys . stderr )
174+ logger . error (f"Failed to augment { args . item_id } : { e } " )
161175 return 1
162176
163- return 0
164-
165177
166178if __name__ == "__main__" :
167179 sys .exit (main ())
0 commit comments