Example #1
0
def snap_bounds(bounds=None, pyramid=None, zoom=None):
    """
    Snaps bounds to tiles boundaries of specific zoom level.

    Parameters
    ----------
    bounds : bounds to be snapped
    pyramid : TilePyramid
    zoom : int

    Returns
    -------
    Bounds(left, bottom, right, top)
    """
    if not isinstance(bounds, (tuple, list)):
        raise TypeError("bounds must be either a tuple or a list")
    if len(bounds) != 4:
        raise ValueError("bounds has to have exactly four values")
    if not isinstance(pyramid, BufferedTilePyramid):
        raise TypeError("pyramid has to be a BufferedTilePyramid")

    bounds = Bounds(*bounds)
    lb = pyramid.tile_from_xy(bounds.left,
                              bounds.bottom,
                              zoom,
                              on_edge_use="rt").bounds
    rt = pyramid.tile_from_xy(bounds.right, bounds.top, zoom,
                              on_edge_use="lb").bounds
    return Bounds(lb.left, lb.bottom, rt.right, rt.top)
Example #2
0
def bounds_from_opts(wkt_geometry=None,
                     point=None,
                     bounds=None,
                     zoom=None,
                     raw_conf=None):
    """
    Loads the process pyramid of a raw configuration.

    Parameters
    ----------
    raw_conf : dict
        Raw mapchete configuration as dictionary.

    Returns
    -------
    BufferedTilePyramid
    """
    if wkt_geometry:
        return Bounds(*wkt.loads(wkt_geometry).bounds)
    elif point:
        x, y = point
        zoom_levels = get_zoom_levels(
            process_zoom_levels=raw_conf["zoom_levels"], init_zoom_levels=zoom)
        tp = raw_conf_process_pyramid(raw_conf)
        return Bounds(*tp.tile_from_xy(x, y, max(zoom_levels)).bounds)
    else:
        return validate_bounds(bounds) if bounds is not None else bounds
Example #3
0
def bounds_from_opts(wkt_geometry=None,
                     point=None,
                     point_crs=None,
                     zoom=None,
                     bounds=None,
                     bounds_crs=None,
                     raw_conf=None):
    """
    Return process bounds depending on given inputs.

    Parameters
    ----------
    wkt_geometry : string
        WKT geometry used to generate bounds.
    point : iterable
        x and y coordinates of point whose corresponding process tile bounds shall be
        returned.
    point_crs : str or CRS
        CRS of point (default: process pyramid CRS)
    zoom : int
        Mandatory zoom level if point is provided.
    bounds : iterable
        Bounding coordinates to be used
    bounds_crs : str or CRS
        CRS of bounds (default: process pyramid CRS)

    raw_conf : dict
        Raw mapchete configuration as dictionary.

    Returns
    -------
    BufferedTilePyramid
    """
    if wkt_geometry:
        return Bounds(*wkt.loads(wkt_geometry).bounds)
    elif point:
        x, y = point
        tp = raw_conf_process_pyramid(raw_conf)
        if point_crs:
            reproj = reproject_geometry(Point(x, y),
                                        src_crs=point_crs,
                                        dst_crs=tp.crs)
            x = reproj.x
            y = reproj.y
        zoom_levels = get_zoom_levels(
            process_zoom_levels=raw_conf["zoom_levels"], init_zoom_levels=zoom)
        return Bounds(*tp.tile_from_xy(x, y, max(zoom_levels)).bounds)
    elif bounds:
        bounds = validate_bounds(bounds)
        if bounds_crs:
            tp = raw_conf_process_pyramid(raw_conf)
            bounds = Bounds(*reproject_geometry(
                box(*bounds), src_crs=bounds_crs, dst_crs=tp.crs).bounds)
        return bounds
    else:
        return
