def test_encode_getmap(): print( kvp_encode_get_map_request( GetMapRequest(Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', dimensions={}))) print( kvp_encode_get_map_request( GetMapRequest(Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', time=Range(year(2012), year(2013)), elevation=1000, dimensions={ 'wavelength': '2456.2', 'pressure': ['123', '234'], 'range': [Range('0', '1'), Range('2', '4')] })))
def test_kvp_decode_getmap(): # simplest form request = "service=WMS&version=1.3.0&request=GetMap&layers=a,b,c&styles=s1,s2,&crs=EPSG:4326&bbox=0,0,10,10&width=256&height=256&format=image/jpeg" # noqa assert kvp_decode_getmap(request) == GetMapRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', dimensions={}) # multi-dim request = "service=WMS&version=1.3.0&request=GetMap&layers=a,b,c&styles=s1,s2,&crs=EPSG:4326&bbox=0,0,10,10&width=256&height=256&format=image/jpeg&time=2012/2013&elevation=1000&dim_wavelength=2456.2&dim_pressure=123,234&dim_range=0/1,2/4" # noqa assert kvp_decode_getmap(request) == GetMapRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', time=Range(year(2012), year(2013)), elevation=1000, dimensions={ 'wavelength': '2456.2', 'pressure': ['123', '234'], 'range': [Range('0', '1'), Range('2', '4')] }) # elevation-range request = "service=WMS&version=1.3.0&request=GetMap&layers=a,b,c&styles=s1,s2,&crs=EPSG:4326&bbox=0,0,10,10&width=256&height=256&format=image/jpeg&elevation=1000/2000/10" # noqa assert kvp_decode_getmap(request) == GetMapRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', elevation=Range(1000, 2000, 10), dimensions={}) # month to date request = "service=WMS&version=1.3.0&request=GetMap&layers=a,b,c&styles=s1,s2,&crs=EPSG:4326&bbox=0,0,10,10&width=256&height=256&format=image/jpeg&time=2012-02/2012-03-15" # noqa assert kvp_decode_getmap(request) == GetMapRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', time=Range(month(2012, 2), date(2012, 3, 15)), dimensions={})
class ExecuteRequest: process_id: str mode: ExecutionMode response: ResponseType inputs: List[Input] = field(default_factory=list) output_definitions: List[OutputDefinition] = field(default_factory=list) version: Version = Version(2, 0, 0)
def test_kvp_decode_getfeatureinfo(): # minimal form request = "service=WMS&version=1.3.0&request=GetFeatureInfo&layers=a,b,c&styles=s1,s2,&crs=EPSG:4326&bbox=0,0,10,10&width=256&height=256&format=image/jpeg&query_layers=a,b&info_format=text/xml&i=12&j=12" # noqa assert kvp_decode_getfeatureinfo(request) == GetFeatureInfoRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', dimensions={}, query_layers=['a', 'b'], info_format='text/xml', i=12, j=12, # feature_count=15, ) # full request = "service=WMS&version=1.3.0&request=GetFeatureInfo&layers=a,b,c&styles=s1,s2,&crs=EPSG:4326&bbox=0,0,10,10&width=256&height=256&format=image/jpeg&query_layers=a,b&info_format=text/xml&i=12&j=12&feature_count=15" # noqa assert kvp_decode_getfeatureinfo(request) == GetFeatureInfoRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', dimensions={}, query_layers=['a', 'b'], info_format='text/xml', i=12, j=12, feature_count=15, )
class GetCoverageRequest: coverage_id: str version: Version = Version(2, 0, 1) format: str = None mediatype: str = None subsetting_crs: str = None output_crs: str = None subsets: List[SubsetTypes] = field(default_factory=list) scalefactor: float = None scales: List[ScaleTypes] = field(default_factory=list) interpolation: str = None axis_interpolations: List[AxisInterpolation] = field(default_factory=list) range_subset: List[Union[str, RangeInterval]] = None geotiff_encoding_parameters: GeoTIFFEncodingParameters = field( default_factory=GeoTIFFEncodingParameters) # noqa
def test_encode_getfeatureinfo(): GetFeatureInfoRequest( Version(1, 3, 0), layers=['a', 'b', 'c'], styles=['s1', 's2', None], bounding_box=BoundingBox('EPSG:4326', [0, 0, 10, 10]), width=256, height=256, format='image/jpeg', dimensions={}, query_layers=['a', 'b'], info_format='text/xml', i=12, j=12, feature_count=15, )
class DescribeProcessRequest: process_ids: List[str] version: Version = Version(2, 0, 0)
class DismissRequest: job_id: str version: Version = Version(2, 0, 0)
class GetResultRequest: job_id: str version: Version = Version(2, 0, 0)
class GetStatusRequest: job_id: str version: Version = Version(2, 0, 0)
class WCS1GetCoverageRequest(): version = Version(1, 0, 0) #pylint: disable=too-many-instance-attributes, too-many-branches, too-many-statements, too-many-locals def __init__(self, args): self.args = args cfg = get_config() # Argument: Coverage (required) if "coverage" not in args: raise WCS1Exception("No coverage specified", WCS1Exception.MISSING_PARAMETER_VALUE, locator="COVERAGE parameter", valid_keys=list(cfg.product_index)) self.product_name = args["coverage"] self.product = cfg.product_index.get(self.product_name) if not self.product or not self.product.wcs: raise WCS1Exception("Invalid coverage: %s" % self.product_name, WCS1Exception.COVERAGE_NOT_DEFINED, locator="COVERAGE parameter", valid_keys=list(cfg.product_index)) # Argument: FORMAT (required) if "format" not in args: raise WCS1Exception("No FORMAT parameter supplied", WCS1Exception.MISSING_PARAMETER_VALUE, locator="FORMAT parameter", valid_keys=cfg.wcs_formats_by_name) if args["format"] not in cfg.wcs_formats_by_name: raise WCS1Exception("Unsupported format: %s" % args["format"], WCS1Exception.INVALID_PARAMETER_VALUE, locator="FORMAT parameter", valid_keys=cfg.wcs_formats_by_name) self.format = cfg.wcs_formats_by_name[args["format"]] # Argument: (request) CRS (required) if "crs" not in args: raise WCS1Exception("No request CRS specified", WCS1Exception.MISSING_PARAMETER_VALUE, locator="CRS parameter", valid_keys=list(cfg.published_CRSs)) self.request_crsid = args["crs"] if self.request_crsid not in cfg.published_CRSs: raise WCS1Exception("%s is not a supported CRS" % self.request_crsid, WCS1Exception.INVALID_PARAMETER_VALUE, locator="CRS parameter", valid_keys=list(cfg.published_CRSs)) self.request_crs = cfg.crs(self.request_crsid) # Argument: response_crs (optional) if "response_crs" in args: self.response_crsid = args["response_crs"] if self.response_crsid not in cfg.published_CRSs: raise WCS1Exception("%s is not a supported CRS" % self.response_crsid, WCS1Exception.INVALID_PARAMETER_VALUE, locator="RESPONSE_CRS parameter", valid_keys=list(cfg.published_CRSs)) self.response_crs = geometry.CRS(self.response_crsid) self.response_crs = cfg.crs(self.response_crsid) else: self.response_crsid = self.request_crsid self.response_crs = self.request_crs # Arguments: One of BBOX or TIME is required #if "bbox" not in args and "time" not in args: # raise WCS1Exception("At least one of BBOX or TIME parameters must be supplied", # WCS1Exception.MISSING_PARAMETER_VALUE, # locator="BBOX or TIME parameter" # ) # Argument: BBOX (technically not required if TIME supplied, but # it's not clear to me what that would mean.) # For WCS 1.0.0 all bboxes will be specified as minx, miny, maxx, maxy if "bbox" not in args: raise WCS1Exception("No BBOX parameter supplied", WCS1Exception.MISSING_PARAMETER_VALUE, locator="BBOX or TIME parameter") try: self.minx, self.miny, self.maxx, self.maxy = map( float, args['bbox'].split(',')) except: raise WCS1Exception("Invalid BBOX parameter", WCS1Exception.INVALID_PARAMETER_VALUE, locator="BBOX parameter") # Argument: TIME # if self.product.wcs_sole_time: # self.times = [parse(self.product.wcs_sole_time).date()] if "time" not in args: # CEOS treats no supplied time argument as all time. # I'm really not sure what the right thing to do is, but QGIS wants us to do SOMETHING - treat it as "now" self.times = [self.product.ranges["times"][-1]] else: # TODO: the min/max/res format option? # It's a bit underspeced. I'm not sure what the "res" would look like. times = args["time"].split(",") self.times = [] for t in times: if t == "now": continue try: time = parse(t).date() if time not in self.product.ranges["time_set"]: raise WCS1Exception( "Time value '%s' not a valid date for coverage %s" % (t, self.product_name), WCS1Exception.INVALID_PARAMETER_VALUE, locator="TIME parameter", valid_keys=[ d.strftime('%Y-%m-%d') for d in self.product.ranges["time_set"] ]) self.times.append(time) except ValueError: raise WCS1Exception( "Time value '%s' not a valid ISO-8601 date" % t, WCS1Exception.INVALID_PARAMETER_VALUE, locator="TIME parameter", valid_keys=[ d.strftime('%Y-%m-%d') for d in self.product.ranges["time_set"] ]) self.times.sort() if len(self.times) == 0: raise WCS1Exception( "No valid ISO-8601 dates", WCS1Exception.INVALID_PARAMETER_VALUE, locator="TIME parameter", valid_keys=[ d.strftime('%Y-%m-%d') for d in self.product.ranges["time_set"] ]) elif len(self.times) > 1 and not self.format["multi-time"]: raise WCS1Exception( "Cannot select more than one time slice with the %s format" % self.format["name"], WCS1Exception.INVALID_PARAMETER_VALUE, locator="TIME and FORMAT parameters") # Range constraint parameter: MEASUREMENTS # No default is set in the DescribeCoverage, so it is required # But QGIS wants us to work without one, so take default from config if "measurements" in args: bands = args["measurements"] self.bands = [] for b in bands.split(","): if not b: continue try: self.bands.append(self.product.band_idx.band(b)) except ConfigException: raise WCS1Exception( f"Invalid measurement: {b}", WCS1Exception.INVALID_PARAMETER_VALUE, locator="MEASUREMENTS parameter", valid_keys=self.product.band_idx.band_labels()) if not bands: raise WCS1Exception( "No measurements supplied", WCS1Exception.INVALID_PARAMETER_VALUE, locator="MEASUREMENTS parameter", valid_keys=self.product.band_idx.band_labels()) elif "styles" in args and args["styles"]: # Use style bands. # Non-standard protocol extension. # # As we have correlated WCS and WMS service implementations, # we can accept a style from WMS, and return the bands used for it. styles = args["styles"].split(",") if len(styles) != 1: raise WCS1Exception("Multiple style parameters not supported") style = self.product.style_index.get(styles[0]) if style: self.bands = style.needed_bands else: self.bands = self.product.wcs_default_bands else: self.bands = self.product.wcs_default_bands # Argument: EXCEPTIONS (optional - defaults to XML) if "exceptions" in args and args[ "exceptions"] != "application/vnd.ogc.se_xml": raise WCS1Exception( f"Unsupported exception format: {args['exceptions']}", WCS1Exception.INVALID_PARAMETER_VALUE, locator="EXCEPTIONS parameter") # Argument: INTERPOLATION (optional only nearest-neighbour currently supported.) # If 'none' is supported in future, validation of width/height/res will need to change. if "interpolation" in args and args[ "interpolation"] != "nearest neighbor": raise WCS1Exception( f'Unsupported interpolation method: {args["interpolation"]}', WCS1Exception.INVALID_PARAMETER_VALUE, locator="INTERPOLATION parameter") if "width" in args: if "resx" in args or "resy" in args: raise WCS1Exception( "Specify WIDTH/HEIGHT parameters OR RESX/RESY parameters - not both", WCS1Exception.MISSING_PARAMETER_VALUE, locator="RESX/RESY/WIDTH/HEIGHT parameters") if "height" not in args: raise WCS1Exception( "WIDTH parameter supplied without HEIGHT parameter", WCS1Exception.MISSING_PARAMETER_VALUE, locator="WIDTH/HEIGHT parameters") try: self.height = int(args["height"]) if self.height < 1: raise ValueError() except ValueError: raise WCS1Exception( "HEIGHT parameter must be a positive integer", WCS1Exception.INVALID_PARAMETER_VALUE, locator="HEIGHT parameter") try: self.width = int(args["width"]) if self.width < 1: raise ValueError() except ValueError: raise WCS1Exception( "WIDTH parameter must be a positive integer", WCS1Exception.INVALID_PARAMETER_VALUE, locator="WIDTH parameter") self.resx = (self.maxx - self.minx) / self.width self.resy = (self.maxy - self.miny) / self.height elif "resx" in args: if "height" in args: raise WCS1Exception( "Specify WIDTH/HEIGHT parameters OR RESX/RESY parameters - not both", WCS1Exception.MISSING_PARAMETER_VALUE, locator="RESX/RESY/WIDTH/HEIGHT parameters") if "resy" not in args: raise WCS1Exception( "RESX parameter supplied without RESY parameter", WCS1Exception.MISSING_PARAMETER_VALUE, locator="RESX/RESY parameters") try: self.resx = float(args["resx"]) if self.resx <= 0.0: raise ValueError(0) except ValueError: raise WCS1Exception("RESX parameter must be a positive number", WCS1Exception.INVALID_PARAMETER_VALUE, locator="RESX parameter") try: self.resy = float(args["resy"]) if self.resy <= 0.0: raise ValueError(0) except ValueError: raise WCS1Exception("RESY parameter must be a positive number", WCS1Exception.INVALID_PARAMETER_VALUE, locator="RESY parameter") self.width = (self.maxx - self.minx) / self.resx self.height = (self.maxy - self.miny) / self.resy self.width = int(self.width + 0.5) self.height = int(self.height + 0.5) elif "height" in args: raise WCS1Exception( "HEIGHT parameter supplied without WIDTH parameter", WCS1Exception.MISSING_PARAMETER_VALUE, locator="WIDTH/HEIGHT parameters") elif "resy" in args: raise WCS1Exception( "RESY parameter supplied without RESX parameter", WCS1Exception.MISSING_PARAMETER_VALUE, locator="RESX/RESY parameters") else: raise WCS1Exception( "You must specify either the WIDTH/HEIGHT parameters or RESX/RESY", WCS1Exception.MISSING_PARAMETER_VALUE, locator="RESX/RESY/WIDTH/HEIGHT parameters") self.extent = geometry.polygon([(self.minx, self.miny), (self.minx, self.maxy), (self.maxx, self.maxy), (self.maxx, self.miny), (self.minx, self.miny)], self.request_crs) xscale = (self.maxx - self.minx) / self.width yscale = (self.miny - self.maxy) / self.height trans_aff = Affine.translation(self.minx, self.maxy) scale_aff = Affine.scale(xscale, yscale) self.affine = trans_aff * scale_aff self.geobox = geometry.GeoBox(self.width, self.height, self.affine, self.request_crs)
class DescribeCoverageRequest: coverage_ids: List[str] version: Version = Version(2, 0, 1)