def test_case_3(): root_cat = Catalog(id='test3', description='test case 3 catalog', title='test case 3 title') image_item = Item(id='imagery-item', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), properties={}) image_item.add_asset('ortho', Asset(href='some/geotiff.tiff', media_type=MediaType.GEOTIFF)) overviews = [LabelOverview('label', counts=[LabelCount('one', 1), LabelCount('two', 2)])] label_item = LabelItem(id='label-items', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), properties={}, label_description='ML Labels', label_type='vector', label_properties=['label'], label_classes=[LabelClasses(classes=['one', 'two'], name='label')], label_tasks=['classification'], label_methods=['manual'], label_overviews=overviews) label_item.add_source(image_item, assets=['ortho']) root_cat.add_item(image_item) root_cat.add_item(label_item) return root_cat
def test_full_copy_2(self): with TemporaryDirectory() as tmp_dir: cat = Catalog(id='test', description='test catalog') image_item = Item(id='Imagery', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), properties={}) for key in ['ortho', 'dsm']: image_item.add_asset( key, Asset(href='some/{}.tif'.format(key), media_type=MediaType.GEOTIFF)) label_item = LabelItem( id='Labels', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), properties={}, label_description='labels', label_type='vector', label_properties='label', label_classes=[LabelClasses(classes=['one', 'two'], name='label')], label_tasks=['classification']) label_item.add_source(image_item, assets=['ortho']) cat.add_items([image_item, label_item]) cat.normalize_hrefs(os.path.join(tmp_dir, 'catalog-full-copy-2-source')) cat.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) cat2 = cat.full_copy() cat2.normalize_hrefs(os.path.join(tmp_dir, 'catalog-full-copy-2-dest')) cat2.save(catalog_type=CatalogType.ABSOLUTE_PUBLISHED) self.check_catalog(cat, 'source') self.check_catalog(cat2, 'dest')
def item(href: str, read_href_modifier: Optional[ReadHrefModifier] = None) -> Item: """Creates a STAC Item from the asset at the provided href. The `read_href_modifer` argument can be used to modify the href for the rasterio read, e.g. if you need to sign a url. This function is intentionally minimal in its signature and capabilities. If you need to customize your Item, do so after creation. This function sets: - id - geometry - bbox - datetime (to the time of item creation): you'll probably want to change this - the proj extension - either the EPSG code or, if not available, the WKT2 - transform - shape - a single asset with key 'data' - asset href - asset roles to ['data'] In particular, the datetime and asset media type fields most likely need to be updated. """ id = os.path.splitext(os.path.basename(href))[0] if read_href_modifier: modified_href = read_href_modifier(href) else: modified_href = href with rasterio.open(modified_href) as dataset: crs = dataset.crs proj_bbox = dataset.bounds proj_transform = list(dataset.transform)[0:6] proj_shape = dataset.shape proj_geometry = shapely.geometry.mapping(shapely.geometry.box(*proj_bbox)) geometry = stactools.core.projection.reproject_geom(crs, 'EPSG:4326', proj_geometry, precision=6) bbox = list(shapely.geometry.shape(geometry).bounds) item = Item(id=id, geometry=geometry, bbox=bbox, datetime=datetime.datetime.now(), properties={}) projection = ProjectionExtension.ext(item, add_if_missing=True) epsg = crs.to_epsg() if epsg: projection.epsg = epsg else: projection.wkt2 = crs.to_wkt('WKT2') projection.transform = proj_transform projection.shape = proj_shape item.add_asset('data', Asset(href=href, roles=['data'])) return item
def test_map_items_multiple_2(self): catalog = Catalog(id='test-1', description='Test1') item1 = Item(id='item1', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), properties={}) item1.add_asset('ortho', Asset(href='/some/ortho.tif')) catalog.add_item(item1) kitten = Catalog(id='test-kitten', description='A cuter version of catalog') catalog.add_child(kitten) item2 = Item(id='item2', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), properties={}) item2.add_asset('ortho', Asset(href='/some/other/ortho.tif')) kitten.add_item(item2) def modify_item_title(item): item.title = 'Some new title' return item def create_label_item(item): # Assumes the GEOJSON labels are in the # same location as the image img_href = item.assets['ortho'].href label_href = '{}.geojson'.format(os.path.splitext(img_href)[0]) label_item = Item(id='Labels', geometry=item.geometry, bbox=item.bbox, datetime=datetime.utcnow(), properties={}) label_item.ext.enable(Extensions.LABEL) label_ext = label_item.ext.label label_ext.apply(label_description='labels', label_type='vector', label_properties=['label'], label_classes=[ LabelClasses.create(classes=['one', 'two'], name='label') ], label_tasks=['classification']) label_ext.add_source(item, assets=['ortho']) label_ext.add_geojson_labels(label_href) return [item, label_item] c = catalog.map_items(modify_item_title) c = c.map_items(create_label_item) new_catalog = c items = new_catalog.get_all_items() self.assertTrue(len(list(items)) == 4)
def test_case_3() -> Catalog: root_cat = Catalog(id="test3", description="test case 3 catalog", title="test case 3 title") image_item = Item( id="imagery-item", geometry=ARBITRARY_GEOM, bbox=ARBITRARY_BBOX, datetime=datetime.utcnow(), properties={}, ) image_item.add_asset( "ortho", Asset(href="some/geotiff.tiff", media_type=MediaType.GEOTIFF)) overviews = [ LabelOverview.create( "label", counts=[ LabelCount.create("one", 1), LabelCount.create("two", 2) ], ) ] label_item = Item( id="label-items", geometry=ARBITRARY_GEOM, bbox=ARBITRARY_BBOX, datetime=datetime.utcnow(), properties={}, ) LabelExtension.add_to(label_item) label_ext = LabelExtension.ext(label_item) label_ext.apply( label_description="ML Labels", label_type=LabelType.VECTOR, label_properties=["label"], label_classes=[ LabelClasses.create(classes=["one", "two"], name="label") ], label_tasks=["classification"], label_methods=["manual"], label_overviews=overviews, ) label_ext.add_source(image_item, assets=["ortho"]) root_cat.add_item(image_item) root_cat.add_item(label_item) return root_cat
def _add_cog_assets( item: pystac.Item, xml_metadata: XmlMetadata, vnir_cog_href: Optional[str], swir_cog_href: Optional[str], tir_cog_href: Optional[str], read_href_modifier: Optional[ReadHrefModifier] = None) -> None: pointing_angles = xml_metadata.pointing_angles sensors_to_hrefs = { VNIR_SENSOR: vnir_cog_href, SWIR_SENSOR: swir_cog_href, TIR_SENSOR: tir_cog_href } def title_for(sensor): return f'{sensor} Swath data' sensors_to_bands = get_sensors_to_bands() for sensor in ASTER_SENSORS: if sensors_to_hrefs[sensor] is None: logger.warning(f'Skipping {sensor} COG') continue cog_href = sensors_to_hrefs[sensor] sensor_asset = pystac.Asset(href=cog_href, media_type=pystac.MediaType.COG, roles=['data'], title=title_for(sensor)) # Set bands item.ext.eo.set_bands(sensors_to_bands[sensor], sensor_asset) # Set view off_nadir if sensor in pointing_angles: item.ext.view.off_nadir = abs(pointing_angles[sensor]) # Open COG headers to get proj info cog_read_href = cog_href if read_href_modifier: cog_read_href = read_href_modifier(cog_read_href) with rio.open(cog_read_href) as ds: image_shape = list(ds.shape) proj_bbox = list(ds.bounds) transform = list(ds.transform) item.ext.projection.set_shape(image_shape, sensor_asset) item.ext.projection.set_bbox(proj_bbox, sensor_asset) item.ext.projection.set_transform(transform, sensor_asset) item.add_asset(sensor, sensor_asset)
def merge_items(source_item: pystac.Item, target_item: pystac.Item, move_assets: bool = False, ignore_conflicts: bool = False) -> None: """Merges the assets from source_item into target_item. The geometry and bounding box of the items will also be merged. Args: source_item (pystac.Item): The Item that will be merged into target_item. This item is not mutated in this operation. target_item (pystac.Item): The target item that will be merged into. This item will be mutated in this operation. move_assets (bool): If true, move the asset files alongside the target item. ignore_conflicts (bool): If True, assets with the same keys will not be merged, and asset files that would be moved to overwrite an existing file will not be moved. If False, either of these situations will throw an error. """ target_item_href = target_item.get_self_href() if target_item_href is None: raise ValueError( f"Target Item {target_item.id} must have an HREF for merge") for key, asset in source_item.assets.items(): if key in target_item.assets: if ignore_conflicts: continue else: raise Exception( 'Target item {} already has asset with key {}, ' 'cannot merge asset in from {}'.format( target_item, key, source_item)) else: asset_href = asset.get_absolute_href() if asset_href is None: raise ValueError( f"Asset {asset.title} must have an HREF for merge") if move_assets: new_asset_href = move_asset_file_to_item( target_item, asset_href, ignore_conflicts=ignore_conflicts) else: if not is_absolute_href(asset.href): asset_href = make_relative_href(asset_href, target_item_href) new_asset_href = asset_href new_asset = asset.clone() new_asset.href = new_asset_href target_item.add_asset(key, new_asset) source_geom = shape(source_item.geometry) target_geom = shape(target_item.geometry) union_geom = source_geom.union(target_geom).buffer(0) target_item.geometry = mapping(union_geom) target_item.bbox = list(union_geom.bounds)
def to_stac(item, in_tiff): item_out = Item(id=item.id, geometry=item.geometry, bbox=item.bbox, datetime=item.datetime, properties=item.properties) item_out.common_metadata.set_gsd(20) item_out.common_metadata.set_constellation('sentinel-1') item_out.common_metadata.set_mission('sentinel-1') item_out.common_metadata.set_platform('sentinel-1{}'.format( item.id[2:3].lower())) eo_item = extensions.eo.EOItemExt(item_out) band = 'sigma_db_vv ' item_out.add_asset(key=band.lower(), asset=Asset(href='{}_{}.tif'.format( item.id, band.upper()), media_type=MediaType.GEOTIFF, properties={ 'sar:polarizations': band.lower().split('_')[1].upper() })) asset = eo_item.item.get_assets()[band.lower()] description = '{} for polarization channel {}{}'.format( band.lower().split('_')[0].title(), band.lower().split('_')[1].upper(), ' in {}'.format(band.lower().split('_')[2]) if len(band.lower().split('_')) == 3 else '') stac_band = extensions.eo.Band.create(name=band.lower(), common_name=band.lower(), description=description) #bands.append(stac_band) eo_item.set_bands([stac_band], asset=asset) #eo_item.set_bands(bands) #eo_item.apply(bands) return item_out
def add_asset(self, item: pystac.Item, mtl_metadata: MtlMetadata, base_href: str) -> None: asset = pystac.Asset(href=self.get_href(base_href), media_type=self.media_type) if self.title: asset.title = self.title if self.description: asset.description = self.description # common_metadata if self.gsd is not None: item.common_metadata.set_gsd(self.gsd, asset) else: if self.is_sr or self.is_qa: sr_grd = mtl_metadata.sr_gsd if item.common_metadata.gsd != sr_grd: item.common_metadata.set_gsd(sr_grd, asset) if self.is_thermal: thermal_grd = mtl_metadata.thermal_gsd if item.common_metadata.gsd != thermal_grd: item.common_metadata.set_gsd(thermal_grd, asset) # eo if self.bands: asset.properties["eo:bands"] = [b.to_dict() for b in self.bands] # projection if self.is_sr or self.is_qa: item.ext.projection.set_shape(mtl_metadata.sr_shape, asset) item.ext.projection.set_transform(mtl_metadata.sr_transform, asset) if self.is_thermal: item.ext.projection.set_shape(mtl_metadata.thermal_shape, asset) item.ext.projection.set_transform(mtl_metadata.thermal_transform, asset) item.add_asset(self.key, asset)
def collection_add_sentinel_chips(collection, uri_list, sentinel_version, debug=False): """ Add sentinel images to a collection """ if debug: uri_list = list(uri_list)[:10] for uri in uri_list: if not uri.endswith(".tif"): continue item_id = os.path.basename(uri).split(".")[0] country, event_id, *_ = item_id.split("_") params = {} params["id"] = item_id params["collection"] = collection params["properties"] = { "country": country, "event_id": event_id, } params["bbox"] = get_chip_bbox(uri, country, event_id) params["geometry"] = box(*params["bbox"]).__geo_interface__ params["datetime"] = image_date_for_country(sentinel_version, country) # Create Tiff Item item = Item(**params) asset = Asset(href=uri, title="GeoTiff", media_type="image/tiff; application=geotiff") item.add_asset(key="image", asset=asset) SENTINEL_CHIP_ITEM_CACHE[sentinel_version.upper()][chip_cache_id( country, event_id)] = item collection.add_item(item) print("Collection {}: Added STAC Item {}".format( collection.id, item.id))
def main(ctx, input_reference, s_expression, cbn): dump(ctx) item = get_item(os.path.join(input_reference, "catalog.json")) logging.info(f"Processing {item.id}") try: os.mkdir(item.id) except FileExistsError: pass cbn = cbn.replace(' ', '-') result = os.path.join(item.id, f"{cbn}.tif") logging.info(f"Apply {s_expression} to {item.id}") apply_s_expression(item=item, s_expression=s_expression, out_tif=result) logging.info("STAC") item_out = Item( id=item.id, geometry=item.geometry, bbox=item.bbox, datetime=item.datetime, properties=item.properties, stac_extensions=item.stac_extensions, ) eo_item = extensions.eo.EOItemExt(item_out) asset_properties = dict() asset_properties["s-expression"] = s_expression asset = Asset( href=os.path.basename(result), media_type=MediaType.COG, roles=["data"], properties=asset_properties, ) eo_bands = [ extensions.eo.Band.create( name=cbn.lower(), common_name=cbn.lower(), description=f"{cbn.lower()} ({s_expression})", ) ] eo_item.set_bands(eo_bands, asset=asset) item_out.add_asset(key=cbn.lower(), asset=asset) logging.info("STAC") cat = Catalog(id="catalog", description="s-expression") cat.add_items([item_out]) cat.normalize_and_save(root_href="./", catalog_type=CatalogType.SELF_CONTAINED) logging.info("Done!")
def burned(pre_item, post_item, ndvi_threshold, ndwi_threshold): items = {} items["pre-event"] = pre_item items["post-event"] = post_item veg_indices = {} scl = {} for key, item in items.items(): stack( item=item, bands=["B04", "B08", "B11", "SCL"], target_res=10, output_name=f"{key}.tif", ) ndvi, ndwi = indices(f"{key}.tif") veg_indices[key] = {"ndvi": ndvi, "ndwi": ndwi} scl[key] = get_scl(f"{key}.tif") if key in ["pre-event"]: geotransform, georef = get_geo(f"{key}.tif") os.remove(f"{key}.tif") conditions = ( (veg_indices["post-event"]["ndwi"] - veg_indices["pre-event"]["ndwi"]) > float(ndwi_threshold) ) & ( (veg_indices["post-event"]["ndvi"] - veg_indices["pre-event"]["ndvi"]) > float(ndvi_threshold) ) & ( scl["pre-event"] == 4 ) | ( scl["post-event"] == 4 ) height = scl["pre-event"].shape[1] width = scl["pre-event"].shape[0] burned = np.zeros((height, width), dtype=np.uint8) burned[conditions] = 1 # free memory for key, _ in items.items(): for indice, _ in veg_indices.items(): veg_indices[key][indice] = None burned[ np.where( (scl["pre-event"] == 0) | (scl["post-event"] == 0) | (scl["pre-event"] == 1) | (scl["post-event"] == 1) | (scl["pre-event"] == 5) | (scl["post-event"] == 5) | (scl["pre-event"] == 6) | (scl["post-event"] == 6) | (scl["pre-event"] == 7) | (scl["post-event"] == 7) | (scl["pre-event"] == 8) | (scl["post-event"] == 8) | (scl["pre-event"] == 9) | (scl["post-event"] == 9) ) ] = 2 output_name = "BURNED_AREA_{}".format( "_".join( [s2_item.datetime.strftime("%Y%m%d") for key, s2_item in items.items()] ) ) item_id = "burned-area" write_tif( burned, os.path.join(item_id, f"{output_name}.tif"), width, height, geotransform, georef, ) item = Item( id=item_id, geometry=items["pre-event"].geometry, bbox=items["pre-event"].bbox, datetime=items["pre-event"].datetime, properties={}, ) item.add_asset( key="data", asset=Asset( href=os.path.join(".", f"{output_name}.tif"), media_type=MediaType.COG, title="Burned area analysis from Sentinel-2", ), ) return item
image_item = Item( "flood_" + flood_id + "_chip_" + group_id, geometry=mapping( Polygon([ [bounds.left, bounds.bottom], [bounds.right, bounds.bottom], [bounds.right, bounds.top], [bounds.left, bounds.top], [bounds.left, bounds.bottom], ])), bbox=[bounds.bottom, bounds.left, bounds.top, bounds.right], datetime=start_time, properties={}, ) for asset in assets: image_item.add_asset( asset.href.split("/")[-1].split(".")[0], asset) stac_items.append(image_item) aggregate_spatial_extent = SpatialExtent([[ aggregate_bounds.bottom, aggregate_bounds.left, aggregate_bounds.top, aggregate_bounds.right, ]]) aggregate_extent = Extent(aggregate_spatial_extent, temporal_extent) collection = Collection( flood_id, "Imagery coextensive with GLOFIMR flood {}".format(flood_id), extent=aggregate_extent, ) for stac_item in stac_items:
def main(ndvi_threshold, ndwi_threshold, pre_event, post_event): os.environ['PREFIX']='/opt/anaconda/envs/env_burned_area' os.environ['PROJ_LIB'] = os.path.join(os.environ['PREFIX'], 'share/proj') os.environ['GDAL_DATA'] = os.path.join(os.environ['PREFIX'], 'share/gdal') s2_item_pre = S2_stac_item(pre_event['value']) s2_item_post = S2_stac_item(post_event['value']) s2_items = dict() s2_items['pre-event'] = S2_stac_item(pre_event['value']) s2_items['post-event'] = S2_stac_item(post_event['value']) dates = [] bboxes = [] for index, item in enumerate([s2_item_pre.item, s2_item_post.item]): dates.append(item.datetime) bboxes.append(shape(item.geometry).bounds) logging.info('Stacking bands for input {}'.format(item.id)) vrt_bands = [] for band in ['B04', 'B08', 'B11', 'SCL']: vrt_bands.append('/vsicurl/{}'.format(item.assets[band].get_absolute_href())) vrt = '{}.vrt'.format('pre_event' if index == 0 else 'post_event') tif = '{}.tif'.format('pre_event' if index == 0 else 'post_event') logging.info('Build vrt for {}'.format(item.id)) ds = gdal.BuildVRT(vrt, vrt_bands, srcNodata=0, xRes=10, yRes=10, separate=True) ds.FlushCache() logging.info('Translate {}'.format(item.id)) gdal.Translate(tif, vrt, outputType=gdal.GDT_UInt16) os.remove(vrt) ds = gdal.Open('pre_event.tif') pre_b04 = ds.GetRasterBand(1).ReadAsArray() pre_b08 = ds.GetRasterBand(2).ReadAsArray() pre_b11 = ds.GetRasterBand(3).ReadAsArray() pre_scl = ds.GetRasterBand(4).ReadAsArray() ds = None os.remove('pre_event.tif') ds = gdal.Open('post_event.tif') post_b04 = ds.GetRasterBand(1).ReadAsArray() post_b08 = ds.GetRasterBand(2).ReadAsArray() post_b11 = ds.GetRasterBand(3).ReadAsArray() post_scl = ds.GetRasterBand(4).ReadAsArray() width = ds.RasterXSize height = ds.RasterYSize input_geotransform = ds.GetGeoTransform() input_georef = ds.GetProjectionRef() ds = None os.remove('post_event.tif') gain = 10000 pre_ndwi2 = (pre_b08 / gain - pre_b11 / gain) / (pre_b08 / gain + pre_b11 / gain) post_ndwi2 = (post_b08 / gain - post_b11 / gain) / (post_b08 / gain + post_b11 / gain) pre_b11 = None post_b11 = None pre_ndvi = (pre_b08 / gain - pre_b04 / gain) / (pre_b08 / gain + pre_b04 / gain) post_ndvi = (post_b08 / gain - post_b04 / gain) / (post_b08 / gain + post_b04 / gain) pre_b04 = None post_b04 = None pre_b08 = None post_b08 = None conditions = (((post_ndwi2 - pre_ndwi2) > float(ndwi_threshold['value'])) & ((post_ndvi - pre_ndvi) > float(ndvi_threshold['value'])) & (pre_scl == 4) | (post_scl == 4)) burned = np.zeros((height, width), dtype=np.uint8) burned[conditions] = 1 pre_ndwi2 = None post_ndwi2 = None pre_ndvi = None post_ndvi = None burned[np.where((pre_scl == 0) | (post_scl == 0) | (pre_scl == 1) | (post_scl == 1) | (pre_scl == 5) | (post_scl == 5) | (pre_scl == 6) | (post_scl == 6) | (pre_scl == 7) | (post_scl == 7) | (pre_scl == 8) | (post_scl == 8) | (pre_scl == 9) | (post_scl == 9))] = 2 logging.info('Write output product') output_name = 'S2_BURNED_AREA_{}'.format('_'.join([d.strftime("%Y%m%d") for d in dates])) write_tif(burned, '{}.tif'.format(output_name), width, height, input_geotransform, input_georef) logging.info('Output catalog') catalog = Catalog(id='catalog', description='Results') catalog.clear_items() catalog.clear_children() result_titles = dict() result_titles[output_name] = {'title': 'Burned area analysis from Sentinel-2', 'media_type': MediaType.COG} items = [] for key, value in result_titles.items(): result_item = Item(id=key, geometry=s2_items['pre-event'].item.geometry, bbox=s2_items['pre-event'].item.bbox, datetime=s2_items['pre-event'].item.datetime, properties={}) result_item.add_asset(key='data', asset=Asset(href='./{}.tif'.format(key), media_type=value['media_type'], title=value['title'])) items.append(result_item) #collection.add_items(items) catalog.add_items(items) catalog.describe() catalog.normalize_and_save(root_href='./', catalog_type=CatalogType.SELF_CONTAINED) shutil.move('{}.tif'.format(output_name), os.path.join('./', output_name, '{}.tif'.format(output_name)))
running_extent = ( min(running_extent[0], bbox[0]), max(running_extent[1], bbox[1]), min(running_extent[2], bbox[2]), max(running_extent[3], bbox[3]), ) bbox_list = [bbox[0], bbox[1], bbox[2], bbox[3]] huc_item = Item(fid, geom, bbox_list, version_dt, {}) hand_asset = Asset( href="{}/{}/{}hand.tif".format(args.root_uri, fid, fid), description="HAND raster, buffer removed, final result", media_type="image/tiff; application=geotiff", ) huc_item.add_asset(key="hand", asset=hand_asset) wbd_asset = Asset( href="{}/{}/{}-wbd.geojson".format(args.root_uri, fid, fid), description="HUC unit boundary, extracted from USGS wbd", media_type="application/geo+json", ) huc_item.add_asset(key="wbd", asset=wbd_asset) flows_asset = Asset( href="{}/{}/{}-flows.geojson".format(args.root_uri, fid, fid), description="Flowline geometry, extracted from NHDPlus V21", media_type="application/geo+json", ) huc_item.add_asset(key="flows", asset=flows_asset)
item_time = datetime.combine(date(year, month, 1), time.min) item = Item( item_id, geometry=mapping( Polygon([ [-92.72807246278022, 29.038948834106055], [-88.02592402528022, 29.038948834106055], [-88.02592402528022, 42.55475543734189], [-92.72807246278022, 42.55475543734189], [-92.72807246278022, 29.038948834106055], ])), bbox=[ 29.038948834106055, -92.72807246278022, 42.55475543734189, -88.02592402528022, ], datetime=item_time, properties={}, ) asset = Asset("s3://{}/{}".format(obj_summary.bucket_name, obj_summary.key)) item.add_asset("labels", asset) collection.add_item(item) # Save Complete Catalog root_path = "./data/catalog" collection.normalize_and_save(root_path, catalog_type=CatalogType.SELF_CONTAINED) print("Saved STAC Catalog {} to {}...".format(collection.id, root_path))
def to_pystac_item( dataset: DatasetDoc, stac_item_destination_url: str, dataset_location: Optional[str] = None, odc_dataset_metadata_url: Optional[str] = None, explorer_base_url: Optional[str] = None, collection_url: Optional[str] = None, ) -> pystac.Item: """ Convert the given ODC Dataset into a Stac Item document. Note: You may want to call `validate_item(doc)` on the outputs to find any incomplete properties. :param collection_url: URL to the Stac Collection. Either this or an explorer_base_url should be specified for Stac compliance. :param stac_item_destination_url: Public 'self' URL where the stac document will be findable. :param dataset_location: Use this location instead of picking from dataset.locations (for calculating relative band paths) :param odc_dataset_metadata_url: Public URL for the original ODC dataset yaml document :param explorer_base_url: An Explorer instance that contains this dataset. Will allow links to things such as the product definition. """ if dataset.geometry is not None: geom = Geometry(dataset.geometry, CRS(dataset.crs)) wgs84_geometry = geom.to_crs(CRS("epsg:4326"), math.inf) geometry = wgs84_geometry.json bbox = wgs84_geometry.boundingbox else: geometry = None bbox = None properties = eo3_to_stac_properties(dataset, title=dataset.label) properties.update(_lineage_fields(dataset.lineage)) dt = properties["datetime"] del properties["datetime"] # TODO: choose remote if there's multiple locations? # Without a dataset location, all paths will be relative. dataset_location = dataset_location or (dataset.locations[0] if dataset.locations else None) item = Item( id=str(dataset.id), datetime=dt, properties=properties, geometry=geometry, bbox=bbox, collection=dataset.product.name, ) # Add links if stac_item_destination_url: item.links.append( Link( rel="self", media_type=MediaType.JSON, target=stac_item_destination_url, )) if odc_dataset_metadata_url: item.links.append( Link( title="ODC Dataset YAML", rel="odc_yaml", media_type="text/yaml", target=odc_dataset_metadata_url, )) for link in _odc_links(explorer_base_url, dataset, collection_url): item.links.append(link) EOExtension.ext(item, add_if_missing=True) if dataset.geometry: proj = ProjectionExtension.ext(item, add_if_missing=True) epsg, wkt = _get_projection(dataset) if epsg is not None: proj.apply(epsg=epsg, **_proj_fields(dataset.grids)) elif wkt is not None: proj.apply(wkt2=wkt, **_proj_fields(dataset.grids)) else: raise STACError( "Projection extension requires either epsg or wkt for crs.") # To pass validation, only add 'view' extension when we're using it somewhere. if any(k.startswith("view:") for k in properties.keys()): ViewExtension.ext(item, add_if_missing=True) # Add assets that are data for name, measurement in dataset.measurements.items(): if not dataset_location and not measurement.path: # No URL to link to. URL is mandatory for Stac validation. continue asset = Asset( href=_uri_resolve(dataset_location, measurement.path), media_type=_media_type(Path(measurement.path)), title=name, roles=["data"], ) eo = EOExtension.ext(asset) # TODO: pull out more information about the band band = Band.create(name) eo.apply(bands=[band]) if dataset.grids: proj_fields = _proj_fields(dataset.grids, measurement.grid) if proj_fields is not None: proj = ProjectionExtension.ext(asset) # Not sure how this handles None for an EPSG code proj.apply( shape=proj_fields["shape"], transform=proj_fields["transform"], epsg=epsg, ) item.add_asset(name, asset=asset) # Add assets that are accessories for name, measurement in dataset.accessories.items(): if not dataset_location and not measurement.path: # No URL to link to. URL is mandatory for Stac validation. continue asset = Asset( href=_uri_resolve(dataset_location, measurement.path), media_type=_media_type(Path(measurement.path)), title=_asset_title_fields(name), roles=_asset_roles_fields(name), ) item.add_asset(name, asset=asset) return item
def label_collection_add_items( collection, root_catalog, uri_list, links_func, label_description, label_type, label_classes=None, label_tasks=None, debug=False, ): """ Add uri_list tif uris to collection as LabelItems root_catalog is the top level node in the STAC Catalog where the chips labeled by these tifs can be found. Required to correctly setup Links to the source chips. links_func is a method with the following signature: def links_func(root_catalog: Catalog, label_item: LabelItem, country: str, event_id: int): [Link] This method should construct a list of links that map the label_item to the STAC objects in the root_catalog that label_item is derived from. Assumes for now that the asset referenced uses the key "labels" The label_ arguments will be passed down to each LabelItem in the collection """ if debug: uri_list = list(uri_list)[:10] for uri in uri_list: if not uri.endswith(".tif"): continue item_id = os.path.basename(uri).split(".")[0] country, event_id, *_ = item_id.split("_") params = {} params["id"] = item_id params["collection"] = collection params["datetime"] = image_date_for_country("s1", country) params["stac_extensions"] = [Extensions.LABEL] params["properties"] = { "country": country, "event_id": event_id, } params["bbox"] = get_chip_bbox(uri, country, event_id) params["geometry"] = box(*params["bbox"]).__geo_interface__ label_ext_params = {} if isinstance(label_classes, list): label_ext_params["label_classes"] = label_classes else: label_ext_params["label_classes"] = [] label_ext_params["label_description"] = label_description if label_tasks is not None: label_ext_params["label_tasks"] = label_tasks label_ext_params["label_type"] = label_type item = Item(**params) item.ext.label.apply(**label_ext_params) # Add Asset asset = Asset(href=uri, title="GeoTiff", media_type="image/tiff; application=geotiff") item.add_asset(key="labels", asset=asset) item.links = links_func(root_catalog, item, country, event_id) collection.add_item(item) print("Collection {}: Added STAC Item {}".format( collection.id, item.id))
def scombi(channel_inputs, bands, s_expressions, resolution='highest', aoi=None, color=None, profile=None, lut=None, epsg=None): target_dir = 'combi' if not os.path.exists(target_dir): os.mkdir(target_dir) items = [] assets_href = [] rescaled = [] for index, input_path in enumerate(channel_inputs): #for index, input_path in enumerate([red_channel_input, green_channel_input, blue_channel_input]): if input_path is None: items.append(None) assets_href.append(None) continue item = get_item(input_path) logging.info(item) items.append(item) assets_href.append(get_band_asset_href(item, bands[index])) # define AOI, if none is supplied, get the minimum bbox if aoi is None: aoi = get_mbb([shape(item.geometry) for item in items]).wkt min_lon, min_lat, max_lon, max_lat = loads(aoi).bounds # analyze get an EPSG code if it hasn't been supplied # check if warp is needed epsg, epsg_codes = get_epsg(epsg, assets_href) # rescale and get the original assets (these are part of the output) logging.info('Rescaling and COG for input assets') rescaled = [] # get the data for index, asset_href in enumerate(assets_href): if asset_href is None: rescaled.append(None) continue logging.info('Getting band {} from {}'.format(bands[index], asset_href)) output_name = '{}/{}_{}.tif'.format(target_dir, index+1, bands[index]) if epsg_codes[index] == epsg: ds = gdal.Translate(output_name, asset_href, outputType=gdal.GDT_Float32, projWin=[min_lon, max_lat, max_lon, min_lat], projWinSRS='EPSG:4326') else: logging.info('Warp') ds = gdal.Warp(output_name, asset_href, outputType=gdal.GDT_Float32, outputBounds=[min_lon, min_lat, max_lon, max_lat], outputBoundsSRS='EPSG:4326', dstSRS=epsg) ds = None del(ds) #rescaled.append(ds) rescaled.append(output_name) # build a VRT with the rescaled assets with the selected resolution mode logging.info('Build VRT') vrt = 'temp.vrt' ds = gdal.BuildVRT(vrt, [ds for ds in rescaled if ds], resolution=resolution, separate=True) ds.FlushCache() output_cell_size = ds.GetGeoTransform()[1] logging.info(str(output_cell_size)) logging.info('Pimp me') pimp.me(vrt, f'{target_dir}/combi.tif', bands, s_expressions, color, lut) ds = None del(ds) # to STAC logging.info('STAC') cat = Catalog(id='scombidooo', description="Combined RGB composite") # TODO fix datetime item = Item(id='combi', geometry=mapping(loads(aoi)), bbox=list(loads(aoi).bounds), datetime=items[0].datetime, properties={'bands': bands, 's_expressions': s_expressions, 'input_items': [_item.id for _item in items], 'color': 'N/A' if not color else color, 'profile': 'N/A' if not profile else profile}) item.common_metadata.set_gsd(output_cell_size) eo_item = extensions.eo.EOItemExt(item) for index, asset_href in enumerate(assets_href): if asset_href is None: continue _asset = get_band_asset(items[index], bands[index]) _asset.href = './{}_{}.tif'.format(index+1, bands[index]) item.add_asset('{}_{}'.format(index+1, bands[index]), _asset) # add the result.tif Asset item.add_asset(key='rgb', asset=Asset(href='./combi.tif', media_type=MediaType.COG)) cat.add_items([item]) cat.normalize_and_save(root_href='./', catalog_type=CatalogType.SELF_CONTAINED) logging.info('Done!') return(cat.get_self_href())
) text_asset = Asset( href="{}-usfimr.wkt".format(fid), description="well known text representation", media_type="application/wkt", ) json_asset = Asset( href="{}-usfimr.geojson".format(fid), description="geojson representation", media_type="application/geo+json", ) serializable_convex_hull = mapping(shapely_geom.convex_hull) item = Item(fid, serializable_convex_hull, bbox_list, start_dt, deepcopy(props)) text_asset.set_owner(item) item.add_asset(key="wkt", asset=text_asset) binary_asset.set_owner(item) item.add_asset(key="wkb", asset=binary_asset) json_asset.set_owner(item) item.add_asset(key="geojson", asset=json_asset) items.append(item) os.makedirs("{}/{}".format(root_path, fid), exist_ok=True) with open("{}/{}/{}-usfimr.wkt".format(root_path, fid, fid), "w") as wkt_file: wkt_file.write(shapely_geom.wkt) with open("{}/{}/{}-usfimr.wkb".format(root_path, fid, fid), "wb") as wkb_file: wkb_file.write(shapely_geom.wkb)