Skip to content

Commit f721b20

Browse files
michaelmattigCopilot
andcommitted
refactor stac item processing
Co-authored-by: Copilot <copilot@github.com>
1 parent dae7867 commit f721b20

1 file changed

Lines changed: 142 additions & 109 deletions

File tree

geoengine/services/src/datasets/external/stac/loading_info.rs

Lines changed: 142 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -364,17 +364,39 @@ impl StacMultiBandMetaData {
364364
files: &mut Vec<TileFile>,
365365
fetch_tiles: bool,
366366
) -> Result<()> {
367+
let Some((time, z_index)) = self.item_time_and_z_index(item)? else {
368+
return Ok(());
369+
};
370+
371+
time_steps.push(time);
372+
373+
if !fetch_tiles {
374+
tracing::trace!(
375+
"STAC query does not require fetching tiles, skipping item with id: {}",
376+
item.id
377+
);
378+
return Ok(());
379+
}
380+
381+
for asset in item.assets.values() {
382+
self.process_stac_asset(asset, time, z_index, files)?;
383+
}
384+
385+
Ok(())
386+
}
387+
388+
fn item_time_and_z_index(&self, item: &Item) -> Result<Option<(TimeInterval, i64)>> {
367389
if item.version != stac::Version::v1_1_0 {
368390
tracing::warn!(
369391
"Skipping STAC item with unsupported version: {:?}",
370392
item.version
371393
);
372-
return Ok(());
394+
return Ok(None);
373395
}
374396

375397
let Some(item_datetime) = item.properties.datetime else {
376398
tracing::warn!("Skipping STAC item without datetime: {}", item.id);
377-
return Ok(());
399+
return Ok(None);
378400
};
379401

380402
let z_index = item
@@ -406,133 +428,144 @@ impl StacMultiBandMetaData {
406428
}
407429
};
408430

409-
time_steps.push(time);
431+
Ok(Some((time, z_index)))
432+
}
410433

