def get_process_list(process): """Analyse the process description and return the Actinia process chain and the name of the processing result layer which is a single raster layer :param args: The process description arguments :return: (output_names, actinia_process_list) """ input_names, process_list = analyse_process_graph(process) output_names = [] if "method" not in process: raise Exception("Parameter method is required.") for input_name in input_names: location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components( input_name) output_name = "%s_%s" % (layer_name, PROCESS_NAME) output_names.append(output_name) pc = create_process_chain_entry(input_name, process["method"], output_name) process_list.append(pc) return output_names, process_list
def get_process_list(args): """Analyse the process description and return the Actinia process chain and the name of the processing result layer which is a single raster layer :param args: The process description :return: (output_names, actinia_process_list) """ # Get the input description and the process chain to attach this process input_names, process_list = process_node_to_actinia_process_chain(args) output_names = [] for input_name in input_names: location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components( input_name) output_name = create_output_name(layer_name, PROCESS_NAME) output_names.append(output_name) if "python_file_url" in args: python_file_url = args["python_file_url"] else: raise Exception( "Python file is missing in the process description") pc = create_process_chain_entry(input_name=input_name, python_file_url=python_file_url, output_name=output_name) process_list.append(pc) return output_names, process_list
def create_process_chain_entry(input_name, python_file_url, output_name): """Create a Actinia command of the process chain that uses g.region to create a valid computational region for the provide input strds :param strds_name: The name of the strds :param python_file_url: The URL to the python file that defines the UDF :param output_name: The name of the output raster layer :return: A Actinia process chain description """ location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components(input_name) input_name = layer_name if mapset is not None: input_name = layer_name + "@" + mapset pc = {"id": "t_rast_aggr_func", "module": "t.rast.aggr_func", "inputs": [{"import_descr": {"source": python_file_url, "type": "file"}, "param": "pyfile", "value": "$file::my_py_func"}, {"param": "input", "value": input_name}, {"param": "output", "value": output_name}]} return pc
def get_process_list(process): """Analyse the process description and return the Actinia process chain and the name of the processing result :param args: The process description arguments :return: (output_names, actinia_process_list) """ output_names = [] process_list = [] # First analyse the data entries if "red" not in process: raise Exception("Process %s requires parameter <red>" % PROCESS_NAME) if "nir" not in process: raise Exception("Process %s requires parameter <nir>" % PROCESS_NAME) # Get the red and ir data separately red_process = dict(myproc="myproc", red=process["red"]) nir_process = dict(myproc="myproc", red=process["nir"]) red_input_names, red_process_list = analyse_process_graph(red_process) process_list.extend(red_process_list) nir_input_names, nir_process_list = analyse_process_graph(nir_process) process_list.extend(nir_process_list) if not red_input_names: raise Exception("Process %s requires an input strds for band <red>" % PROCESS_NAME) if not nir_input_names: raise Exception("Process %s requires an input strds for band <nir>" % PROCESS_NAME) red_stds = red_input_names[-1] nir_strds = nir_input_names[-1] # Take the last entry from the if len(red_input_names) > 1: output_names.extend(red_input_names[0:-1]) # Take the last entry from the if len(nir_input_names) > 1: output_names.extend(nir_input_names[0:-1]) location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components( red_stds) output_name = "%s_%s" % (layer_name, PROCESS_NAME) output_names.append(output_name) pc = create_process_chain_entry(nir_strds, red_stds, output_name) process_list.extend(pc) return output_names, process_list
def create__process_chain_entry(input_name, start_time, end_time, output_name): """Create a Actinia command of the process chain that uses t.rast.extract to create a subset of a strds :param strds_name: The name of the strds :param start_time: :param end_time: :return: A Actinia process chain description """ location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components( input_name) input_name = layer_name if mapset is not None: input_name = layer_name + "@" + mapset base_name = "%s_extract" % layer_name # Get info about the time series to extract its resolution settings and bbox rn = randint(0, 1000000) pc = { "id": "t_rast_extract_%i" % rn, "module": "t.rast.extract", "inputs": [{ "param": "input", "value": input_name }, { "param": "where", "value": "start_time >= '%(start)s' " "AND end_time <= '%(end)s'" % { "start": start_time, "end": end_time } }, { "param": "output", "value": output_name }, { "param": "expression", "value": "1.0 * %s" % input_name }, { "param": "basename", "value": base_name }, { "param": "suffix", "value": "num" }] } return pc
def get_process_list(process): """Analyse the process description and return the Actinia process chain and the name of the processing result :param args: The process description arguments :return: (output_names, actinia_process_list) """ output_names = [] # First analyse the data entries if "red" not in process: raise Exception("Process %s requires parameter <red>" % PROCESS_NAME) if "nir" not in process: raise Exception("Process %s requires parameter <nir>" % PROCESS_NAME) red_strds = None nir_strds = None input_names, process_list = analyse_process_graph(process) # Find the red and nir datasets in the input for input_name in input_names: if process["red"] in input_name: red_strds = input_name elif process["nir"] in input_name: nir_strds = input_name else: # Pipe other inputs to the output output_names.append(input_name) if not red_strds: raise Exception("Process %s requires an input strds for band <red>" % PROCESS_NAME) if not nir_strds: raise Exception("Process %s requires an input strds for band <nir>" % PROCESS_NAME) location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components( red_strds) output_name = "%s_%s" % (layer_name, PROCESS_NAME) output_names.append(output_name) pc = create_process_chain_entry(nir_strds, red_strds, output_name) process_list.extend(pc) return output_names, process_list
def get_process_list(process): """Analyse the process description and return the Actinia process chain and the name of the processing result strds that was filtered by start and end date :param process: The process description :return: (output_names, actinia_process_list) """ # Get the input description and the process chain to attach this process input_names, process_list = analyse_process_graph(process) output_names = [] for input_name in input_names: location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components( input_name) # Skip if the datatype is not a strds and put the input into the output if datatype and datatype != "strds": output_names.append(input_name) continue output_name = "%s_%s" % (layer_name, PROCESS_NAME) output_names.append(output_name) start_time = None end_time = None if "from" in process: start_time = process["from"] if "to" in process: end_time = process["to"] pc = create__process_chain_entry(input_name=input_name, start_time=start_time, end_time=end_time, output_name=output_name) process_list.append(pc) return output_names, process_list
def create_process_chain_entry(input_name): """Create a Actinia process description that uses t.rast.series to create the minimum value of the time series. :param input_time_series: The input time series name :param output_map: The name of the output map :return: A Actinia process chain description """ location, mapset, datatype, layer_name = ActiniaInterface.layer_def_to_components(input_name) input_name = layer_name if mapset is not None: input_name = layer_name + "@" + mapset rn = randint(0, 1000000) pc = {} if datatype == "raster": pc = {"id": "r_info_%i" % rn, "module": "r.info", "inputs": [{"param": "map", "value": input_name}, ], "flags": "g"} elif datatype == "vector": pc = {"id": "v_info_%i" % rn, "module": "v.info", "inputs": [{"param": "map", "value": input_name}, ], "flags": "g"} elif datatype == "strds": pc = {"id": "t_info_%i" % rn, "module": "t.info", "inputs": [{"param": "input", "value": input_name}, ], "flags": "g"} else: raise Exception("Unsupported datatype") return pc
class CollectionInformationResource(Resource): def __init__(self): self.iface = ActiniaInterface() def get(self, name): # List strds maps from the GRASS location location, mapset, datatype, layer = self.iface.layer_def_to_components( name) if location == "stac": status_code, collection = self.iface.get_stac_collection(name=name) if status_code != 200: return make_response( jsonify( { "id": "12345678", "code": "Internal", "message": "Server error: %s" % (name), "links": {}}), 500) # Not using CollectionInformation model here for now # as valid STAC collections comply. # Using it here might omit some properties # which are not modelled in this backend (e.g. assets) return make_response(collection, 200) status_code, layer_data = self.iface.layer_info(layer_name=name) if status_code != 200: return make_response( jsonify( { "id": "12345678", "code": "CollectionNotFound", "message": "Collection '%s' does not exist." % (name), "links": {}}), 404) # Get the projection from the GRASS mapset status_code, mapset_info = self.iface.mapset_info( location=location, mapset=mapset) if status_code != 200: return make_response( jsonify( { "id": "12345678", "code": "Internal", "message": "Server error: %s" % (mapset_info), "links": {}}), 500) extent = CollectionExtent( spatial=( float( layer_data["west"]), float( layer_data["south"]), float( layer_data["east"]), float( layer_data["north"])), temporal=( "1900-01-01T00:00:00", "2100-01-01T00:00:00")) title = "Raster dataset" bands = [] dimensions = {"x": { "type": "spatial", "axis": "x", "extent": [layer_data["west"], layer_data["east"]], "reference_system": mapset_info["projection"] }, "y": { "type": "spatial", "axis": "y", "extent": [layer_data["south"], layer_data["north"]], "reference_system": mapset_info["projection"] }, } platform = "unknown" instrument = "unknown" if datatype.lower() == "strds": title = "Space time raster dataset" start_time = layer_data["start_time"] end_time = layer_data["end_time"] if start_time: start_time = start_time.replace( " ", "T").replace( "'", "").replace( '"', '') if end_time: end_time = end_time.replace( " ", "T").replace( "'", "").replace( '"', '') dimensions['t'] = {"type": "temporal", "extent": [start_time, end_time] } extent = CollectionExtent( spatial=( float( layer_data["west"]), float( layer_data["south"]), float( layer_data["east"]), float( layer_data["north"])), temporal=( start_time, end_time)) if "semantic_labels" in layer_data: bandlist = layer_data["semantic_labels"].split(',') dimensions['bands'] = {"type": "bands", "values": bandlist } for bandname in bandlist: # not so nice, better use different name and common_name # waiting for GRASS GIS bands.append(EOBands(name=bandname, common_name=bandname)) # get platform and sensor # see # https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md#platform # https://github.com/radiantearth/stac-spec/blob/master/item-spec/common-metadata.md#instruments if "_" in bandlist[0]: sensor_abbr = bandlist[0].split('_')[0] if sensor_abbr == "L5": platform = "landsat-5" instrument = "tm, mss" elif sensor_abbr == "L7": platform = "landsat-7" instrument = "etm+" elif sensor_abbr == "L8": platform = "landsat-8" instrument = "oli, trs" elif sensor_abbr == "S1": platform = "sentinel-1" instrument = "c-sar" elif sensor_abbr == "S2": platform = "sentinel-2" instrument = "msi" if datatype.lower() == "vector": title = "Vector dataset" description = "GRASS GIS location/mapset path: /%s/%s" % ( location, mapset) crs = mapset_info["projection"] coordinate_transform_extent_to_EPSG_4326(crs=crs, extent=extent) # GRASS / actinia do not yet report platform and instrument properties = (CollectionProperties(eo_platform=platform, eo_instrument=instrument, eo_bands=bands)) ci = CollectionInformation(id=name, title=title, description=description, extent=extent, properties=properties, dimensions=dimensions) return ci.as_response(http_status=200)
class CollectionInformationResource(Resource): def __init__(self): self.iface = ActiniaInterface() def get(self, name): # List strds maps from the GRASS location location, mapset, datatype, layer = self.iface.layer_def_to_components( name) status_code, layer_data = self.iface.layer_info(layer_name=name) if status_code != 200: return make_response( jsonify( { "description": "An internal error occurred " "while catching GRASS GIS layer information " "for layer <%s>!\n Error: %s" "" % (name, str(layer_data)) }, 400)) # Get the projection from the GRASS mapset status_code, mapset_info = self.iface.mapset_info(location=location, mapset=mapset) if status_code != 200: return make_response( jsonify( { "description": "An internal error occurred " "while catching mapset info " "for mapset <%s>!" % mapset }, 400)) extent = Extent(spatial=(float(layer_data["west"]), float(layer_data["south"]), float(layer_data["east"]), float(layer_data["north"]))) title = "Raster dataset" if datatype.lower() == "strds": title = "Space time raster dataset" extent = Extent(spatial=(float(layer_data["west"]), float(layer_data["south"]), float(layer_data["east"]), float(layer_data["north"])), temporal=(layer_data["start_time"], layer_data["end_time"])) if datatype.lower() == "vector": title = "Vector dataset" description = "GRASS GIS location/mapset path: /%s/%s" % (location, mapset) crs = mapset_info["projection"] coorindate_transform_extent_to_EPSG_4326(crs=crs, extent=extent) ci = CollectionInformation(name=name, title=title, description=description, extent=extent) return make_response(ci.to_json(), 200)