@@ -1237,6 +1237,235 @@ def add_stac_layer(
12371237
12381238 self .cog_layer_dict [name ] = params
12391239
1240+ def add_cmr_layer (
1241+ self ,
1242+ concept_id : str ,
1243+ datetime : Optional [str ] = None ,
1244+ backend : str = "rasterio" ,
1245+ variable : Optional [str ] = None ,
1246+ bands : Optional [Union [str , List [str ]]] = None ,
1247+ bands_regex : Optional [str ] = None ,
1248+ expression : Optional [str ] = None ,
1249+ name : str = "CMR Layer" ,
1250+ attribution : str = "NASA Earthdata" ,
1251+ opacity : float = 1.0 ,
1252+ shown : bool = True ,
1253+ rescale : Optional [Union [str , List ]] = None ,
1254+ colormap_name : Optional [str ] = None ,
1255+ color_formula : Optional [str ] = None ,
1256+ titiler_cmr_endpoint : Optional [str ] = None ,
1257+ zoom_to_layer : bool = True ,
1258+ layer_index : Optional [int ] = None ,
1259+ ** kwargs ,
1260+ ) -> None :
1261+ """Adds a NASA Earthdata CMR layer to the map using TiTiler CMR.
1262+
1263+ This method allows you to visualize NASA Earthdata collections directly
1264+ on the map using the TiTiler CMR endpoint.
1265+
1266+ Args:
1267+ concept_id (str): NASA CMR collection concept ID (e.g., 'C2036881735-POCLOUD').
1268+ datetime (str, optional): RFC3339 datetime or range (e.g., '2024-01-15' or
1269+ '2024-01-01/2024-01-31'). Defaults to None.
1270+ backend (str, optional): Backend to use - 'rasterio' for COGs or 'xarray' for
1271+ NetCDF/Zarr. Defaults to 'rasterio'.
1272+ variable (str, optional): Variable name for xarray backend datasets. Required
1273+ when using backend='xarray'.
1274+ bands (str | list, optional): Band name(s) for rasterio backend.
1275+ bands_regex (str, optional): Regex pattern for selecting bands (e.g., 'B[0-9][0-9]').
1276+ expression (str, optional): Band math expression (e.g., '(B05-B04)/(B05+B04)').
1277+ name (str, optional): The layer name. Defaults to 'CMR Layer'.
1278+ attribution (str, optional): Attribution text. Defaults to 'NASA Earthdata'.
1279+ opacity (float, optional): Layer opacity (0.0 to 1.0). Defaults to 1.0.
1280+ shown (bool, optional): Whether the layer is visible. Defaults to True.
1281+ rescale (str | list, optional): Min/max values for rescaling (e.g., '270,305'
1282+ or [[270, 305]]).
1283+ colormap_name (str, optional): Name of colormap (e.g., 'thermal', 'viridis').
1284+ color_formula (str, optional): Color formula (e.g., 'Gamma RGB 3.5 Saturation 1.7').
1285+ titiler_cmr_endpoint (str, optional): TiTiler CMR endpoint URL.
1286+ zoom_to_layer (bool, optional): Whether to zoom to the layer extent. Defaults to True.
1287+ layer_index (int, optional): Index to insert the layer. Defaults to None.
1288+ **kwargs: Additional arguments passed to the TiTiler CMR endpoint.
1289+
1290+ Examples:
1291+ >>> # Sea Surface Temperature using xarray backend
1292+ >>> m = leafmap.Map()
1293+ >>> m.add_cmr_layer(
1294+ ... concept_id="C2036881735-POCLOUD",
1295+ ... datetime="2024-01-15",
1296+ ... backend="xarray",
1297+ ... variable="analysed_sst",
1298+ ... rescale="270,305",
1299+ ... colormap_name="thermal",
1300+ ... name="Sea Surface Temperature"
1301+ ... )
1302+
1303+ >>> # HLS Landsat using rasterio backend
1304+ >>> m.add_cmr_layer(
1305+ ... concept_id="C2021957657-LPCLOUD",
1306+ ... datetime="2024-06-20T00:00:00Z/2024-06-27T23:59:59Z",
1307+ ... backend="rasterio",
1308+ ... bands=["B04", "B03", "B02"],
1309+ ... bands_regex="B[0-9][0-9]",
1310+ ... color_formula="Gamma RGB 3.5 Saturation 1.7 Sigmoidal RGB 15 0.35",
1311+ ... name="HLS Landsat"
1312+ ... )
1313+ """
1314+ from .stac import cmr_tile , cmr_bounds
1315+
1316+ if os .environ .get ("USE_MKDOCS" ) is not None :
1317+ return
1318+
1319+ tile_url = cmr_tile (
1320+ concept_id = concept_id ,
1321+ datetime = datetime ,
1322+ backend = backend ,
1323+ variable = variable ,
1324+ bands = bands ,
1325+ bands_regex = bands_regex ,
1326+ expression = expression ,
1327+ rescale = rescale ,
1328+ colormap_name = colormap_name ,
1329+ color_formula = color_formula ,
1330+ titiler_cmr_endpoint = titiler_cmr_endpoint ,
1331+ ** kwargs ,
1332+ )
1333+
1334+ if tile_url is None :
1335+ print ("Failed to get CMR tile URL" )
1336+ return
1337+
1338+ self .add_tile_layer (tile_url , name , attribution , opacity , shown , layer_index )
1339+
1340+ if zoom_to_layer :
1341+ bounds = cmr_bounds (
1342+ concept_id = concept_id ,
1343+ datetime = datetime ,
1344+ backend = backend ,
1345+ variable = variable ,
1346+ titiler_cmr_endpoint = titiler_cmr_endpoint ,
1347+ )
1348+ # Skip zooming if bounds are global (TiTiler CMR returns global bounds
1349+ # when actual data bounds are not available)
1350+ if bounds is not None and not common .is_global_bounds (bounds ):
1351+ self .fit_bounds ([[bounds [1 ], bounds [0 ]], [bounds [3 ], bounds [2 ]]])
1352+ common .arc_zoom_to_extent (bounds [0 ], bounds [1 ], bounds [2 ], bounds [3 ])
1353+
1354+ def add_cmr_timeseries (
1355+ self ,
1356+ concept_id : str ,
1357+ datetime : str ,
1358+ step : str = "P1D" ,
1359+ temporal_mode : str = "point" ,
1360+ backend : str = "rasterio" ,
1361+ variable : Optional [str ] = None ,
1362+ bands : Optional [Union [str , List [str ]]] = None ,
1363+ bands_regex : Optional [str ] = None ,
1364+ expression : Optional [str ] = None ,
1365+ rescale : Optional [Union [str , List ]] = None ,
1366+ colormap_name : Optional [str ] = None ,
1367+ color_formula : Optional [str ] = None ,
1368+ name_prefix : str = "CMR" ,
1369+ attribution : str = "NASA Earthdata" ,
1370+ opacity : float = 1.0 ,
1371+ time_interval : int = 1 ,
1372+ position : str = "bottomright" ,
1373+ slider_length : str = "150px" ,
1374+ titiler_cmr_endpoint : Optional [str ] = None ,
1375+ ** kwargs ,
1376+ ) -> None :
1377+ """Adds a NASA Earthdata CMR time series layer with an interactive time slider.
1378+
1379+ This method creates multiple tile layers for different time steps and adds
1380+ a time slider control to navigate through the time series.
1381+
1382+ Args:
1383+ concept_id (str): NASA CMR collection concept ID (e.g., 'C2036881735-POCLOUD').
1384+ datetime (str): RFC3339 datetime range (e.g., '2024-01-01/2024-01-31').
1385+ step (str, optional): ISO 8601 duration for time steps (e.g., 'P1D' for 1 day,
1386+ 'P1M' for 1 month, 'P2W' for 2 weeks). Defaults to 'P1D'.
1387+ temporal_mode (str, optional): Temporal mode - 'point' or 'range'. Defaults to 'point'.
1388+ backend (str, optional): Backend to use - 'rasterio' for COGs or 'xarray' for
1389+ NetCDF/Zarr. Defaults to 'rasterio'.
1390+ variable (str, optional): Variable name for xarray backend datasets.
1391+ bands (str | list, optional): Band name(s) for rasterio backend.
1392+ bands_regex (str, optional): Regex pattern for selecting bands (e.g., 'B[0-9][0-9]').
1393+ expression (str, optional): Band math expression (e.g., '(B05-B04)/(B05+B04)').
1394+ rescale (str | list, optional): Min/max values for rescaling (e.g., '270,305'
1395+ or [[270, 305]]).
1396+ colormap_name (str, optional): Name of colormap (e.g., 'thermal', 'viridis').
1397+ color_formula (str, optional): Color formula (e.g., 'Gamma RGB 3.5 Saturation 1.7').
1398+ name_prefix (str, optional): Prefix for layer names. Defaults to 'CMR'.
1399+ attribution (str, optional): Attribution text. Defaults to 'NASA Earthdata'.
1400+ opacity (float, optional): Layer opacity (0.0 to 1.0). Defaults to 1.0.
1401+ time_interval (int, optional): Time interval in seconds between frames. Defaults to 1.
1402+ position (str, optional): Position of the time slider ('topleft', 'topright',
1403+ 'bottomleft', 'bottomright'). Defaults to 'bottomright'.
1404+ slider_length (str, optional): Length of the time slider. Defaults to '150px'.
1405+ titiler_cmr_endpoint (str, optional): TiTiler CMR endpoint URL.
1406+ **kwargs: Additional arguments passed to the TiTiler CMR endpoint.
1407+
1408+ Example:
1409+ >>> m = leafmap.Map()
1410+ >>> m.add_cmr_timeseries(
1411+ ... concept_id="C2036881735-POCLOUD",
1412+ ... datetime="2023-11-01/2024-10-30",
1413+ ... step="P1M",
1414+ ... backend="xarray",
1415+ ... variable="sea_ice_fraction",
1416+ ... colormap_name="blues_r",
1417+ ... rescale="0,1"
1418+ ... )
1419+ """
1420+ from .stac import cmr_timeseries_tilejson
1421+
1422+ if os .environ .get ("USE_MKDOCS" ) is not None :
1423+ return
1424+
1425+ tilejsons = cmr_timeseries_tilejson (
1426+ concept_id = concept_id ,
1427+ datetime = datetime ,
1428+ step = step ,
1429+ temporal_mode = temporal_mode ,
1430+ backend = backend ,
1431+ variable = variable ,
1432+ bands = bands ,
1433+ bands_regex = bands_regex ,
1434+ expression = expression ,
1435+ rescale = rescale ,
1436+ colormap_name = colormap_name ,
1437+ color_formula = color_formula ,
1438+ titiler_cmr_endpoint = titiler_cmr_endpoint ,
1439+ ** kwargs ,
1440+ )
1441+
1442+ if tilejsons is None :
1443+ print ("Failed to get CMR time series TileJSON" )
1444+ return
1445+
1446+ layers_dict = {}
1447+
1448+ for dt_str , tilejson in tilejsons .items ():
1449+ if "tiles" in tilejson :
1450+ tile_url = tilejson ["tiles" ][0 ]
1451+ # Format the datetime string for display (extract date portion)
1452+ if "T" in dt_str :
1453+ label = dt_str .split ("T" )[0 ]
1454+ else :
1455+ label = dt_str
1456+ layers_dict [label ] = tile_url
1457+
1458+ if len (layers_dict ) == 0 :
1459+ print ("No valid time series layers found" )
1460+ return
1461+
1462+ self .add_time_slider (
1463+ layers = layers_dict ,
1464+ time_interval = time_interval ,
1465+ position = position ,
1466+ slider_length = slider_length ,
1467+ )
1468+
12401469 def add_zarr (
12411470 self ,
12421471 url : str ,
0 commit comments