411-
if !fetch_tiles {
412-
tracing::trace!(
413-
"STAC query does not require fetching tiles, skipping item with id: {}",
414-
item.id
434+
fn process_stac_asset(
435+
&self,
436+
asset: &stac::Asset,
437+
time: TimeInterval,
438+
z_index: i64,
439+
files: &mut Vec<TileFile>,
440+
) -> Result<()> {
441+
if data_type_from_asset_v1_1_0(asset) != Some(self.dataset.data_type) {
442+
return Ok(());
443+
}
444+
445+
if !proj_code_matches_dataset(&asset.additional_fields, self.dataset.projection) {
446+
return Ok(());
447+
}
448+
449+
let Some(geo_transform) = geo_transform_from_fields(&asset.additional_fields) else {
450+
tracing::warn!(
451+
"Skipping asset with href {} due to missing geo transform",
452+
asset.href
415453
);
416454
return Ok(());
455+
};
456+
457+
let Some((height, width)) = proj_shape_from_fields(&asset.additional_fields) else {
458+
tracing::warn!(
459+
"Skipping asset with href {} due to missing projection shape",
460+
asset.href
461+
);
462+
return Ok(());
463+
};
464+
465+
if (geo_transform.x_pixel_size().abs() - self.dataset.resolution.x).abs() > 1e-9
466+
|| (geo_transform.y_pixel_size().abs() - self.dataset.resolution.y).abs() > 1e-9
467+
{
468+
return Ok(());
417469
}
418470

419-
for (_asset_key, asset) in &item.assets {
420-
if data_type_from_asset_v1_1_0(&asset) != Some(self.dataset.data_type) {
421-
continue;
422-
}
471+
let Some(asset_title) = asset.title.as_deref() else {
472+
tracing::warn!(
473+
"Skipping asset with href {} due to missing title",
474+
asset.href
475+
);
476+
return Ok(());
477+
};
478+
479+
let grid_bounds = GridBoundingBox2D::new(
480+
GridIdx2D::new([0, 0]),
481+
GridIdx2D::new([(width as isize) - 1, (height as isize) - 1]),
482+
)
483+
.map_err(|_e| geoengine_operators::error::Error::InvalidDataProviderConfig)?;
484+
let spatial_partition = geo_transform.grid_to_spatial_bounds(&grid_bounds);
423485

424-
if !proj_code_matches_dataset(&asset.additional_fields, self.dataset.projection) {
486+
let file_path = gdal_file_path(&asset.href)
487+
.ok_or(geoengine_operators::error::Error::InvalidDataProviderConfig)?;
488+
489+
let gdal_config_options = self.gdal_config_options_for_file_path(&file_path);
490+
491+
for (dataset_band_idx, dataset_band) in self.dataset.bands.iter().enumerate() {
492+
if dataset_band.asset_title != asset_title {
425493
continue;
426494
}
427495

428-
let Some(geo_transform) = geo_transform_from_fields(&asset.additional_fields) else {
429-
tracing::warn!(
430-
"Skipping asset with href {} due to missing geo transform",
431-
asset.href
432-
);
496+
let Some(rasterband_channel) =
497+
Self::rasterband_channel_for_dataset_band(asset, dataset_band.band_name.as_deref())
498+
else {
433499
continue;
434500
};
435501

436-
let Some((height, width)) = proj_shape_from_fields(&asset.additional_fields) else {
437-
tracing::warn!(
438-
"Skipping asset with href {} due to missing projection shape",
439-
asset.href
440-
);
441-
continue;
442-
};
502+
files.push(TileFile {
503+
time,
504+
spatial_partition,
505+
band: dataset_band_idx as u32,
506+
z_index,
507+
params: GdalDatasetParameters {
508+
file_path: file_path.clone(),
509+
rasterband_channel,
510+
geo_transform: GdalDatasetGeoTransform {
511+
origin_coordinate: geo_transform.origin_coordinate(),
512+
x_pixel_size: geo_transform.x_pixel_size(),
513+
y_pixel_size: geo_transform.y_pixel_size(),
514+
},
515+
width,
516+
height,
517+
file_not_found_handling: FileNotFoundHandling::Error,
518+
no_data_value: None,
519+
properties_mapping: None,
520+
gdal_open_options: None,
521+
gdal_config_options: gdal_config_options.clone(),
522+
allow_alphaband_as_mask: false,
523+
retry: Some(GdalRetryOptions { max_retries: 99 }), // TODO: make configurable?
524+
},
525+
});
526+
}
443527

444-
if (geo_transform.x_pixel_size().abs() - self.dataset.resolution.x).abs() > 1e-9
445-
|| (geo_transform.y_pixel_size().abs() - self.dataset.resolution.y).abs() > 1e-9
446-
{
447-
continue;
448-
}
528+
Ok(())
529+
}
449530

450-
let Some(asset_title) = asset.title.as_deref() else {
531+
fn rasterband_channel_for_dataset_band(
532+
asset: &stac::Asset,
533+
required_band_name: Option<&str>,
534+
) -> Option<usize> {
535+
if asset.bands.is_empty() {
536+
if required_band_name.is_some() {
451537
tracing::warn!(
452-
"Skipping asset with href {} due to missing title",
538+
"STAC asset with href {} does not include bands, but dataset configuration requires a band name. Skipping asset.",
453539
asset.href
454540
);
455-
continue;
456-
};
457-
458-
let grid_bounds = GridBoundingBox2D::new(
459-
GridIdx2D::new([0, 0]),
460-
GridIdx2D::new([(width as isize) - 1, (height as isize) - 1]),
461-
)
462-
.map_err(|_e| geoengine_operators::error::Error::InvalidDataProviderConfig)?;
463-
let spatial_partition = geo_transform.grid_to_spatial_bounds(&grid_bounds);
464-
465-
let file_path = gdal_file_path(&asset.href)
466-
.ok_or(geoengine_operators::error::Error::InvalidDataProviderConfig)?;
467-
468-
let gdal_config_options = self.gdal_config_options_for_file_path(&file_path);
469-
470-
for (dataset_band_idx, dataset_band) in self.dataset.bands.iter().enumerate() {
471-
if dataset_band.asset_title != asset_title {
472-
continue;
473-
}
474-
475-
let rasterband_channel = if asset.bands.is_empty() {
476-
if dataset_band.band_name.is_some() {
477-
tracing::warn!(
478-
"STAC asset with href {} does not include bands, but dataset configuration requires a band name. Skipping asset.",
479-
asset.href
480-
);
481-
continue;
482-
}
483-
484-
1
485-
} else {
486-
let Some(required_band_name) = dataset_band.band_name.as_deref() else {
487-
tracing::warn!(
488-
"STAC asset with href {} includes bands, but dataset configuration does not specify a band name. Skipping asset.",
489-
asset.href
490-
);
491-
continue;
492-
};
493-
494-
let Some(asset_band_idx) = asset.bands.iter().position(|asset_band| {
495-
asset_band.name.as_deref() == Some(required_band_name)
496-
}) else {
497-
tracing::debug!(
498-
"Skipping asset with href {} due to missing required band {}",
499-
asset.href,
500-
required_band_name
501-
);
502-
continue;
503-
};
504-
505-
asset_band_idx + 1
506-
};
507-
508-
files.push(TileFile {
509-
time,
510-
spatial_partition,
511-
band: dataset_band_idx as u32,
512-
z_index,
513-
params: GdalDatasetParameters {
514-
file_path: file_path.clone(),
515-
rasterband_channel,
516-
geo_transform: GdalDatasetGeoTransform {
517-
origin_coordinate: geo_transform.origin_coordinate(),
518-
x_pixel_size: geo_transform.x_pixel_size(),
519-
y_pixel_size: geo_transform.y_pixel_size(),
520-
},
521-
width,
522-
height,
523-
file_not_found_handling: FileNotFoundHandling::Error,
524-
no_data_value: None,
525-
properties_mapping: None,
526-
gdal_open_options: None,
527-
gdal_config_options: gdal_config_options.clone(),
528-
allow_alphaband_as_mask: false,
529-
retry: Some(GdalRetryOptions { max_retries: 99 }), // TODO: make configurable?
530-
},
531-
});
541+
return None;
532542
}
543+
544+
return Some(1);
533545
}
534546

535-
Ok(())
547+
let Some(required_band_name) = required_band_name else {
548+
tracing::warn!(
549+
"STAC asset with href {} includes bands, but dataset configuration does not specify a band name. Skipping asset.",
550+
asset.href
551+
);
552+
return None;
553+
};
554+
555+
let Some(asset_band_idx) = asset
556+
.bands
557+
.iter()
558+
.position(|asset_band| asset_band.name.as_deref() == Some(required_band_name))
559+
else {
560+
tracing::debug!(
561+
"Skipping asset with href {} due to missing required band {}",
562+
asset.href,
563+
required_band_name
564+
);
565+
return None;
566+
};
567+
568+
Some(asset_band_idx + 1)
536569
}
537570

538571
fn gdal_config_options_for_file_path(&self, file_path: &Path) -> Option<Vec<(String, String)>> {

0 commit comments

Comments
 (0)