def validate_min_lt_max(min, max): try: min = float(min) max = float(max) except ValueError: raise error_handler.InvalidUsage( "Specified min and max values must be numbers.") if not min < max: raise error_handler.InvalidUsage( "Specified min must be less than max.")
def get_msfd_data(): """ Get Crucial data. Either groundwater_declining_trend, or evaporation_deficit dataset must be provided. See datasets_visualization_parameters.json for possible bands to request as band :return: """ r = request.get_json() dataset = r.get("dataset", None) image_id = r.get("imageId", None) band = r.get("band", None) function = r.get("function", None) start_date = r.get("startDate", None) end_date = r.get("endDate", None) image_num_limit = r.get("limit", None) min = r.get("min", None) max = r.get("max", None) if not band: msg = f"band is a required parameter" logger.error(msg) raise error_handler.InvalidUsage(msg) if dataset: source = "projects/dgds-gee/msfd/" + dataset elif image_id: source = image_id else: msg = f"dataset or image_id is a required parameter" logger.error(msg) raise error_handler.InvalidUsage(msg) image_info = dgds_functions.get_dgds_data( source=source, dataset=dataset, image_id=image_id, band=band, function=function, start_date=start_date, end_date=end_date, image_num_limit=image_num_limit, min=min, max=max, ) if not image_info: raise error_handler.InvalidUsage("No images returned.") return Response(json.dumps(image_info), status=200, mimetype="application/json")
def get_chasm_data(): """ Get metocean data. dataset must be provided. :return: """ r = request.get_json() dataset = r.get("dataset", None) band = r["band"] image_id = r.get("imageId", None) function = r.get("function", None) start_date = r.get("startDate", None) end_date = r.get("endDate", None) image_num_limit = r.get("limit", None) min = r.get("min", None) max = r.get("max", None) source = None # Can provide either dataset and/or image_id if not (dataset or image_id): msg = f"dataset or imageId required." logger.error(msg) raise error_handler.InvalidUsage(msg) if dataset: source = "projects/dgds-gee/chasm/" + dataset elif image_id: image_location_parameters = image_id.split("/") source = ("/").join(image_location_parameters[:-1]) image_info = dgds_functions.get_dgds_data( source=source, dataset=dataset, image_id=image_id, band=band, function=function, start_date=start_date, end_date=end_date, image_num_limit=image_num_limit, min=min, max=max, ) if not image_info: raise error_handler.InvalidUsage("No images returned.") return Response(json.dumps(image_info), status=200, mimetype="application/json")
def get_gebco_data(): r = request.get_json() dataset = r.get("dataset", "gebco") band = r.get("band", "elevation") image_id = r.get("imageId", None) start_date = r.get("startDate", None) end_date = r.get("endDate", None) image_num_limit = r.get("limit", None) min = r.get("min", None) max = r.get("max", None) if dataset: source = "projects/dgds-gee/bathymetry/" + dataset + "/2019" if image_id: source = image_id image_info = dgds_functions.get_dgds_data( source=source, dataset=dataset, image_id=image_id, band=band, start_date=start_date, end_date=end_date, image_num_limit=image_num_limit, min=min, max=max, ) if not image_info: raise error_handler.InvalidUsage("No images returned.") return Response(json.dumps(image_info), status=200, mimetype="application/json")
def get_glossis_data(): """ Get GLOSSIS data. Either currents, wind, or waterlevel dataset must be provided. If waterlevel dataset is requested, must specify if band water_level_surge, water_level, or astronomical_tide is requested :return: """ r = request.get_json() dataset = r.get("dataset", None) image_id = r.get("imageId", None) band = r.get("band", None) function = r.get("function", None) start_date = r.get("startDate", None) end_date = r.get("endDate", None) image_num_limit = r.get("limit", None) min = r.get("min", None) max = r.get("max", None) if not (dataset or image_id): msg = f"dataset or imageId required." logger.error(msg) raise error_handler.InvalidUsage(msg) if dataset: source = "projects/dgds-gee/glossis/" + dataset if image_id: image_location_parameters = image_id.split("/") source = ("/").join(image_location_parameters[:-1]) image_info = dgds_functions.get_dgds_data( source=source, dataset=dataset, image_id=image_id, band=band, function=function, start_date=start_date, end_date=end_date, image_num_limit=image_num_limit, min=min, max=max, ) if not image_info: raise error_handler.InvalidUsage("No images returned.") return Response(json.dumps(image_info), status=200, mimetype="application/json")
def get_image_collection_info(): r = request.get_json() source = r['source'] start_date = r.get('startDate', None) end_date = r.get('endDate', None) image_num_limit = r.get('limit', None) info = dgds_functions.get_image_collection_info(source, start_date, end_date, image_num_limit) if not info: raise error_handler.InvalidUsage('No images returned.') return Response(json.dumps(info), status=200, mimetype='application/json')
def get_gll_dtm_data(): r = request.get_json() band = r.get("band", "elevation") imageid = "users/maartenpronk/gll_dtm/gll_dtm_v1" image_info = dgds_functions.get_dgds_data( source=None, image_id=imageid, band=band, ) if not image_info: raise error_handler.InvalidUsage("No images returned.") return Response(json.dumps(image_info), status=200, mimetype="application/json")
def filter_liwo_collection_v1(collection_path, id_key, liwo_ids, band, reducer): """ Create combined max image from collection. Version 1 based on unnamed single and multi band images. :param collection_path: Path to Earth Engine Image Collection :param id_key: Metadata key name for unique ids :param scenario_ids: List of scenario ids to combine :param band: band of image to select :param reducer: reducer operation by which to combine images :return: combined image """ # Filter based on breach location collection = ee.ImageCollection(collection_path) # TODO: how to make this generic, consider GraphQL collection = collection.filter(ee.Filter.inList(id_key, liwo_ids)) collection = collection.map(lambda im: im.set('bandNames', im.bandNames())) n_selected = collection.size().getInfo() if band != 'waterdepth': collection = collection.filterMetadata('bandNames', 'equals', ['b1', 'b2', 'b3', 'b4', 'b5']) n_filtered = collection.size().getInfo() if n_selected != n_filtered: logging.warning('missing images, selected %s, filtered %s', n_selected, n_filtered) # Filter based on band name (characteristic to display) collection = collection.select(band) n_images = collection.size().getInfo() msg = 'No images available for breach locations: %s' % (liwo_ids, ) logger.debug(msg) if not n_images: raise error_handler.InvalidUsage(msg) # get max image reduce_func = getattr(ee.Reducer, reducer)() image = ee.Image(collection.reduce(reduce_func)) # clip image to region and show only values greater than 0 (no-data value given in images) .clip(region) image = image.mask(image.gt(0)) return image
def filter_liwo_collection_v2(collection_path, id_key, scenario_ids, band, reducer): """ Create combined max image from collection. Version 2 based on named image bands :param collection_path: Path to Earth Engine Image Collection :param id_key: Metadata key name for unique ids :param scenario_ids: List of scenario ids to combine :param band: band of image to select :param reducer: reducer operation by which to combine images :return: combined image """ # Filter based on scenario id, band scenarios = ee.ImageCollection(collection_path) scenarios = scenarios.filter(ee.Filter.inList(id_key, scenario_ids)) scenarios = scenarios.filter( ee.Filter.listContains("system:band_names", band)) scenarios = scenarios.select(band) n_selected = scenarios.size().getInfo() if n_selected == 0: msg = 'No images available for breach locations: %s' % (scenario_ids, ) logger.debug(msg) raise error_handler.InvalidUsage(msg) # raise ValueError("No images with band {} in scenario_ids {}".format(band, scenario_ids)) if len(scenario_ids) != n_selected: logging.info("collection {}, missing {} scenarios for band {}".format( collection_path, len(scenario_ids) - n_selected, band)) bounds = scenarios.geometry().bounds() # reduce image reduce_func = getattr(ee.Reducer, reducer)() if reducer == 'min': # Aankomstijden <= 0 should not be included in the aggregated minimum scenarios = scenarios.map(lambda i: i.mask(i.gt(0))) image = ee.Image(scenarios.reduce(reduce_func)) if reducer == 'max': # Do not display any values <= 0 (Some no data values assigned as 0 and not -9999). image = image.mask(image.gt(0)) # reclip by bounds image = image.clip(bounds) return image
def validate_min_lt_max(min, max): if not min < max: raise error_handler.InvalidUsage( 'Specified min must be less than max.')
def get_feature_info(): """ Get image value at point :return: """ r = request.get_json() image_id = r['imageId'] bbox = r['bbox'] datasets = r.get('datasets', None) band = r.get('band', None) function = r.get('function', None) info_format = r.get('info_format', 'JSON') if function == 'mosaic_elevation_datasets': if not datasets: msg = f'datsets list expected for function {function}' raise error_handler.InvalidUsage(msg) image = ee.Image( dgds_functions.mosaic_elevation_datasets(datasets).select( 'elevation')) else: image = ee.Image(image_id) image_location_parameters = image_id.split('/') source = ('/').join(image_location_parameters[:-1]) data_params = dgds_functions.get_dgds_source_vis_params( source, image_id) if band: band_name = data_params['bandNames'][band] image = image.select(band_name) if function: assert (function in data_params.get('function', None)) or \ (function == data_params['function'].get(band, None)), \ f'{function} not an option.' image = dgds_functions.apply_image_operation(image, function, data_params, band) image = image.rename('value') # TODO: get scale of image, or load default scale from parameters value = (image.sample(**{ 'region': ee.Geometry(bbox), 'geometries': True, 'scale': 10 }).first().getInfo()) # if no data at point, value will be None. if not value: # return Feature with value None (standard return format from GEE) value = { "geometry": bbox, "id": "0", "properties": { "value": None }, "type": "Feature" } else: value['properties']['value'] = round(value['properties']['value'], 2) if info_format == 'JSON': value = value['properties'] return Response(json.dumps(value), status=200, mimetype='application/json')
def get_liwo_scenarios(): r = request.get_json() # Currently 'liwo' only option variable = 'liwo' # name of breach location as string liwo_ids = r['liwo_ids'] # band name as string band = r['band'] raster_assets = { 'liwo': 'users/rogersckw9/liwo/liwo-scenarios-03-2019' } bands = { 'waterdepth': 'b1', 'velocity': 'b2', 'riserate': 'b3', 'damage': 'b4', 'fatalities': 'b5' } reducers = { 'waterdepth': 'max', 'velocity': 'max', 'riserate': 'max', 'damage': 'max', 'fatalities': 'max' } # for now use max as a reducer assert band in reducers assert band in bands reducer = reducers[band] styles = { 'waterdepth': { 'sld_style': '\ <RasterSymbolizer>\ <ColorMap type="intervals">\ <ColorMapEntry color="#FFFFFF" opacity="0.01" quantity="0.01999"/>\ <ColorMapEntry color="#CEFEFE" opacity="1.0" quantity="0.5" label="< 0.5"/>\ <ColorMapEntry color="#94bff7" opacity="1.0" quantity="1" label="0.5 - 1.0"/>\ <ColorMapEntry color="#278ef4" opacity="1.0" quantity="1.5" label="1.0 - 1.5"/>\ <ColorMapEntry color="#0000cc" opacity="1.0" quantity="2.0" label="1.5 - 2.0"/>\ <ColorMapEntry color="#4A0177" opacity="1.0" quantity="5" label="2.0 - 5.0"/>\ <ColorMapEntry color="#73004c" opacity="1.0" quantity="9999" label="> 5.0"/>\ </ColorMap>\ </RasterSymbolizer>' }, 'velocity': { 'sld_style': '\ <RasterSymbolizer>\ <ColorMap type="intervals">\ <ColorMapEntry color="#FFFFFF" opacity="0.01" quantity="0.01"/>\ <ColorMapEntry color="#FAD7FE" opacity="1.0" quantity="0.5" label="< 0.5"/>\ <ColorMapEntry color="#E95CF5" opacity="1.0" quantity="1" label="0.5 - 1.0"/>\ <ColorMapEntry color="#CB00DB" opacity="1.0" quantity="2" label="1.0 - 2.0"/>\ <ColorMapEntry color="#8100B1" opacity="1.0" quantity="4" label="2.0 - 4.0"/>\ <ColorMapEntry color="#8100D2" opacity="1.0" quantity="1000" label="> 4.0"/>\ </ColorMap>\ </RasterSymbolizer>' }, 'riserate': { 'sld_style': '\ <RasterSymbolizer>\ <ColorMap type="intervals">\ <ColorMapEntry color="#FFFFFF" opacity="0.01" quantity="0.01"/>\ <ColorMapEntry color="#FFF5E6" opacity="1.0" quantity="0.25" label="< 0.25"/>\ <ColorMapEntry color="#FFD2A8" opacity="1.0" quantity="0.5" label="0.25 - 0.5"/>\ <ColorMapEntry color="#FFAD66" opacity="1.0" quantity="1" label="0.5 - 1.0"/>\ <ColorMapEntry color="#EB7515" opacity="1.0" quantity="2" label="1.0 - 2.0"/>\ <ColorMapEntry color="#B05500" opacity="1.0" quantity="1000000" label="> 2.0"/>\ </ColorMap>\ </RasterSymbolizer>' }, 'damage': { 'sld_style': '\ <RasterSymbolizer>\ <ColorMap type="intervals">\ <ColorMapEntry color="#FFFFFF" opacity="0.01" quantity="0.01"/>\ <ColorMapEntry color="#499b1b" opacity="1.0" quantity="10000" label="< 10.000"/>\ <ColorMapEntry color="#61f033" opacity="1.0" quantity="100000" label="10.000 - 100.000"/>\ <ColorMapEntry color="#ffbb33" opacity="1.0" quantity="1000000" label="100.000 - 1.000.000"/>\ <ColorMapEntry color="#ff3333" opacity="1.0" quantity="5000000" label="1.000.000 - 5.000.000"/>\ <ColorMapEntry color="#8f3333" opacity="1.0" quantity="1000000000000000" label="> 5.000.000"/>\ </ColorMap>\ </RasterSymbolizer>' }, 'fatalities': { 'sld_style': '\ <RasterSymbolizer>\ <ColorMap type="intervals">\ <ColorMapEntry color="#FFFFFF" opacity="0.01" quantity="0.0001"/>\ <ColorMapEntry color="#499b1b" opacity="1.0" quantity="0.1" label="< 0.1"/>\ <ColorMapEntry color="#61f033" opacity="1.0" quantity="0.3" label="0.1 - 0.3"/>\ <ColorMapEntry color="#ffbb33" opacity="1.0" quantity="1" label="0.3 - 1"/>\ <ColorMapEntry color="#ff3333" opacity="1.0" quantity="3" label="1 - 3"/>\ <ColorMapEntry color="#8f3333" opacity="1.0" quantity="10000" label="> 3"/>\ </ColorMap>\ </RasterSymbolizer>' } } # Filter based on breach location collection = ee.ImageCollection(raster_assets[variable]) # TODO: how to make this generic, consider GraphQL collection = collection.filter( ee.Filter.inList('LIWO_ID', liwo_ids) ) collection = collection.map( lambda im: im.set('bandNames', im.bandNames()) ) n_selected = collection.size().getInfo() collection = collection.filterMetadata('bandNames', 'equals', ['b1', 'b2', 'b3', 'b4', 'b5']) n_filtered = collection.size().getInfo() if n_selected != n_filtered: logging.warning('missing images, selected %s, filtered %s', n_selected, n_filtered) # Filter based on band name (characteristic to display) collection = collection.select(bands[band]) n_images = collection.size().getInfo() msg = 'No images available for breach locations: %s' % (liwo_ids, ) logger.debug(msg) if not n_images: raise error_handler.InvalidUsage(msg) # get max image reduce_func = getattr(ee.Reducer, reducer)() image = ee.Image(collection.reduce(reduce_func)) # clip image to region and mask all 0 values (no-data value given in images) .clip(region) image = image.mask(image.neq(0)) def generate_image_info(im, params): """generate url and tokens for image""" im = ee.Image(im) # some images are scaled to a factor of 10. if params.get('scale') == 'log': im = im.log10() im = im.sldStyle(params.get('sld_style')) m = im.getMapId() mapid = m.get('mapid') token = m.get('token') url = 'https://earthengine.googleapis.com/map/{mapid}/{{z}}/{{x}}/{{y}}?token={token}'.format( mapid=mapid, token=token ) result = { 'mapid': mapid, 'token': token, 'url': url } return result def export_image_response(image, region, info): """create export response for image""" url = image.getDownloadURL({ 'name': 'export', 'format': 'tif', 'crs': info['crs'], 'scale': info['scale'], 'region': json.dumps(region.bounds(info['scale']).getInfo()) }) result = {'export_url': url} return result # TODO: generate visualization params for map ids params = styles[band] info = generate_image_info(image, params) info['variable'] = variable info['liwo_ids'] = liwo_ids info['band'] = band # # Following needed for export: # # Specify region over which to compute # export is True or None/False if r.get('export'): region = ee.Geometry(r['region']) # scale of pixels for export, in meters info['scale'] = float(r['scale']) # coordinate system for export projection info['crs'] = r['crs'] extra_info = export_image_response(image, region, info) info.update(extra_info) return Response( json.dumps(info), status=200, mimetype='application/json' )