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
@@ -96,57 +102,72 @@ 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 )
118+
119+ # Fix: Remove /0/ subdirs from r10m/r20m paths (GeoZarr creates flat structure for these)
120+ # Only r60m should have /0/ subdirectories
121+ for asset in item .assets .values ():
122+ if (
123+ hasattr (asset , "href" )
124+ and isinstance (asset .href , str )
125+ and ("/r10m/0/" in asset .href or "/r20m/0/" in asset .href )
126+ ):
127+ asset .href = asset .href .replace ("/r10m/0/" , "/r10m/" ).replace ("/r20m/0/" , "/r20m/" )
128+
105129 return item
106130
107131
108132def main (argv : list [str ] | None = None ) -> int :
109133 """Main entry point."""
110134 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" )
135+ p .add_argument ("--stac-api-url " , required = True , help = "STAC API base URL " )
136+ p .add_argument ("--collection-id " , required = True , help = "Collection ID" )
113137 p .add_argument ("--item-id" , required = True , help = "Item ID" )
114- p .add_argument ("--bearer" , default = "" , help = "Bearer token" )
138+ p .add_argument ("--bearer" , default = "" , help = "Bearer token (optional) " )
115139 p .add_argument (
116- "--raster-base " ,
140+ "--raster-api-url " ,
117141 default = "https://api.explorer.eopf.copernicus.eu/raster" ,
118- help = "TiTiler base" ,
142+ help = "TiTiler raster API base URL " ,
119143 )
120- p .add_argument ("--verbose" , action = "store_true" )
144+ p .add_argument ("--verbose" , action = "store_true" , help = "Enable verbose logging" )
121145 args = p .parse_args (argv )
122146
147+ if args .verbose :
148+ logger .setLevel (logging .DEBUG )
149+
123150 headers = {"Authorization" : f"Bearer { args .bearer } " } if args .bearer else {}
124- item_url = f"{ args .stac .rstrip ('/' )} /collections/{ args .collection } /items/{ args .item_id } "
151+ item_url = (
152+ f"{ args .stac_api_url .rstrip ('/' )} /collections/{ args .collection_id } /items/{ args .item_id } "
153+ )
125154
126- # Fetch item
155+ # Fetch, augment, and update item
127156 try :
128157 with httpx .Client () as client :
158+ # Fetch item
129159 r = client .get (item_url , headers = headers , timeout = 30.0 )
130160 r .raise_for_status ()
131161 item = Item .from_dict (r .json ())
132- except Exception as e :
133- print (f"ERROR: GET failed: { e } " , file = sys .stderr )
134- return 1
135-
136- # Augment with CRS + preview links
137- target_collection = item .collection_id or args .collection
138162
139- augment (
140- item ,
141- raster_base = args .raster_base ,
142- collection_id = target_collection ,
143- verbose = args .verbose ,
144- )
163+ # Augment with CRS + preview links
164+ target_collection = item .collection_id or args .collection_id
165+ augment (item , raster_base = args .raster_api_url , collection_id = target_collection )
145166
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 :
167+ # Update item via PUT
168+ target_url = (
169+ f" { args . stac_api_url . rstrip ( '/' ) } /collections/ { target_collection } /items/ { item . id } "
170+ )
150171 r = client .put (
151172 target_url ,
152173 json = item .to_dict (),
@@ -155,13 +176,15 @@ def main(argv: list[str] | None = None) -> int:
155176 )
156177 r .raise_for_status ()
157178 if args .verbose :
158- print (f"PUT { target_url } → { r .status_code } " )
179+ logger .debug (f"PUT { target_url } → { r .status_code } " )
180+
181+ logger .info (f"✅ Augmented { item .id } in { target_collection } " )
182+ return 0
183+
159184 except Exception as e :
160- print (f"ERROR: PUT failed : { e } " , file = sys . stderr )
185+ logger . error (f"Failed to augment { args . item_id } : { e } " )
161186 return 1
162187
163- return 0
164-
165188
166189if __name__ == "__main__" :
167190 sys .exit (main ())
0 commit comments