def output(self): """Output object of driver.""" output_params = self._raw["output"] if "path" in output_params: output_params.update( path=os.path.normpath( os.path.join(self.config_dir, output_params["path"]))) output_params.update( type=self.output_pyramid.grid, pixelbuffer=self.output_pyramid.pixelbuffer, metatiling=self.output_pyramid.metatiling) if "format" not in output_params: raise MapcheteConfigError("output format not specified") if output_params["format"] not in available_output_formats(): raise MapcheteConfigError( "format %s not available in %s" % ( output_params["format"], str(available_output_formats()))) writer = load_output_writer(output_params) try: writer.is_valid_with_config(output_params) except Exception as e: logger.exception(e) raise MapcheteConfigError( "driver %s not compatible with configuration: %s" % ( writer.METADATA["driver_name"], e)) return writer
def _validate_zooms(zooms): """ Return a list of zoom levels. Following inputs are converted: - int --> [int] - dict{min, max} --> range(min, max + 1) - [int] --> [int] - [int, int] --> range(smaller int, bigger int + 1) """ if isinstance(zooms, dict): if any([a not in zooms for a in ["min", "max"]]): raise MapcheteConfigError("min and max zoom required") zmin = _validate_zoom(zooms["min"]) zmax = _validate_zoom(zooms["max"]) if zmin > zmax: raise MapcheteConfigError( "max zoom must not be smaller than min zoom") return range(zmin, zmax + 1) elif isinstance(zooms, list): if len(zooms) == 1: return zooms elif len(zooms) == 2: zmin, zmax = sorted([_validate_zoom(z) for z in zooms]) return range(zmin, zmax + 1) else: raise MapcheteConfigError( "when providing zooms as list, just min and max are allowed") else: return [_validate_zoom(zooms)]
def zoom_levels(self): """Determine valid process zoom levels.""" # Read from raw configuration. if "process_zoom" in self.raw: zoom = [self.raw["process_zoom"]] elif all(k in self.raw for k in ("process_minzoom", "process_maxzoom")): zoom = [self.raw["process_minzoom"], self.raw["process_maxzoom"]] else: zoom = [] # overwrite zoom if provided in additional_parameters zoom = self._delimiters["zoom"] if self._delimiters["zoom"] else zoom # # if zoom still empty, throw exception if not zoom: raise MapcheteConfigError("No zoom level(s) provided.") zoom = [zoom] if isinstance(zoom, int) else zoom if len(zoom) == 1: if zoom[0] < 0: raise MapcheteConfigError("Zoom level must be greater 0.") return zoom elif len(zoom) == 2: for i in zoom: if i < 0: raise MapcheteConfigError("Zoom levels must be greater 0.") if zoom[0] < zoom[1]: return range(zoom[0], zoom[1] + 1) else: return range(zoom[1], zoom[0] + 1) else: raise MapcheteConfigError( "Zoom level parameter requires one or two value(s).")
def baselevels(self): """ Optional baselevels configuration. baselevels: min: <zoom> max: <zoom> lower: <resampling method> higher: <resampling method> """ if "baselevels" not in self._raw: return {} baselevels = self._raw["baselevels"] minmax = { k: v for k, v in six.iteritems(baselevels) if k in ["min", "max"]} if not minmax: raise MapcheteConfigError( "no min and max values given for baselevels") for v in minmax.values(): if not isinstance(v, int) or v < 0: raise MapcheteConfigError( "invalid baselevel zoom parameter given: %s" % ( minmax.values())) return dict( zooms=range( minmax.get("min", min(self.zoom_levels)), minmax.get("max", max(self.zoom_levels)) + 1), lower=baselevels.get("lower", "nearest"), higher=baselevels.get("higher", "nearest"), tile_pyramid=BufferedTilePyramid( self.output_pyramid.grid, pixelbuffer=self.output_pyramid.pixelbuffer, metatiling=self.process_pyramid.metatiling))
def write_output_metadata(output_params): """Dump output JSON and verify parameters if output metadata exist.""" if "path" in output_params: metadata_path = os.path.join(output_params["path"], "metadata.json") logger.debug("check for output %s", metadata_path) try: existing_params = read_output_metadata(metadata_path) logger.debug("%s exists", metadata_path) logger.debug("existing output parameters: %s", pformat(existing_params)) existing_tp = existing_params["pyramid"] current_params = params_to_dump(output_params) logger.debug("current output parameters: %s", pformat(current_params)) current_tp = BufferedTilePyramid(**current_params["pyramid"]) if existing_tp != current_tp: # pragma: no cover raise MapcheteConfigError( "pyramid definitions between existing and new output do not match: " "%s != %s" % (existing_tp, current_tp)) existing_format = existing_params["driver"]["format"] current_format = current_params["driver"]["format"] if existing_format != current_format: # pragma: no cover raise MapcheteConfigError( "existing output format does not match new output format: " "%s != %s" % ((existing_format, current_format))) except FileNotFoundError: logger.debug("%s does not exist", metadata_path) dump_params = params_to_dump(output_params) # dump output metadata write_json(metadata_path, dump_params)
def output(self): """Output object of driver.""" output_params = self.raw["output"] if "format" not in output_params: raise MapcheteConfigError("output format not specified") if output_params["format"] not in available_output_formats(): raise MapcheteConfigError( "format %s not available in %s" % (output_params["format"], str(available_output_formats()))) writer = load_output_writer(output_params) if not writer.is_valid_with_config(output_params): raise MapcheteConfigError( "driver %s not compatible with configuration: %s" % (writer.METADATA["driver_name"], output_params)) return writer
def _load_process_module(process_path=None, config_dir=None, run_compile=False): if process_path.endswith(".py"): module_path = os.path.join(config_dir, process_path) if not os.path.isfile(module_path): raise MapcheteConfigError(f"{module_path} is not available") try: if run_compile: py_compile.compile(module_path, doraise=True) module_name = os.path.splitext(os.path.basename(module_path))[0] # load module spec = importlib.util.spec_from_file_location( module_name, module_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) # required to make imported module available using multiprocessing sys.modules[module_name] = module # configure process file logger add_module_logger(module.__name__) except py_compile.PyCompileError as e: raise MapcheteProcessSyntaxError(e) except ImportError as e: raise MapcheteProcessImportError(e) else: try: module = importlib.import_module(process_path) except ImportError as e: raise MapcheteProcessImportError(e) return module
def _config_to_dict(input_config): if isinstance(input_config, dict): if "config_dir" not in input_config: raise MapcheteConfigError("config_dir parameter missing") return dict(input_config, mapchete_file=None) # from Mapchete file elif os.path.splitext(input_config)[1] == ".mapchete": with open(input_config, "r") as config_file: return dict( yaml.safe_load(config_file.read()), config_dir=os.path.dirname(os.path.realpath(input_config)), mapchete_file=input_config) # throw error if unknown object else: raise MapcheteConfigError( "Configuration has to be a dictionary or a .mapchete file.")
def _strip_zoom(input_string, strip_string): """Return zoom level as integer or throws error.""" try: element_zoom = input_string.strip(strip_string) return int(element_zoom) except Exception: raise MapcheteConfigError("zoom level could not be determined")
def process_file(self): """Absolute path of process file.""" abs_path = os.path.join(self.config_dir, self.raw["process_file"]) if os.path.isfile(abs_path): return abs_path else: raise MapcheteConfigError("%s is not available" % abs_path)
def prepare(self, process_area=None, **kwargs): bounds = snap_bounds( bounds=Bounds(*process_area.intersection( box(*self.output_params["delimiters"] ["effective_bounds"])).bounds), pyramid=self.pyramid, zoom=self.zoom) if process_area else self.output_params[ "delimiters"]["effective_bounds"] height = math.ceil((bounds.top - bounds.bottom) / self.pyramid.pixel_x_size(self.zoom)) width = math.ceil((bounds.right - bounds.left) / self.pyramid.pixel_x_size(self.zoom)) logger.debug("output raster bounds: %s", bounds) logger.debug("output raster shape: %s, %s", height, width) self._profile = dict( GTIFF_DEFAULT_PROFILE, driver="GTiff", transform=Affine(self.pyramid.pixel_x_size(self.zoom), 0, bounds.left, 0, -self.pyramid.pixel_y_size(self.zoom), bounds.top), height=height, width=width, count=self.output_params["bands"], crs=self.pyramid.crs, **{ k: self.output_params.get(k, GTIFF_DEFAULT_PROFILE[k]) for k in GTIFF_DEFAULT_PROFILE.keys() }, bigtiff=self.output_params.get("bigtiff", "NO")) logger.debug("single GTiff profile: %s", self._profile) self.in_memory = (self.in_memory if self.in_memory is False else height * width < IN_MEMORY_THRESHOLD) # set up rasterio if path_exists(self.path): if self.output_params["mode"] != "overwrite": raise MapcheteConfigError( "single GTiff file already exists, use overwrite mode to replace" ) else: logger.debug("remove existing file: %s", self.path) os.remove(self.path) # create output directory if necessary makedirs(os.path.dirname(self.path)) logger.debug("open output file: %s", self.path) self._ctx = ExitStack() # (1) use memfile if output is remote or COG if self.cog or path_is_remote(self.path): if self.in_memory: self._memfile = self._ctx.enter_context(MemoryFile()) self.dst = self._ctx.enter_context( self._memfile.open(**self._profile)) else: # in case output raster is too big, use tempfile on disk self._tempfile = self._ctx.enter_context(NamedTemporaryFile()) self.dst = self._ctx.enter_context( rasterio.open(self._tempfile.name, "w+", **self._profile)) else: self.dst = self._ctx.enter_context( rasterio.open(self.path, "w+", **self._profile))
def __init__(self, input_config, zoom=None, bounds=None, single_input_file=None, mode="continue", debug=False): """Initialize configuration.""" LOGGER.info("preparing configuration ...") if debug: LOGGER.setLevel(logging.DEBUG) if mode not in ["memory", "readonly", "continue", "overwrite"]: raise MapcheteConfigError("invalid process mode") LOGGER.debug("zooms provided to config: %s" % zoom) self.mode = mode # parse configuration LOGGER.debug("parse configuration ...") self._input_cache = {} self._process_area_cache = {} self.raw, self.mapchete_file, self.config_dir = self._parse_config( input_config, single_input_file=single_input_file) if not self.process_file: raise MapcheteConfigError("no process_file given") # set process delimiters self._delimiters = dict(zoom=zoom, bounds=bounds) # helper caches self._at_zoom_cache = {} self._global_process_area = None # other properties try: self.output_type = self.raw["output"]["type"] except KeyError: raise MapcheteConfigError("no output type given") if self.raw["output"]["type"] not in TILING_TYPES: raise MapcheteConfigError("output type is missing: %s" % PYRAMID_PARAMS.keys()) self.process_pyramid = BufferedTilePyramid( self.output_type, metatiling=self.metatiling, pixelbuffer=self.pixelbuffer) self.output_pyramid = BufferedTilePyramid( self.output_type, metatiling=self.raw["output"]["metatiling"], pixelbuffer=self.raw["output"]["pixelbuffer"]) self.crs = self.process_pyramid.crs LOGGER.debug("validate ...") self._validate()
def _validate_bounds(bounds): if any([ not isinstance(bounds, (list, tuple)), len(bounds) != 4, any([not isinstance(i, (int, float)) for i in bounds]) ]): raise MapcheteConfigError("bounds not valid") return bounds
def _validate_process_file(config): abs_path = os.path.join(config["config_dir"], config["process_file"]) if not os.path.isfile(abs_path): raise MapcheteConfigError("%s is not available" % abs_path) try: py_compile.compile(abs_path, doraise=True) except py_compile.PyCompileError as e: raise MapcheteProcessSyntaxError(e) return abs_path
def process_func(self): """Import process function and make syntax check.""" if self.mode == "readonly": raise MapcheteConfigError( "process function cannot be loaded in readonly mode.") else: return get_process_func(process_path=self.process_path, config_dir=self.config_dir, run_compile=True)
def bounds(self): """Process bounds as defined in the configuration.""" if self._raw["bounds"] is None: return self.process_pyramid.bounds else: try: return validate_bounds(self._raw["bounds"]) except Exception as e: raise MapcheteConfigError(e)
def _get_process_area(self, area=None, bounds=None, area_fallback=None, bounds_fallback=None, area_crs=None, bounds_crs=None): """ Determine process area by combining configuration with instantiation arguments. In the configuration the process area can be provided by using the (1) ``area`` option, (2) ``bounds`` option or (3) a combination of both. (1) If only ``area`` is provided, output shall be the area geometry (2) If only ``bounds`` is provided, output shall be box(*self.bounds) (3) If both are provided, output shall be the intersection between ``area`` and ``bounds`` The area parameter can be provided in multiple variations, see _guess_geometry(). """ try: dst_crs = self.process_pyramid.crs if bounds is None and area is None: return area_fallback elif bounds is None: area, crs = _guess_geometry(area, base_dir=self.config_dir) # in case vector file has no CRS use manually provided CRS area_crs = crs or area_crs return reproject_geometry(area, src_crs=area_crs or dst_crs, dst_crs=dst_crs) elif area is None: return reproject_geometry(box(*validate_bounds(bounds)), src_crs=bounds_crs or dst_crs, dst_crs=dst_crs) else: area, crs = _guess_geometry(area, base_dir=self.config_dir) # in case vector file has no CRS use manually provided CRS area_crs = crs or area_crs bounds = validate_bounds(bounds) # reproject area and bounds to process CRS and return intersection return reproject_geometry( area, src_crs=area_crs or dst_crs, dst_crs=dst_crs).intersection( reproject_geometry(box(*validate_bounds(bounds)), src_crs=bounds_crs or dst_crs, dst_crs=dst_crs), ) except Exception as e: raise MapcheteConfigError(e)
def output(self): """Output writer class of driver.""" writer = load_output_writer(self._output_params) try: writer.is_valid_with_config(self._output_params) except Exception as e: logger.exception(e) raise MapcheteConfigError( "driver %s not compatible with configuration: %s" % (writer.METADATA["driver_name"], e)) return writer
def get_zoom_levels(process_zoom_levels=None, init_zoom_levels=None): """Validate and return zoom levels.""" process_zoom_levels = _validate_zooms(process_zoom_levels) if init_zoom_levels is None: return process_zoom_levels else: init_zoom_levels = _validate_zooms(init_zoom_levels) if not set(init_zoom_levels).issubset(set(process_zoom_levels)): raise MapcheteConfigError( "init zooms must be a subset of process zoom") return init_zoom_levels
def _output_params(self): """Output params of driver.""" output_params = dict(self._raw["output"], grid=self.output_pyramid.grid, pixelbuffer=self.output_pyramid.pixelbuffer, metatiling=self.output_pyramid.metatiling, delimiters=self._delimiters, mode=self.mode) if "path" in output_params: output_params.update(path=absolute_path(path=output_params["path"], base_dir=self.config_dir)) if "format" not in output_params: raise MapcheteConfigError("output format not specified") if output_params["format"] not in available_output_formats(): raise MapcheteConfigError( "format %s not available in %s" % (output_params["format"], str(available_output_formats()))) return output_params
def _map_to_new_config(config): if "pyramid" not in config: warnings.warn("'pyramid' needs to be defined in root config element.") config["pyramid"] = dict(grid=config["output"]["type"], metatiling=config.get("metatiling", 1), pixelbuffer=config.get("pixelbuffer", 0)) if "zoom_levels" not in config: warnings.warn( "use new config element 'zoom_levels' instead of 'process_zoom', " "'process_minzoom' and 'process_maxzoom'") if "process_zoom" in config: config["zoom_levels"] = config["process_zoom"] elif all([i in config for i in ["process_minzoom", "process_maxzoom"]]): config["zoom_levels"] = dict(min=config["process_minzoom"], max=config["process_maxzoom"]) else: raise MapcheteConfigError( "process zoom levels not provided in config") if "bounds" not in config: if "process_bounds" in config: warnings.warn( "'process_bounds' are deprecated and renamed to 'bounds'") config["bounds"] = config["process_bounds"] else: config["bounds"] = None if "input" not in config: if "input_files" in config: warnings.warn( "'input_files' are deprecated and renamed to 'input'") config["input"] = config["input_files"] else: raise MapcheteConfigError("no 'input' found") elif "input_files" in config: raise MapcheteConfigError( "'input' and 'input_files' are not allowed at the same time") if "process_file" in config: warnings.warn("'process_file' is deprecated and renamed to 'process'") config["process"] = config.pop("process_file") return config
def main(args=None): """Execute a Mapchete process.""" parsed = args multi = parsed.multi if parsed.multi else cpu_count() mode = "overwrite" if parsed.overwrite else "continue" # process single tile if parsed.tile: conf = yaml.load(open(parsed.mapchete_file, "r").read()) if "output" not in conf: raise MapcheteConfigError("output definition missing") if "pyramid" not in conf or "grid" not in conf["pyramid"]: raise MapcheteConfigError("pyramid definition missing") tile = BufferedTilePyramid( conf["pyramid"]["grid"], metatiling=conf["pyramid"].get("metatiling", 1), pixelbuffer=conf["pyramid"].get("pixelbuffer", 0)).tile(*parsed.tile) with mapchete.open(parsed.mapchete_file, mode=mode, bounds=tile.bounds, zoom=tile.zoom, single_input_file=parsed.input_file, debug=parsed.debug) as mp: mp.batch_process(tile=parsed.tile, quiet=parsed.quiet, debug=parsed.debug, logfile=parsed.logfile) # initialize and run process else: with mapchete.open(parsed.mapchete_file, bounds=parsed.bounds, zoom=parsed.zoom, mode=mode, single_input_file=parsed.input_file, debug=parsed.debug) as mp: mp.batch_process(multi=multi, quiet=parsed.quiet, debug=parsed.debug, zoom=parsed.zoom, logfile=parsed.logfile)
def _validate_process_file(config): abs_path = os.path.join(config["config_dir"], config["process_file"]) if not os.path.isfile(abs_path): raise MapcheteConfigError("%s is not available" % abs_path) try: py_compile.compile(abs_path, doraise=True) imp.load_source(os.path.splitext(os.path.basename(abs_path))[0], abs_path) except py_compile.PyCompileError as e: raise MapcheteProcessSyntaxError(e) except ImportError as e: raise MapcheteProcessImportError(e) return abs_path
def baselevels(self): """ Optional baselevels configuration. baselevels: min: <zoom> max: <zoom> lower: <resampling method> higher: <resampling method> """ try: baselevels = self.raw["baselevels"] except KeyError: return {} minmax = { k: v for k, v in baselevels.iteritems() if k in ["min", "max"] } if not minmax: raise MapcheteConfigError( "no min and max values given for baselevels") for v in minmax.values(): if v < 0 or not isinstance(v, int): raise MapcheteConfigError( "invalid baselevel zoom parameter given: %s" % (minmax.values())) base_min = minmax["min"] if "min" in minmax else min(self.zoom_levels) base_max = minmax["max"] if "max" in minmax else max(self.zoom_levels) resampling_lower = (baselevels["lower"] if "lower" in baselevels else "nearest") resampling_higher = (baselevels["higher"] if "higher" in baselevels else "nearest") return dict(zooms=range(base_min, base_max + 1), lower=resampling_lower, higher=resampling_higher, tile_pyramid=BufferedTilePyramid( self.output_pyramid.type, pixelbuffer=self.output_pyramid.pixelbuffer, metatiling=self.process_pyramid.metatiling))
def init_zoom_levels(self): """ Zoom levels this process is currently initialized with. This gets triggered by using the ``zoom`` kwarg. If not set, it will be equal to self.zoom_levels. """ try: return get_zoom_levels( process_zoom_levels=self._raw["zoom_levels"], init_zoom_levels=self._raw["init_zoom_levels"]) except Exception as e: raise MapcheteConfigError(e)
def init_bounds(self): """ Process bounds this process is currently initialized with. This gets triggered by using the ``init_bounds`` kwarg. If not set, it will be equal to self.bounds. """ if self._raw["init_bounds"] is None: return self.bounds else: try: return validate_bounds(self._raw["init_bounds"]) except Exception as e: raise MapcheteConfigError(e)
def init_zoom_levels(self): """ Zoom levels this process is currently initialized with. This gets triggered by using the ``zoom`` kwarg. If not set, it will be equal to self.zoom_levels. """ if self._raw["init_zoom_levels"] is None: return self.zoom_levels else: init_zooms = _validate_zooms(self._raw["init_zoom_levels"]) if not set(init_zooms).issubset(set(self.zoom_levels)): raise MapcheteConfigError( "init zooms must be a subset of process zoom") return init_zooms
def _parse_config(self, input_config, single_input_file): # from configuration dictionary if isinstance(input_config, dict): raw = input_config mapchete_file = None try: config_dir = input_config["config_dir"] except KeyError: raise MapcheteConfigError("config_dir parameter missing") # from Mapchete file elif os.path.splitext(input_config)[1] == ".mapchete": with open(input_config, "r") as config_file: raw = yaml.load(config_file.read()) mapchete_file = input_config config_dir = os.path.dirname(os.path.realpath(mapchete_file)) # throw error if unknown object else: raise MapcheteConfigError( "Configuration has to be a dictionary or a .mapchete file.") # make sure old input_files parameter is converted correctly if "input_files" in raw and "input" in raw: raise MapcheteConfigError("Either 'input_files' or'input allowed") elif "input_files" in raw: warnings.warn( "'input_files' is deprecated and will be replaced by 'input'") raw["input"] = raw.pop("input_files") # check if mandatory parameters are provided for param in _MANDATORY_PARAMETERS: if param not in raw: raise MapcheteConfigError("%s parameter missing" % param) # pixelbuffer and metatiling raw["pixelbuffer"] = self._set_pixelbuffer(raw) raw["output"]["pixelbuffer"] = self._set_pixelbuffer(raw["output"]) raw["metatiling"] = self._set_metatiling(raw) raw["output"]["metatiling"] = self._set_metatiling( raw["output"], default=raw["metatiling"]) if not raw["metatiling"] >= raw["output"]["metatiling"]: raise MapcheteConfigError( "Process metatiles cannot be smaller than output metatiles.") # absolute output path raw["output"].update(path=os.path.normpath( os.path.join(config_dir, raw["output"]["path"]))) # determine input files if raw["input"] == "from_command_line" and (self.mode in [ "memory", "continue", "overwrite" ]): if not single_input_file: raise MapcheteConfigError( "please provide an input file via command line") else: raw.update(input={"input_file": single_input_file}) # return parsed configuration return raw, mapchete_file, config_dir
def prepare(self, process_area=None, **kwargs): bounds = snap_bounds( bounds=Bounds(*process_area.intersection( box(*self.output_params["delimiters"] ["effective_bounds"])).bounds), pyramid=self.pyramid, zoom=self.zoom) if process_area else self.output_params[ "delimiters"]["effective_bounds"] height = math.ceil((bounds.top - bounds.bottom) / self.pyramid.pixel_x_size(self.zoom)) width = math.ceil((bounds.right - bounds.left) / self.pyramid.pixel_x_size(self.zoom)) logger.debug("output raster bounds: %s", bounds) logger.debug("output raster shape: %s, %s", height, width) self._profile = dict( GTIFF_DEFAULT_PROFILE, driver="GTiff", transform=Affine(self.pyramid.pixel_x_size(self.zoom), 0, bounds.left, 0, -self.pyramid.pixel_y_size(self.zoom), bounds.top), height=height, width=width, count=self.output_params["bands"], crs=self.pyramid.crs, **{ k: self.output_params.get(k, GTIFF_DEFAULT_PROFILE[k]) for k in GTIFF_DEFAULT_PROFILE.keys() }) logger.debug("single GTiff profile: %s", self._profile) if height * width > 20000 * 20000: raise ValueError("output GeoTIFF too big") # set up rasterio if path_exists(self.path): if self.output_params["mode"] != "overwrite": raise MapcheteConfigError( "single GTiff file already exists, use overwrite mode to replace" ) else: logger.debug("remove existing file: %s", self.path) os.remove(self.path) logger.debug("open output file: %s", self.path) self.rio_file = rasterio.open(self.path, "w+", **self._profile)
def __init__(self, input_params, **kwargs): """Initialize.""" super(InputData, self).__init__(input_params, **kwargs) self._params = input_params["abstract"] # validate parameters validate_values( self._params, [ ("path", six.string_types), ("type", six.string_types), ("extension", six.string_types)]) if not self._params["extension"] in [ "tif", "vrt", "png", "jpg", "mixed", "jp2", "geojson" ]: raise MapcheteConfigError( "invalid file extension given: %s" % self._params["extension"]) self._ext = self._params["extension"] self.path = _absolute_path( input_params["conf_dir"], self._params["path"]) # define pyramid self.td_pyramid = BufferedTilePyramid( self._params["type"], metatiling=self._params.get("metatiling", 1), tile_size=self._params.get("tile_size", 256), pixelbuffer=self._params.get("pixelbuffer", 0)) # additional params self._bounds = self._params.get("bounds", self.td_pyramid.bounds) self._file_type = ( "vector" if self._params["extension"] == "geojson" else "raster") if self._file_type == "raster": self._params["count"] = self._params.get( "count", self._params.get("bands", None)) validate_values(self._params, [ ("dtype", six.string_types), ("count", int)]) self._profile = { "nodata": self._params.get("nodata", 0), "dtype": self._params["dtype"], "count": self._params["count"]} else: self._profile = None