Example #4
0
def clip_bounds(bounds=None, clip=None):
    """
    Clips bounds by clip.

    Parameters
    ----------
    bounds : bounds to be clipped
    clip : clip bounds

    Returns
    -------
    Bounds(left, bottom, right, top)
    """
    bounds = Bounds(*bounds)
    clip = Bounds(*clip)
    return Bounds(max(bounds.left, clip.left), max(bounds.bottom, clip.bottom),
                  min(bounds.right, clip.right), min(bounds.top, clip.top))
Example #5
0
    def init_bounds(self):
        """
        Process bounds this process is currently initialized with.

        This gets triggered by using the ``bounds`` kwarg. If not set, it will
        be equal to self.bounds.
        """
        if self._raw["init_bounds"] is None:
            return self.bounds
        else:
            return Bounds(*_validate_bounds(self._raw["init_bounds"]))
Example #6
0
    def bounds_at_zoom(self, zoom=None):
        """
        Return process bounds for zoom level.

        Parameters
        ----------
        zoom : integer or list

        Returns
        -------
        process bounds : tuple
            left, bottom, right, top
        """
        return () if self.area_at_zoom(zoom).is_empty else Bounds(
            *self.area_at_zoom(zoom).bounds)
Example #7
0
def snap_bounds(bounds=None, pyramid=None, zoom=None):
    """
    Snaps bounds to tiles boundaries of specific zoom level.

    Parameters
    ----------
    bounds : bounds to be snapped
    pyramid : TilePyramid
    zoom : int

    Returns
    -------
    Bounds(left, bottom, right, top)
    """
    bounds = validate_bounds(bounds)
    pyramid = validate_bufferedtilepyramid(pyramid)
    lb = pyramid.tile_from_xy(bounds.left,
                              bounds.bottom,
                              zoom,
                              on_edge_use="rt").bounds
    rt = pyramid.tile_from_xy(bounds.right, bounds.top, zoom,
                              on_edge_use="lb").bounds
    return Bounds(lb.left, lb.bottom, rt.right, rt.top)
Example #8
0
def validate_bounds(bounds):
    """
    Return validated bounds.

    Bounds must be a list or tuple with exactly four elements.

    Parameters
    ----------
    bounds : list or tuple

    Returns
    -------
    Bounds

    Raises
    ------
    TypeError if type is invalid.
    """
    if not isinstance(bounds, (tuple, list)):
        raise TypeError("bounds must be either a tuple or a list: %s" % str(bounds))
    if len(bounds) != 4:
        raise ValueError("bounds has to have exactly four values: %s" % str(bounds))
    return Bounds(*bounds)
Example #9
0
 def bounds(self):
     """Process bounds as defined in the configuration."""
     if self._raw["bounds"] is None:
         return self.process_pyramid.bounds
     else:
         return Bounds(*_validate_bounds(self._raw["bounds"]))
