def __init__(self, cfg, parent_layer=None, **kwargs): super().__init__(cfg, **kwargs) self.global_cfg = kwargs["global_cfg"] self.parent_layer = parent_layer if "title" not in cfg: raise ConfigException( "Layer without title found under parent layer %s" % str(parent_layer)) self.title = cfg["title"] if "abstract" in cfg: self.abstract = cfg["abstract"] elif parent_layer: self.abstract = parent_layer.abstract else: raise ConfigException( "No abstract supplied for top-level layer %s" % self.title) # Accumulate keywords self.keywords = set() if self.parent_layer: for word in self.parent_layer.keywords: self.keywords.add(word) else: for word in self.global_cfg.keywords: self.keywords.add(word) for word in cfg.get("keywords", []): self.keywords.add(word) # Inherit or override attribution if "attribution" in cfg: self.attribution = AttributionCfg.parse(cfg.get("attribution")) elif parent_layer: self.attribution = self.parent_layer.attribution else: self.attribution = self.global_cfg.attribution
def parse_flags(self, cfg): if cfg: self.parse_pq_names(cfg) self.pq_band = cfg["band"] if "fuse_func" in cfg: self.pq_fuse_func = FunctionWrapper(self, cfg["fuse_func"]) else: self.pq_fuse_func = None self.pq_ignore_time = cfg.get("ignore_time", False) self.ignore_info_flags = cfg.get("ignore_info_flags", []) self.pq_manual_merge = cfg.get("manual_merge", False) else: self.pq_names = [] self.pq_low_res_names = [] self.pq_name = None self.pq_band = None self.pq_ignore_time = False self.ignore_info_flags = [] self.pq_manual_merge = False self.declare_unready("pq_products") self.declare_unready("pq_product") self.declare_unready("flags_def") self.declare_unready("info_mask") self.pq_products = [] self.pq_low_res_products = []
def parse_wcs(self, cfg): if cfg is None: self.wcs = False return else: self.wcs = True # Native CRS self.cfg_native_crs = cfg.get("native_crs") self.declare_unready("native_CRS") self.declare_unready("native_CRS_def") # Rectified Grids self.declare_unready("origin_x") self.declare_unready("origin_y") self.cfg_native_resolution = cfg.get("native_resolution") self.declare_unready("resolution_x") self.declare_unready("resolution_y") self.declare_unready("grid_high_x") self.declare_unready("grid_high_y") self.declare_unready("grids") # Band management self.wcs_raw_default_bands = cfg["default_bands"] self.declare_unready("wcs_default_bands") # Native format if "native_format" in cfg: self.native_format = cfg["native_format"] if self.native_format not in self.global_cfg.wcs_formats_by_name: raise ConfigException( f"WCS native format {self.native_format} for layer {self.name} is not in supported formats list" ) else: self.native_format = self.global_cfg.native_wcs_format
def __init__(self, cfg, global_cfg, dc, parent_layer=None): self.global_cfg = global_cfg self.parent_layer = parent_layer if "title" not in cfg: raise ConfigException("Layer without title found under parent layer %s" % str(parent_layer)) self.title = cfg["title"] try: if "abstract" in cfg: self.abstract = cfg["abstract"] elif parent_layer: self.abstract = parent_layer.abstract else: raise ConfigException("No abstract supplied for top-level layer %s" % self.title) # Accumulate keywords self.keywords = set() if self.parent_layer: for word in self.parent_layer.keywords: self.keywords.add(word) else: for word in self.global_cfg.keywords: self.keywords.add(word) for word in cfg.get("keywords", []): self.keywords.add(word) # Inherit or override attribution if "attribution" in cfg: self.attribution = AttributionCfg.parse(cfg.get("attribution")) elif parent_layer: self.attribution = self.parent_layer.attribution else: self.attribution = self.global_cfg.attribution except KeyError: raise ConfigException("Required entry missing in layer %s" % self.title) super().__init__({})
def parse_ows_layer(cfg, global_cfg, parent_layer=None): if cfg.get("name", None): if cfg.get("multi_product", False): return OWSMultiProductLayer(cfg, global_cfg, parent_layer) else: return OWSProductLayer(cfg, global_cfg, parent_layer) else: return OWSFolder(cfg, global_cfg, parent_layer)
def parse_resource_limits(self, cfg): wms_cfg = cfg.get("wms", {}) wcs_cfg = cfg.get("wcs", {}) self.zoom_fill = wms_cfg.get("zoomed_out_fill_colour", [150, 180, 200, 160]) self.min_zoom = wms_cfg.get("min_zoom_factor", 300.0) self.max_datasets_wms = wms_cfg.get("max_datasets", 0) self.max_datasets_wcs = wcs_cfg.get("max_datasets", 0)
def parse_feature_info(self, cfg): self.feature_info_include_utc_dates = cfg.get("include_utc_dates", False) custom = cfg.get("include_custom", {}) self.feature_info_custom_includes = { k: FunctionWrapper(self, v) for k, v in custom.items() }
def parse_resource_limits(self, cfg): wms_cfg = cfg.get("wms", {}) wcs_cfg = cfg.get("wcs", {}) self.zoom_fill = wms_cfg.get("zoomed_out_fill_colour", [150, 180, 200, 160]) self.min_zoom = wms_cfg.get("min_zoom_factor", 300.0) self.max_datasets_wms = wms_cfg.get("max_datasets", 0) self.max_datasets_wcs = wcs_cfg.get("max_datasets", 0) self.wms_cache_rules = CacheControlRules( wms_cfg.get("dataset_cache_rules"), self, self.max_datasets_wms) self.wcs_cache_rules = CacheControlRules( wcs_cfg.get("dataset_cache_rules"), self, self.max_datasets_wcs)
def parse_image_processing(self, cfg): emf_cfg = cfg["extent_mask_func"] if isinstance(emf_cfg, Mapping) or isinstance(emf_cfg, str): self.extent_mask_func = [ FunctionWrapper(self, emf_cfg) ] else: self.extent_mask_func = list([ FunctionWrapper(self, emf) for emf in emf_cfg ]) raw_afb = cfg.get("always_fetch_bands", []) self.always_fetch_bands = list([ self.band_idx.band(b) for b in raw_afb ]) self.solar_correction = cfg.get("apply_solar_corrections", False) self.data_manual_merge = cfg.get("manual_merge", False) if cfg.get("fuse_func"): self.fuse_func = FunctionWrapper(self, cfg["fuse_func"]) else: self.fuse_func = None
def __init__(self, cfg): self.title = cfg.get("title") self.url = cfg.get("url") logo = cfg.get("logo") if not logo: self.logo_width = None self.logo_height = None self.logo_url = None self.logo_fmt = None else: self.logo_width = logo.get("width") self.logo_height = logo.get("height") self.logo_url = logo.get("url") self.logo_fmt = logo.get("format")
def __init__(self, refresh=False): if not self.initialised or refresh: self.initialised = True cfg = read_config() try: self.parse_global(cfg["global"]) except KeyError as e: raise ConfigException( "Missing required config entry in 'global' section: %s" % str(e)) if self.wms: self.parse_wms(cfg.get("wms", {})) else: self.parse_wms({}) if self.wcs: try: self.parse_wcs(cfg["wcs"]) except KeyError as e: raise ConfigException( "Missing required config entry in 'wcs' section (with WCS enabled): %s" % str(e)) else: self.parse_wcs(None) try: self.parse_layers(cfg["layers"]) except KeyError as e: raise ConfigException( "Missing required config entry in 'layers' section") super().__init__({})
def parse_pq_names(self, cfg): if "dataset" in cfg: raise ConfigException( f"The 'dataset' entry in the flags section is no longer supported. Please refer to the documentation for the correct format (layer {self.name})" ) if "product" in cfg: pq_names = (cfg["product"], ) else: pq_names = (self.product_name, ) if "low_res_product" in cfg: pq_low_res_names = (cfg.get("low_res_product"), ) else: pq_low_res_names = self.low_res_product_names if "products" in cfg: raise ConfigException( f"'products' entry in flags section of non-multi-product layer {self.name} - use 'product' only" ) if "low_res_products" in cfg: raise ConfigException( f"'low_res_products' entry in flags section of non-multi-product layer {self.name}- use 'low_res_product' only" ) return { "pq_names": pq_names, "pq_low_res_names": pq_low_res_names, }
def parse_wcs(self, cfg): if self.wcs: if not isinstance(cfg, Mapping): raise ConfigException("WCS section missing (and WCS is enabled)") self.default_geographic_CRS = cfg.get("default_geographic_CRS") if self.default_geographic_CRS not in self.published_CRSs: raise ConfigException("Configured default geographic CRS not listed in published CRSs.") if not self.published_CRSs[self.default_geographic_CRS]["geographic"]: raise ConfigException("Configured default geographic CRS not listed in published CRSs as geographic.") self.default_geographic_CRS_def = self.published_CRSs[self.default_geographic_CRS] self.wcs_formats = {} for fmt_name, fmt in cfg["formats"].items(): self.wcs_formats[fmt_name] = { "mime": fmt["mime"], "extension": fmt["extension"], "multi-time": fmt["multi-time"], "name": fmt_name, } self.wcs_formats[fmt_name]["renderer"] = get_function(fmt["renderer"]) if not self.wcs_formats: raise ConfigException("Must configure at least one wcs format to support WCS.") self.native_wcs_format = cfg["native_format"] if self.native_wcs_format not in self.wcs_formats: raise Exception("Configured native WCS format not a supported format.") else: self.default_geographic_CRS = None self.default_geographic_CRS_def = None self.wcs_formats = {} self.native_wcs_format = None
def parse_pq_names(self, cfg): # pylint: disable=attribute-defined-outside-init if "dataset" in cfg: self.pq_name = cfg["dataset"] print("CFG WARNING:", "The preferred name for the 'dataset' entry", "in the flags section is now 'product'.", "Please update the configuration for layer", self.name) elif "product" in cfg: self.pq_name = cfg["product"] else: self.pq_name = self.product_name self.pq_names = [self.pq_name] if "low_res_product" in cfg: self.pq_low_res_name = cfg.get("low_res_product") self.pq_low_res_names = [self.pq_low_res_name] else: self.pq_low_res_names = self.low_res_product_names if "products" in cfg: raise ConfigException( f"'products' entry in flags section of non-multi-product layer {self.name} - use 'product' only" ) if "low_res_products" in cfg: raise ConfigException( f"'low_res_products' entry in flags section of non-multi-product layer {self.name}- use 'low_res_product' only" )
def parse_flags(self, cfg, dc): if cfg: self.parse_pq_names(cfg) self.pq_band = cfg["band"] if "fuse_func" in cfg: self.pq_fuse_func = FunctionWrapper(self, cfg["fuse_func"]) else: self.pq_fuse_func = None self.pq_ignore_time = cfg.get("ignore_time", False) self.ignore_info_flags = cfg.get("ignore_info_flags", []) self.pq_manual_merge = cfg.get("manual_merge", False) else: self.pq_names = [] self.pq_name = None self.pq_band = None self.pq_ignore_time = False self.ignore_info_flags = [] self.pq_manual_merge = False self.pq_products = [] if self.pq_names: for pqn in self.pq_names: if pqn is not None: pq_product = dc.index.products.get_by_name(pqn) if pq_product is None: raise ConfigException( "Could not find pq_product %s for %s in database" % (pqn, self.name)) self.pq_products.append(pq_product) self.info_mask = ~0 if self.pq_products: self.pq_product = self.pq_products[0] meas = self.pq_product.lookup_measurements([self.pq_band]) self.flags_def = meas[self.pq_band]["flags_definition"] for bitname in self.ignore_info_flags: bit = self.flags_def[bitname]["bits"] if not isinstance(bit, int): continue flag = 1 << bit self.info_mask &= ~flag else: self.pq_product = None
def parse_global(self, cfg): self._response_headers = cfg.get("response_headers", {}) self.wms = cfg.get("services", {}).get("wms", True) self.wmts = cfg.get("services", {}).get("wmts", True) self.wcs = cfg.get("services", {}).get("wcs", False) if not self.wms and not self.wmts and not self.wcs: raise ConfigException("At least one service must be active.") self.title = cfg["title"] self.allowed_urls = cfg["allowed_urls"] self.info_url = cfg["info_url"] self.abstract = cfg.get("abstract") self.contact_info = cfg.get("contact_info", {}) self.keywords = cfg.get("keywords", []) self.fees = cfg.get("fees") self.access_constraints = cfg.get("access_constraints") # self.use_extent_views = cfg.get("use_extent_views", False) if not self.fees: self.fees = "none" if not self.access_constraints: self.access_constraints = "none" self.published_CRSs = {} for crs_str, crsdef in cfg["published_CRSs"].items(): if crs_str.startswith("EPSG:"): gml_name = "http://www.opengis.net/def/crs/EPSG/0/" + crs_str[ 5:] else: gml_name = crs_str self.published_CRSs[crs_str] = { "geographic": crsdef["geographic"], "horizontal_coord": crsdef.get("horizontal_coord", "longitude"), "vertical_coord": crsdef.get("vertical_coord", "latitude"), "vertical_coord_first": crsdef.get("vertical_coord_first", False), "gml_name": gml_name } if self.published_CRSs[crs_str]["geographic"]: if self.published_CRSs[crs_str][ "horizontal_coord"] != "longitude": raise Exception( "Published CRS {} is geographic" "but has a horizontal coordinate that is not 'longitude'" .format(crs_str)) if self.published_CRSs[crs_str]["vertical_coord"] != "latitude": raise Exception( "Published CRS {} is geographic" "but has a vertical coordinate that is not 'latitude'". format(crs_str))
def parse_wms(self, cfg): if not self.wms and not self.wmts: cfg = {} self.s3_bucket = cfg.get("s3_bucket", "") self.s3_url = cfg.get("s3_url", "") self.s3_aws_zone = cfg.get("s3_aws_zone", "") self.wms_max_width = cfg.get("max_width", 256) self.wms_max_height = cfg.get("max_height", 256) self.attribution = AttributionCfg.parse(cfg.get("attribution")) self.authorities = cfg.get("authorities", {})
def parse_image_processing(self, cfg): emf_cfg = cfg["extent_mask_func"] if isinstance(emf_cfg, Mapping) or isinstance(emf_cfg, str): self.extent_mask_func = [FunctionWrapper(self, emf_cfg)] else: self.extent_mask_func = list( [FunctionWrapper(self, emf) for emf in emf_cfg]) self.raw_afb = cfg.get("always_fetch_bands", []) self.declare_unready("always_fetch_bands") self.solar_correction = cfg.get("apply_solar_corrections", False) self.data_manual_merge = cfg.get("manual_merge", False) if self.solar_correction and not self.data_manual_merge: raise ConfigException("Solar correction requires manual_merge.") if self.data_manual_merge and not self.solar_correction: _LOG.warning( "Manual merge is only recommended where solar correction is required." ) if cfg.get("fuse_func"): self.fuse_func = FunctionWrapper(self, cfg["fuse_func"]) else: self.fuse_func = None
def __init__(self, cfg): super().__init__(cfg) self.title = cfg.get("title") self.url = cfg.get("url") logo = cfg.get("logo") if not self.title and not self.url and not logo: raise ConfigException( "At least one of title, url and logo is required in an attribution definition" ) if not logo: self.logo_width = None self.logo_height = None self.logo_url = None self.logo_fmt = None else: self.logo_width = logo.get("width") self.logo_height = logo.get("height") self.logo_url = logo.get("url") self.logo_fmt = logo.get("format") if not self.logo_url or not self.logo_fmt: raise ConfigException( "url and format must both be specified in an attribution logo." )
def parse_product_names(self, cfg): self.product_names = cfg["product_names"] self.product_name = self.product_names[0] self.low_res_product_names = cfg.get("low_res_product_names", []) if self.low_res_product_names: self.low_res_product_name = self.low_res_product_names[0] else: self.low_res_product_name = None if "product_name" in cfg: raise ConfigException( f"'product_name' entry in multi-product layer {self.name} - use 'product_names' only" ) if "low_res_product_name" in cfg: raise ConfigException( f"'low_res_product_name' entry in multi-product layer {self.name} - use 'low_res_product_names' only" )
def parse_product_names(self, cfg): self.product_name = cfg["product_name"] self.product_names = (self.product_name, ) self.low_res_product_name = cfg.get("low_res_product_name") if self.low_res_product_name: self.low_res_product_names = (self.low_res_product_name, ) else: self.low_res_product_names = tuple() if "product_names" in cfg: raise ConfigException( f"'product_names' entry in non-multi-product layer {self.name} - use 'product_name' only" ) if "low_res_product_names" in cfg: raise ConfigException( f"'low_res_product_names' entry in non-multi-product layer {self.name} - use 'low_res_product_name' only" )
def parse_wcs(self, cfg, dc): if cfg is None: self.wcs = False return else: self.wcs = True # Native CRS try: self.native_CRS = self.product.definition["storage"]["crs"] except KeyError: self.native_CRS = None if not self.native_CRS: self.native_CRS = cfg.get("native_crs") if not self.native_CRS: raise ConfigException("No native CRS could be found for layer %s" % self.name) if self.native_CRS not in self.global_cfg.published_CRSs: raise ConfigException("Native CRS for product %s (%s) not in published CRSs" % ( self.product_name, self.native_CRS)) self.native_CRS_def = self.global_cfg.published_CRSs[self.native_CRS] # Prepare Rectified Grid native_bounding_box = self.bboxes[self.native_CRS] self.origin_x = native_bounding_box["left"] self.origin_y = native_bounding_box["bottom"] try: self.resolution_x, self.resolution_y = cfg["native_resolution"] except KeyError: raise ConfigException("No native resolution supplied for WCS enabled layer %s" % self.name) except ValueError: raise ConfigException("Invalid native resolution supplied for WCS enabled layer %s" % self.name) except TypeError: raise ConfigException("Invalid native resolution supplied for WCS enabled layer %s" % self.name) self.grid_high_x = int((native_bounding_box["right"] - native_bounding_box["left"]) / self.resolution_x) self.grid_high_y = int((native_bounding_box["top"] - native_bounding_box["bottom"]) / self.resolution_y) # Band management self.wcs_default_bands = [self.band_idx.band(b) for b in cfg["default_bands"]] # Cache some metadata from the datacube bands = dc.list_measurements().loc[self.product_name] self.bands = bands.index.values self.nodata_values = bands['nodata'].values self.nodata_dict = {a: b for a, b in zip(self.bands, self.nodata_values)}
def __init__(self, cfg, global_cfg, dc, parent_layer=None): super().__init__(cfg, global_cfg, dc, parent_layer) self.name = cfg["name"] self.hide = False try: self.parse_product_names(cfg) self.products = [] for prod_name in self.product_names: if "__" in prod_name: raise ConfigException( "Product names cannot contain a double underscore '__'." ) product = dc.index.products.get_by_name(prod_name) if not product: raise ConfigException( "Could not find product %s in datacube" % prod_name) self.products.append(product) self.product = self.products[0] self.definition = self.product.definition self.time_resolution = cfg.get("time_resolution", TIMERES_RAW) if self.time_resolution not in TIMERES_VALS: raise ConfigException( "Invalid time resolution value %s in named layer %s" % (self.time_resolution, self.name)) except KeyError: raise ConfigException( "Required product names entry missing in named layer %s" % self.name) self.dynamic = cfg.get("dynamic", False) self.force_range_update(dc) # TODO: sub-ranges self.band_idx = BandIndex(self.product, cfg.get("bands"), dc) try: self.parse_resource_limits(cfg.get("resource_limits", {})) except KeyError: raise ConfigException( "Missing required config items in resource limits for layer %s" % self.name) try: self.parse_flags(cfg.get("flags", {}), dc) except KeyError: raise ConfigException( "Missing required config items in flags section for layer %s" % self.name) try: self.parse_image_processing(cfg["image_processing"]) except KeyError: raise ConfigException( "Missing required config items in image processing section for layer %s" % self.name) self.identifiers = cfg.get("identifiers", {}) for auth in self.identifiers.keys(): if auth not in self.global_cfg.authorities: raise ConfigException( "Identifier with non-declared authority: %s" % repr(auth)) try: self.parse_urls(cfg.get("urls", {})) except KeyError: raise ConfigException( "Missing required config items in urls section for layer %s" % self.name) self.parse_feature_info(cfg.get("feature_info", {})) self.feature_info_include_utc_dates = cfg.get("feature_info_url_dates", False) try: self.parse_styling(cfg["styling"]) except KeyError: raise ConfigException( "Missing required config items in styling section for layer %s" % self.name) if self.global_cfg.wcs: try: self.parse_wcs(cfg.get("wcs"), dc) except KeyError: raise ConfigException( "Missing required config items in wcs section for layer %s" % self.name) sub_prod_cfg = cfg.get("sub_products", {}) self.sub_product_label = sub_prod_cfg.get("label") if "extractor" in sub_prod_cfg: self.sub_product_extractor = FunctionWrapper( self, sub_prod_cfg["extractor"]) else: self.sub_product_extractor = None # And finally, add to the global product index. self.global_cfg.product_index[self.name] = self if not self.multi_product: self.global_cfg.native_product_index[self.product_name] = self
def parse_urls(self, cfg): self.feature_list_urls = SuppURL.parse_list(cfg.get("features", [])) self.data_urls = SuppURL.parse_list(cfg.get("data", []))
def parse_wcs(self, cfg, dc): if cfg is None: self.wcs = False return else: self.wcs = True # Native CRS try: self.native_CRS = self.product.definition["storage"]["crs"] except KeyError: self.native_CRS = None if not self.native_CRS: self.native_CRS = cfg.get("native_crs") elif cfg.get("native_crs") == self.native_CRS: _LOG.debug( "Native crs for layer %s is specified in ODC metadata and does not need to be specified in configuration", self.name) elif "native_crs" in cfg: _LOG.warning( "Native crs for layer %s is specified in config as %s - overridden to %s by ODC metadata", self.name, cfg['native_crs'], self.native_CRS) if not self.native_CRS: raise ConfigException( f"No native CRS could be found for layer {self.name}") if self.native_CRS not in self.global_cfg.published_CRSs: raise ConfigException( "Native CRS for product %s (%s) not in published CRSs" % (self.product_name, self.native_CRS)) self.native_CRS_def = self.global_cfg.published_CRSs[self.native_CRS] # Prepare Rectified Grids try: native_bounding_box = self.bboxes[self.native_CRS] except KeyError: _LOG.warning( "Layer: %s No bounding box in ranges for native CRS %s - rerun update_ranges.py", self.name, self.native_CRS) self.hide = True return self.origin_x = native_bounding_box["left"] self.origin_y = native_bounding_box["bottom"] try: self.resolution_x = self.product.definition["storage"][ "resolution"][self.native_CRS_def["horizontal_coord"]] self.resolution_y = self.product.definition["storage"][ "resolution"][self.native_CRS_def["vertical_coord"]] except KeyError: self.resolution_x = None self.resolution_y = None if self.resolution_x is None: try: self.resolution_x, self.resolution_y = cfg["native_resolution"] except KeyError: raise ConfigException( f"No native resolution supplied for WCS enabled layer {self.name}" ) except ValueError: raise ConfigException( f"Invalid native resolution supplied for WCS enabled layer {self.name}" ) except TypeError: raise ConfigException( f"Invalid native resolution supplied for WCS enabled layer {self.name}" ) elif "native_resolution" in cfg: config_x, config_y = cfg["native_resolution"] if (math.isclose(config_x, self.resolution_x, rel_tol=1e-10) and math.isclose(config_y, self.resolution_y, rel_tol=1e-10)): _LOG.debug( "Native resolution for layer %s is specified in ODC metadata and does not need to be specified in configuration", self.name) else: _LOG.warning( "Native resolution for layer %s is specified in config as %s - overridden to (%.f, %.f) by ODC metadata", self.name, repr(cfg['native_resolution']), self.resolution_x, self.resolution_y) if (native_bounding_box["right"] - native_bounding_box["left"]) < self.resolution_x: ConfigException( "Native (%s) bounding box on layer %s has left %f, right %f (diff %d), but horizontal resolution is %f" % (self.native_CRS, self.name, native_bounding_box["left"], native_bounding_box["right"], native_bounding_box["right"] - native_bounding_box["left"], self.resolution_x)) if (native_bounding_box["top"] - native_bounding_box["bottom"]) < self.resolution_x: ConfigException( "Native (%s) bounding box on layer %s has bottom %f, top %f (diff %d), but vertical resolution is %f" % (self.native_CRS, self.name, native_bounding_box["bottom"], native_bounding_box["top"], native_bounding_box["top"] - native_bounding_box["bottom"], self.resolution_y)) self.grid_high_x = int( (native_bounding_box["right"] - native_bounding_box["left"]) / self.resolution_x) self.grid_high_y = int( (native_bounding_box["top"] - native_bounding_box["bottom"]) / self.resolution_y) if self.grid_high_x == 0: err_str = f"Grid High X is zero on layer {self.name}: native ({self.native_CRS}) extent: {native_bounding_box['left']},{native_bounding_box['right']}: x_res={self.resolution_x}" raise ConfigException(err_str) if self.grid_high_y == 0: err_str = f"Grid High y is zero on layer {self.name}: native ({self.native_CRS}) extent: {native_bounding_box['bottom']},{native_bounding_box['top']}: x_res={self.resolution_y}" raise ConfigException(err_str) self.grids = {} for crs, crs_def in self.global_cfg.published_CRSs.items(): if crs == self.native_CRS: self.grids[crs] = { "origin": (self.origin_x, self.origin_y), "resolution": (self.resolution_x, self.resolution_y), } else: try: bbox = self.bboxes[crs] except KeyError: continue self.grids[crs] = { "origin": (bbox["left"], bbox["bottom"]), "resolution": ((bbox["right"] - bbox["left"]) / self.grid_high_x, (bbox["top"] - bbox["bottom"]) / self.grid_high_y) } # Band management self.wcs_default_bands = [ self.band_idx.band(b) for b in cfg["default_bands"] ] # Cache some metadata from the datacube try: bands = dc.list_measurements().loc[self.product_name] except KeyError: raise ConfigException( "Datacube.list_measurements() not returning measurements for product %s" % self.product_name) self.bands = bands.index.values try: self.nodata_values = bands['nodata'].values except KeyError: raise ConfigException( "Datacube has no 'nodata' values for bands in product %s" % self.product_name) self.nodata_dict = { a: b for a, b in zip(self.bands, self.nodata_values) } # Native format if "native_format" in cfg: self.native_format = cfg["native_format"] if self.native_format not in self.global_cfg.wcs_formats_by_name: raise ConfigException( "WCS native format for layer %s is not in supported formats list" % self.product_name) else: self.native_format = self.global_cfg.native_wcs_format
def parse_global(self, cfg): self._response_headers = cfg.get("response_headers", {}) self.wms = cfg.get("services", {}).get("wms", True) self.wmts = cfg.get("services", {}).get("wmts", True) self.wcs = cfg.get("services", {}).get("wcs", False) if not self.wms and not self.wmts and not self.wcs: raise ConfigException("At least one service must be active.") self.title = cfg["title"] self.allowed_urls = cfg["allowed_urls"] self.info_url = cfg["info_url"] self.abstract = cfg.get("abstract") self.contact_info = cfg.get("contact_info", {}) self.keywords = cfg.get("keywords", []) self.fees = cfg.get("fees") self.access_constraints = cfg.get("access_constraints") # self.use_extent_views = cfg.get("use_extent_views", False) if not self.fees: self.fees = "none" if not self.access_constraints: self.access_constraints = "none" def make_gml_name(name): if name.startswith("EPSG:"): return f"http://www.opengis.net/def/crs/EPSG/0/{name[5:]}" else: return name self.published_CRSs = {} self.internal_CRSs = {} CRS_aliases = {} for crs_str, crsdef in cfg["published_CRSs"].items(): if "alias" in crsdef: CRS_aliases[crs_str] = crsdef continue self.internal_CRSs[crs_str] = { "geographic": crsdef["geographic"], "horizontal_coord": crsdef.get("horizontal_coord", "longitude"), "vertical_coord": crsdef.get("vertical_coord", "latitude"), "vertical_coord_first": crsdef.get("vertical_coord_first", False), "gml_name": make_gml_name(crs_str), "alias_of": None } self.published_CRSs[crs_str] = self.internal_CRSs[crs_str] if self.published_CRSs[crs_str]["geographic"]: if self.published_CRSs[crs_str][ "horizontal_coord"] != "longitude": raise ConfigException( f"Published CRS {crs_str} is geographic" "but has a horizontal coordinate that is not 'longitude'" ) if self.published_CRSs[crs_str]["vertical_coord"] != "latitude": raise ConfigException( f"Published CRS {crs_str} is geographic" "but has a vertical coordinate that is not 'latitude'") for alias, alias_def in CRS_aliases.items(): target_crs = alias_def["alias"] if target_crs not in self.published_CRSs: _LOG.warning( "CRS %s defined as alias for %s, which is not a published CRS - skipping", alias, target_crs) continue target_def = self.published_CRSs[target_crs] self.published_CRSs[alias] = target_def.copy() self.published_CRSs[alias]["gml_name"] = make_gml_name(alias) self.published_CRSs[alias]["alias_of"] = target_crs
def __init__(self, cfg, global_cfg, parent_layer=None, **kwargs): name = cfg["name"] super().__init__(cfg, global_cfg=global_cfg, parent_layer=parent_layer, keyvals={"layer": name}, **kwargs) self.name = name cfg = self._raw_cfg self.hide = False try: self.parse_product_names(cfg) if len(self.low_res_product_names) not in ( 0, len(self.product_names)): raise ConfigException( f"Lengths of product_names and low_res_product_names do not match in layer {self.name}" ) for prod_name in self.product_names: if "__" in prod_name: # I think this was for subproducts which are currently broken raise ConfigException( "Product names cannot contain a double underscore '__'." ) except IndexError: raise ConfigException(f"No products declared in layer {self.name}") except KeyError: raise ConfigException( "Required product names entry missing in named layer %s" % self.name) self.declare_unready("products") self.declare_unready("low_res_products") self.declare_unready("product") self.declare_unready("definition") self.time_resolution = cfg.get("time_resolution", TIMERES_RAW) if self.time_resolution not in TIMERES_VALS: raise ConfigException( "Invalid time resolution value %s in named layer %s" % (self.time_resolution, self.name)) self.dynamic = cfg.get("dynamic", False) self.declare_unready("_ranges") self.declare_unready("bboxes") # TODO: sub-ranges self.band_idx = BandIndex(self, cfg.get("bands")) self.parse_resource_limits(cfg.get("resource_limits", {})) try: self.parse_flags(cfg.get("flags", {})) self.declare_unready("all_flag_band_names") except KeyError as e: raise ConfigException( f"Missing required config ({str(e)}) in flags section for layer {self.name}" ) try: self.parse_image_processing(cfg["image_processing"]) except KeyError as e: raise ConfigException( f"Missing required config ({str(e)}) in image processing section for layer {self.name}" ) self.identifiers = cfg.get("identifiers", {}) for auth in self.identifiers.keys(): if auth not in self.global_cfg.authorities: raise ConfigException( f"Identifier with non-declared authority: {auth} in layer {self.name}" ) self.parse_urls(cfg.get("urls", {})) self.parse_feature_info(cfg.get("feature_info", {})) self.feature_info_include_utc_dates = cfg.get("feature_info_url_dates", False) try: self.parse_styling(cfg["styling"]) except KeyError as e: raise ConfigException( f"Missing required config item {e} in styling section for layer {self.name}" ) if self.global_cfg.wcs: try: self.parse_wcs(cfg.get("wcs")) except KeyError as e: raise ConfigException( f"Missing required config item {e} in wcs section for layer {self.name}" ) # Sub-products have been broken for some time. # sub_prod_cfg = cfg.get("sub_products", {}) # self.sub_product_label = sub_prod_cfg.get("label") # if "extractor" in sub_prod_cfg: # self.sub_product_extractor = FunctionWrapper(self, sub_prod_cfg["extractor"]) # else: # self.sub_product_extractor = None # And finally, add to the global product index. self.global_cfg.product_index[self.name] = self