def _make_band_dict(prod_cfg, pixel_dataset, band_list): band_dict = {} for band in band_list: try: band_lbl = prod_cfg.band_idx.band_label(band) ret_val = band_val = pixel_dataset[band].item() if band_val == pixel_dataset[band].nodata or numpy.isnan(band_val): band_dict[band_lbl] = "n/a" else: if 'flags_definition' in pixel_dataset[band].attrs: flag_def = pixel_dataset[band].attrs['flags_definition'] # HACK: Work around bands with floating point values try: flag_dict = mask_to_dict(flag_def, band_val) except TypeError as te: logging.warning('Working around for float bands') flag_dict = mask_to_dict(flag_def, int(band_val)) try: ret_val = [ flag_def[flag]['description'] for flag, val in flag_dict.items() if val ] except KeyError: # Weirdly formatted flag definition. Hacky workaround for USGS data in DEAfrica demo. ret_val = [ val for flag, val in flag_dict.items() if val ] band_dict[band_lbl] = ret_val except ConfigException: pass return band_dict
def test_simple_mask_to_dict(): simple_var = SimpleVariableWithFlagsDef() bits_def = simple_var.flags_definition contiguous_true = mask_to_dict(bits_def, 256) assert contiguous_true['contiguous'] test_dict = mask_to_dict(bits_def, 768) assert test_dict['contiguous'] assert test_dict['land_sea'] == 'land' test_dict = mask_to_dict(bits_def, 16383) assert test_dict['ga_good_pixel']
def _make_band_dict(prod_cfg, pixel_dataset, band_list): band_dict = {} for band in band_list: try: band_lbl = prod_cfg.band_idx.band_label(band) ret_val = band_val = pixel_dataset[band].item() if band_val == pixel_dataset[band].nodata or numpy.isnan(band_val): band_dict[band_lbl] = "n/a" else: if 'flags_definition' in pixel_dataset[band].attrs: flag_def = pixel_dataset[band].attrs['flags_definition'] flag_dict = mask_to_dict(flag_def, band_val) try: ret_val = [ flag_def[flag]['description'] for flag, val in flag_dict.items() if val ] except KeyError: # Weirdly formatted flag definition. Hacky workaround for USGS data in DEAfrica demo. ret_val = [ val for flag, val in flag_dict.items() if val ] band_dict[band_lbl] = ret_val except ProductLayerException: pass return band_dict
def test_simple_mask_to_dict(bits_def): # All 0. Contiguous should be False, Saturated bits should be true test_dict = mask_to_dict(bits_def, 0) assert not test_dict['contiguous'] assert test_dict['blue_saturated'] # Only contiguous (bit 8) set test_dict = mask_to_dict(bits_def, 256) assert test_dict['contiguous'] assert test_dict['blue_saturated'] assert test_dict['land_sea'] == 'sea' # Contiguous and land_sea bits set to 1. (bits 7 and 8) test_dict = mask_to_dict(bits_def, 768) assert test_dict['contiguous'] assert test_dict['land_sea'] == 'land' assert test_dict['blue_saturated'] # All GA PQ bits set to 1 test_dict = mask_to_dict(bits_def, 16383) assert test_dict['ga_good_pixel'] assert test_dict['contiguous'] assert test_dict['land_sea'] == 'land' assert not test_dict['blue_saturated']
def _make_band_dict(prod_cfg, pixel_dataset, band_list): band_dict = {} for band in band_list: try: band_lbl = prod_cfg.band_idx.band_label(band) ret_val = band_val = pixel_dataset[band].item() if band_val == pixel_dataset[band].nodata or numpy.isnan(band_val): band_dict[band_lbl] = "n/a" else: if 'flags_definition' in pixel_dataset[band].attrs: flag_def = pixel_dataset[band].attrs['flags_definition'] flag_dict = mask_to_dict(flag_def, band_val) ret_val = [ flag_def[flag]['description'] for flag, val in flag_dict.items() if val ] band_dict[band_lbl] = ret_val except ProductLayerException: pass return band_dict
def feature_info(args): # pylint: disable=too-many-nested-blocks, too-many-branches, too-many-statements, too-many-locals # Parse GET parameters params = GetFeatureInfoParameters(args) # Prepare to extract feature info stacker = DataStacker(params.product, params.geobox, params.time) feature_json = {} # --- Begin code section requiring datacube. service_cfg = get_service_cfg() dc = get_cube() try: geo_point = img_coords_to_geopoint(params.geobox, params.i, params.j) datasets = stacker.datasets(dc.index, all_time=True, point=geo_point) pq_datasets = stacker.datasets(dc.index, mask=True, all_time=False, point=geo_point) h_coord = service_cfg.published_CRSs[params.crsid]["horizontal_coord"] v_coord = service_cfg.published_CRSs[params.crsid]["vertical_coord"] isel_kwargs = {h_coord: [params.i], v_coord: [params.j]} if not datasets: pass else: available_dates = set() drill = {} for d in datasets: idx_date = (d.center_time + timedelta(hours=params.product.time_zone)).date() available_dates.add(idx_date) pixel_ds = None if idx_date == params.time and "lon" not in feature_json: data = stacker.data([d], skip_corrections=True) # Use i,j image coordinates to extract data pixel from dataset, and # convert to lat/long geographic coordinates if service_cfg.published_CRSs[params.crsid]["geographic"]: # Geographic coordinate systems (e.g. EPSG:4326/WGS-84) are already in lat/long feature_json["lat"] = data.latitude[params.j].item() feature_json["lon"] = data.longitude[params.i].item() pixel_ds = data.isel(**isel_kwargs) else: # Non-geographic coordinate systems need to be projected onto a geographic # coordinate system. Why not use EPSG:4326? # Extract coordinates in CRS data_x = getattr(data, h_coord) data_y = getattr(data, v_coord) x = data_x[params.i].item() y = data_y[params.j].item() pt = geometry.point(x, y, params.crs) # Project to EPSG:4326 crs_geo = geometry.CRS("EPSG:4326") ptg = pt.to_crs(crs_geo) # Capture lat/long coordinates feature_json["lon"], feature_json["lat"] = ptg.coords[ 0] # Extract data pixel pixel_ds = data.isel(**isel_kwargs) # Get accurate timestamp from dataset feature_json["time"] = d.center_time.strftime( "%Y-%m-%d %H:%M:%S UTC") feature_json["bands"] = {} # Collect raw band values for pixel for band in stacker.needed_bands(): ret_val = band_val = pixel_ds[band].item() if band_val == pixel_ds[band].nodata: feature_json["bands"][band] = "n/a" else: if hasattr(pixel_ds[band], 'flags_definition'): flag_def = pixel_ds[band].flags_definition flag_dict = mask_to_dict(flag_def, band_val) ret_val = [ flag_def[k]['description'] for k in filter(flag_dict.get, flag_dict) ] feature_json["bands"][band] = ret_val for k, v in filter( lambda kv: hasattr(kv[1], 'index_function'), params.product.style_index.items()): if v.index_function is None: continue vals_nodata = [ pixel_ds[b] == pixel_ds[b].nodata for b in v.needed_bands ] if any(vals_nodata): continue value = v.index_function(pixel_ds).item() try: feature_json["band_derived"][k] = value except KeyError: feature_json["band_derived"] = {} feature_json["band_derived"][k] = value if params.product.band_drill: if pixel_ds is None: data = stacker.data([d], skip_corrections=True) pixel_ds = data.isel(**isel_kwargs) drill_section = {} for band in params.product.band_drill: band_val = pixel_ds[band].item() if band_val == pixel_ds[band].nodata: drill_section[band] = "n/a" else: drill_section[band] = pixel_ds[band].item() drill[idx_date.strftime("%Y-%m-%d")] = drill_section if drill: feature_json["time_drill"] = drill feature_json["datasets_read"] = len(datasets) my_flags = 0 pqdi = -1 for pqd in pq_datasets: pqdi += 1 idx_date = (pqd.center_time + timedelta(hours=params.product.time_zone)).date() if idx_date == params.time: pq_data = stacker.data([pqd], mask=True) pq_pixel_ds = pq_data.isel(**isel_kwargs) # PQ flags m = params.product.pq_product.measurements[ params.product.pq_band] flags = pq_pixel_ds[params.product.pq_band].item() if not flags & ~params.product.info_mask: my_flags = my_flags | flags else: continue feature_json["flags"] = {} for mk, mv in m["flags_definition"].items(): if mk in params.product.ignore_flags_info: continue bits = mv["bits"] values = mv["values"] if not isinstance(bits, int): continue flag = 1 << bits if my_flags & flag: val = values['1'] else: val = values['0'] feature_json["flags"][mk] = val lads = list(available_dates) lads.sort() feature_json["data_available_for_dates"] = [ d.strftime("%Y-%m-%d") for d in lads ] feature_json["data_links"] = sorted(get_s3_browser_uris(datasets)) release_cube(dc) except Exception as e: release_cube(dc) raise e # --- End code section requiring datacube. result = { "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": feature_json }] } return json.dumps(result), 200, resp_headers( {"Content-Type": "application/json"})