Example #10
0
    def __init__(self,
                 input_config,
                 zoom=None,
                 area=None,
                 area_crs=None,
                 bounds=None,
                 bounds_crs=None,
                 single_input_file=None,
                 mode="continue",
                 debug=False,
                 **kwargs):
        """Initialize configuration."""
        logger.debug(f"parsing {input_config}")
        # get dictionary representation of input_config and
        # (0) map deprecated params to new structure
        self._raw = _map_to_new_config(_config_to_dict(input_config))
        self._raw["init_zoom_levels"] = zoom
        self._raw["init_bounds"] = bounds
        self._raw["init_bounds_crs"] = bounds_crs
        self._raw["init_area"] = area
        self._raw["init_area_crs"] = area_crs
        self._cache_area_at_zoom = {}
        self._cache_full_process_area = None

        if mode not in ["memory", "continue", "readonly", "overwrite"]:
            raise MapcheteConfigError("unknown mode %s" % mode)
        self.mode = mode

        # (1) assert mandatory params are available
        try:
            validate_values(self._raw, _MANDATORY_PARAMETERS)
        except Exception as e:
            raise MapcheteConfigError(e)

        # (2) check user process
        self.config_dir = self._raw["config_dir"]
        self.process_name = self.process_path = self._raw["process"]
        if self.mode != "readonly":
            logger.debug("validating process code")
            self.process_func

        # (3) set process and output pyramids
        logger.debug("initializing pyramids")
        try:
            process_metatiling = self._raw["pyramid"].get("metatiling", 1)
            # output metatiling defaults to process metatiling if not set
            # explicitly
            output_metatiling = self._raw["output"].get(
                "metatiling", process_metatiling)
            # we cannot properly handle output tiles which are bigger than
            # process tiles
            if output_metatiling > process_metatiling:
                raise ValueError(
                    "output metatiles must be smaller than process metatiles")
            # these two BufferedTilePyramid instances will help us with all
            # the tile geometries etc.
            self.process_pyramid = BufferedTilePyramid(
                self._raw["pyramid"]["grid"],
                metatiling=process_metatiling,
                pixelbuffer=self._raw["pyramid"].get("pixelbuffer", 0))
            self.output_pyramid = BufferedTilePyramid(
                self._raw["pyramid"]["grid"],
                metatiling=output_metatiling,
                pixelbuffer=self._raw["output"].get("pixelbuffer", 0))
        except Exception as e:
            logger.exception(e)
            raise MapcheteConfigError(e)

        # (4) set approach how to handle inputs
        # don't inititalize inputs on readonly mode or if only overviews are going to be
        # built
        self._init_inputs = False if (self.mode == "readonly" or (not len(
            set(self.baselevels["zooms"]).intersection(
                set(self.init_zoom_levels))) if self.baselevels else False)
                                      ) else True

        # (5) prepare process parameters per zoom level without initializing
        # input and output classes
        logger.debug("preparing process parameters")
        self._params_at_zoom = _raw_at_zoom(self._raw, self.init_zoom_levels)

        # (6) determine process area and process boundaries both from config as well
        # as from initialization.
        # First, the area and bounds parameters from the configuration are checked. If
        # both are provided, the intersection will be taken into account. If none are,
        # the process pyramid area is assumed.
        # Second, they can be overrided by the area and bounds kwargs when constructing
        # the configuration.
        # To finally determine the process tiles, the intersection of process area and the
        # union of all inputs is considered.
        self.area = self._get_process_area(
            area=self._raw.get("area"),
            bounds=self._raw.get("bounds"),
            area_fallback=box(*self.process_pyramid.bounds),
            bounds_fallback=self.process_pyramid.bounds,
            area_crs=area_crs,
            bounds_crs=bounds_crs)
        logger.debug(f"process area: {self.area}")
        self.bounds = Bounds(*self.area.bounds)
        logger.debug(f"process bounds: {self.bounds}")
        self.init_area = self._get_process_area(
            area=self._raw.get("init_area"),
            bounds=self._raw.get("init_bounds"),
            area_fallback=self.area,
            bounds_fallback=self.bounds,
            area_crs=area_crs,
            bounds_crs=bounds_crs)
        logger.debug(f"init area: {self.init_area}")
        self.init_bounds = Bounds(*self.init_area.bounds)
        logger.debug(f"init bounds: {self.init_bounds}")

        # (7) the delimiters are used by some input drivers
        self._delimiters = dict(zoom=self.init_zoom_levels,
                                bounds=self.init_bounds,
                                process_bounds=self.bounds,
                                effective_bounds=self.effective_bounds)

        # (8) initialize output
        logger.debug("initializing output")
        self.output

        # (9) initialize input items
        # depending on the inputs this action takes the longest and is done
        # in the end to let all other actions fail earlier if necessary
        logger.debug("initializing input")
        self.input

        # (10) some output drivers such as the GeoTIFF single file driver also needs the
        # process area to prepare
        logger.debug("prepare output")
        self.output.prepare(process_area=self.area_at_zoom())