Пример #1
0
    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)
Пример #2
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
Пример #3
0
    def bbox(self, out_crs=None):
        """
        Return data bounding box.

        Parameters
        ----------
        out_crs : ``rasterio.crs.CRS``
            rasterio CRS object (default: CRS of process pyramid)

        Returns
        -------
        bounding box : geometry
            Shapely geometry object
        """
        out_crs = self.pyramid.crs if out_crs is None else out_crs
        with rasterio.open(self.path) as inp:
            inp_crs = inp.crs
            out_bbox = bbox = box(*inp.bounds)
        # If soucre and target CRSes differ, segmentize and reproject
        if inp_crs != out_crs:
            # estimate segmentize value (raster pixel size * tile size)
            # and get reprojected bounding box
            return reproject_geometry(segmentize_geometry(
                bbox, inp.transform[0] * self.pyramid.tile_size),
                                      src_crs=inp_crs,
                                      dst_crs=out_crs)
        else:
            return out_bbox
Пример #4
0
 def nodatamask(self):
     """SAFE file nodata mask as iterable list of geometries."""
     return [
         reproject_geometry(granule["nodatamask"],
                            src_crs=CRS.from_epsg(4326),
                            dst_crs=self.crs)
         for granule in self.s2metadata["granules"]
     ]
Пример #5
0
 def bbox(self, out_crs=None):
     """Return data bounding box."""
     out_crs = self.pyramid.crs if out_crs is None else out_crs
     inp_crs = CRS().from_epsg(4326)
     if inp_crs != out_crs:
         return reproject_geometry(self.s2metadata["footprint"],
                                   src_crs=inp_crs,
                                   dst_crs=out_crs)
     else:
         return self.s2metadata["footprint"]
Пример #6
0
def test_reproject_geometry():
    """Reproject geometry."""
    with fiona.open(os.path.join(TESTDATA_DIR, "landpoly.geojson"),
                    "r") as src:
        for feature in src:

            # WGS84 to Spherical Mercator
            out_geom = vector.reproject_geometry(shape(feature["geometry"]),
                                                 CRS(src.crs),
                                                 CRS().from_epsg(3857))
            assert out_geom.is_valid

            # WGS84 to LAEA
            out_geom = vector.reproject_geometry(shape(feature["geometry"]),
                                                 CRS(src.crs),
                                                 CRS().from_epsg(3035))
            assert out_geom.is_valid

            # WGS84 to WGS84
            out_geom = vector.reproject_geometry(shape(feature["geometry"]),
                                                 CRS(src.crs),
                                                 CRS().from_epsg(4326))
            assert out_geom.is_valid

    # WGS84 bounds to Spherical Mercator
    big_box = box(-180, -90, 180, 90)
    vector.reproject_geometry(big_box,
                              CRS().from_epsg(4326),
                              CRS().from_epsg(3857))

    # WGS84 bounds to Spherical Mercator raising clip error
    try:
        vector.reproject_geometry(big_box,
                                  CRS().from_epsg(4326),
                                  CRS().from_epsg(3857),
                                  error_on_clip=True)
        raise Exception()
    except RuntimeError:
        pass

    # empty geometry
    assert vector.reproject_geometry(Polygon(),
                                     CRS().from_epsg(4326),
                                     CRS().from_epsg(3857)).is_empty
    assert vector.reproject_geometry(Polygon(),
                                     CRS().from_epsg(4326),
                                     CRS().from_epsg(4326)).is_empty
Пример #7
0
    def _get_tiles_paths(
        self,
        tile_directory_zoom=None,
        fallback_to_higher_zoom=False,
        matching_method="gdal",
        matching_precision=8,
        matching_max_zoom=None,
    ):
        # determine tile bounds in TileDirectory CRS
        td_bounds = reproject_geometry(
            self.tile.bbox,
            src_crs=self.tile.tp.crs,
            dst_crs=self._td_pyramid.crs
        ).bounds

        # find target zoom level
        if tile_directory_zoom is None:
            zoom = tile_to_zoom_level(
                self.tile,
                dst_pyramid=self._td_pyramid,
                matching_method=matching_method,
                precision=matching_precision
            )
            if matching_max_zoom is not None:
                zoom = min([zoom, matching_max_zoom])
        else:
            zoom = tile_directory_zoom

        if fallback_to_higher_zoom:
            tiles_paths = []
            # check if tiles exist otherwise try higher zoom level
            while len(tiles_paths) == 0 and zoom >= 0:
                tiles_paths = _get_tiles_paths(
                    basepath=self._basepath,
                    ext=self._ext,
                    pyramid=self._td_pyramid,
                    bounds=td_bounds,
                    zoom=zoom
                )
                logger.debug("%s existing tiles found at zoom %s", len(tiles_paths), zoom)
                zoom -= 1
        else:
            tiles_paths = _get_tiles_paths(
                basepath=self._basepath,
                ext=self._ext,
                pyramid=self._td_pyramid,
                bounds=td_bounds,
                zoom=zoom
            )
            logger.debug("%s existing tiles found at zoom %s", len(tiles_paths), zoom)

        return tiles_paths
Пример #8
0
def _get_warped_array(
    input_file=None, band_idx=None, dst_bounds=None, dst_shape=None,
    dst_affine=None, dst_crs=None, resampling="nearest"
):
    """Extract a numpy array from a raster file."""
    LOGGER.debug("read array using rasterio")
    with rasterio.open(input_file, "r") as src:
        if dst_crs == src.crs:
            src_left, src_bottom, src_right, src_top = dst_bounds
        else:
            # Return empty array if destination bounds don't intersect with
            # file bounds.
            file_bbox = box(*src.bounds)
            tile_bbox = reproject_geometry(
                box(*dst_bounds), src_crs=dst_crs, dst_crs=src.crs)
            if not file_bbox.intersects(tile_bbox):
                LOGGER.debug("file bounding box does not intersect with tile")
                return ma.MaskedArray(
                    data=ma.zeros(dst_shape, dtype=src.profile["dtype"]),
                    mask=ma.ones(dst_shape), fill_value=src.nodata)
            # Reproject tile bounds to source file SRS.
            src_left, src_bottom, src_right, src_top = transform_bounds(
                dst_crs, src.crs, *dst_bounds, densify_pts=21)

        if float('Inf') in (src_left, src_bottom, src_right, src_top):
            # Maybe not the best way to deal with it, but if bounding box
            # cannot be translated, it is assumed that data is emtpy
            LOGGER.debug("tile seems to be outside of input CRS bounds")
            return ma.MaskedArray(
                data=ma.zeros(dst_shape, dtype=src.profile["dtype"]),
                mask=ma.ones(dst_shape), fill_value=src.nodata)
        # Read data window.
        window = src.window(
            src_left, src_bottom, src_right, src_top, boundless=True)
        start = time.time()
        src_band = src.read(band_idx, window=window, boundless=True)
        LOGGER.debug("window read in %ss" % round(time.time() - start, 3))
        # Quick fix because None nodata is not allowed.
        nodataval = 0 if not src.nodata else src.nodata
        # Prepare reprojected array.
        dst_band = np.empty(dst_shape, src.dtypes[band_idx-1])
        # Run rasterio's reproject().
        start = time.time()
        reproject(
            src_band, dst_band, src_transform=src.window_transform(window),
            src_crs=src.crs, src_nodata=nodataval, dst_transform=dst_affine,
            dst_crs=dst_crs, dst_nodata=nodataval,
            resampling=RESAMPLING_METHODS[resampling])
        LOGGER.debug(
            "window reprojected in %ss" % round(time.time() - start, 3))
        return ma.MaskedArray(dst_band, mask=dst_band == nodataval)
Пример #9
0
def get_best_zoom_level(input_file, tile_pyramid_type):
    """
    Determine the best base zoom level for a raster.

    "Best" means the maximum zoom level where no oversampling has to be done.

    Parameters
    ----------
    input_file : path to raster file
    tile_pyramid_type : ``TilePyramid`` projection (``geodetic`` or
        ``mercator``)

    Returns
    -------
    zoom : integer
    """
    tile_pyramid = TilePyramid(tile_pyramid_type)
    with rasterio.open(input_file, "r") as src:
        if not src.crs.is_valid:
            raise IOError("CRS could not be read from %s" % input_file)
        bbox = box(
            src.bounds.left, src.bounds.bottom, src.bounds.right,
            src.bounds.top)
        if src.crs != tile_pyramid.crs:
            segmentize = raster_file._get_segmentize_value(
                input_file, tile_pyramid)
            ogr_bbox = ogr.CreateGeometryFromWkb(bbox.wkb)
            ogr_bbox.Segmentize(segmentize)
            segmentized_bbox = loads(ogr_bbox.ExportToWkt())
            bbox = segmentized_bbox
            xmin, ymin, xmax, ymax = reproject_geometry(
                bbox, src_crs=src.crs, dst_crs=tile_pyramid.crs).bounds
        else:
            xmin, ymin, xmax, ymax = bbox.bounds
        x_dif = xmax - xmin
        y_dif = ymax - ymin
        size = float(src.width + src.height)
        avg_resolution = (
            (x_dif / float(src.width)) * (float(src.width) / size) +
            (y_dif / float(src.height)) * (float(src.height) / size)
        )

    for zoom in range(0, 25):
        if tile_pyramid.pixel_x_size(zoom) <= avg_resolution:
            return zoom-1

    raise ValueError("no fitting zoom level found")
Пример #10
0
    def bbox(self, out_crs=None):
        """
        Return data bounding box.

        Parameters
        ----------
        out_crs : ``rasterio.crs.CRS``
            rasterio CRS object (default: CRS of process pyramid)

        Returns
        -------
        bounding box : geometry
            Shapely geometry object
        """
        return reproject_geometry(
            self.process.config.area_at_zoom(),
            src_crs=self.process.config.process_pyramid.crs,
            dst_crs=self.pyramid.crs if out_crs is None else out_crs)
Пример #11
0
    def bbox(self, out_crs=None):
        """
        Return data bounding box.

        Parameters
        ----------
        out_crs : ``rasterio.crs.CRS``
            rasterio CRS object (default: CRS of process pyramid)

        Returns
        -------
        bounding box : geometry
            Shapely geometry object
        """
        out_crs = self.pyramid.crs if out_crs is None else out_crs
        with fiona.open(self.path) as inp:
            inp_crs = CRS(inp.crs)
            bbox = box(*inp.bounds)
        # TODO find a way to get a good segmentize value in bbox source CRS
        return reproject_geometry(bbox, src_crs=inp_crs, dst_crs=out_crs)
Пример #12
0
    def bbox(self, out_crs=None):
        """
        Return data bounding box.

        Parameters
        ----------
        out_crs : ``rasterio.crs.CRS``
            rasterio CRS object (default: CRS of process pyramid)

        Returns
        -------
        bounding box : geometry
            Shapely geometry object
        """
        if out_crs is None:
            out_crs = self.pyramid.crs
        if str(out_crs) not in self._bbox_cache:
            with rasterio.open(self.path) as inp:
                inp_crs = inp.crs
                try:
                    assert inp_crs.is_valid
                except AssertionError:
                    raise IOError("CRS could not be read from %s" % self.path)
            out_bbox = bbox = box(
                inp.bounds.left, inp.bounds.bottom, inp.bounds.right,
                inp.bounds.top
            )
            # If soucre and target CRSes differ, segmentize and reproject
            if inp_crs != out_crs:
                # estimate segmentize value (raster pixel size * tile size)
                segmentize = inp.transform[0] * self.pyramid.tile_size
                ogr_bbox = ogr.CreateGeometryFromWkb(bbox.wkb)
                ogr_bbox.Segmentize(segmentize)
                self._bbox_cache[str(out_crs)] = reproject_geometry(
                    loads(ogr_bbox.ExportToWkt()),
                    src_crs=inp_crs, dst_crs=out_crs
                )
            else:
                self._bbox_cache[str(out_crs)] = out_bbox
        return self._bbox_cache[str(out_crs)]
Пример #13
0
    def bbox(self, out_crs=None):
        """
        Return data bounding box.

        Parameters
        ----------
        out_crs : ``rasterio.crs.CRS``
            rasterio CRS object (default: CRS of process pyramid)

        Returns
        -------
        bounding box : geometry
            Shapely geometry object
        """
        if out_crs is None:
            out_crs = self.pyramid.crs
        if str(out_crs) not in self._bbox_cache:
            self._bbox_cache[str(out_crs)] = reproject_geometry(
                self.process.config.process_area(),
                src_crs=self.process.config.crs,
                dst_crs=out_crs)

        return self._bbox_cache[str(out_crs)]
Пример #14
0
def get_best_zoom_level(input_file, tile_pyramid_type):
    """
    Determine the best base zoom level for a raster.

    "Best" means the maximum zoom level where no oversampling has to be done.

    Parameters
    ----------
    input_file : path to raster file
    tile_pyramid_type : ``TilePyramid`` projection (``geodetic`` or
        ``mercator``)

    Returns
    -------
    zoom : integer
    """
    tile_pyramid = TilePyramid(tile_pyramid_type)
    with rasterio.open(input_file, "r") as src:
        xmin, ymin, xmax, ymax = reproject_geometry(
            segmentize_geometry(
                box(src.bounds.left, src.bounds.bottom, src.bounds.right,
                    src.bounds.top),
                get_segmentize_value(input_file, tile_pyramid)),
            src_crs=src.crs,
            dst_crs=tile_pyramid.crs).bounds
        x_dif = xmax - xmin
        y_dif = ymax - ymin
        size = float(src.width + src.height)
        avg_resolution = ((x_dif / float(src.width)) *
                          (float(src.width) / size) +
                          (y_dif / float(src.height)) *
                          (float(src.height) / size))

    for zoom in range(0, 40):
        if tile_pyramid.pixel_x_size(zoom) <= avg_resolution:
            return zoom - 1
Пример #15
0
 def __init__(self, input_params, **kwargs):
     """Initialize."""
     self.path = input_params["path"]
     self.pyramid = input_params["pyramid"]
     self.pixelbuffer = input_params["pixelbuffer"]
     self.crs = self.pyramid.crs
     self.srid = self.pyramid.srid
     with s2reader.open(self.path) as s2dataset:
         self.s2metadata = {
             "path":
             s2dataset.path,
             "footprint":
             s2dataset.footprint,
             "granules": [{
                 "id":
                 granule.granule_identifier,
                 "datastrip_id":
                 granule.datastrip_identifier,
                 "srid":
                 granule.srid,
                 "footprint":
                 reproject_geometry(granule.footprint,
                                    src_crs=CRS.from_epsg(4326),
                                    dst_crs=self.crs),
                 "nodatamask":
                 granule.nodata_mask,
                 "cloudmask":
                 granule.cloudmask,
                 "band_path": {
                     index: granule.band_path(_id,
                                              for_gdal=True,
                                              absolute=True)
                     for index, _id in zip(range(1, 14), BAND_IDS)
                 }
             } for granule in s2dataset.granules]
         }
Пример #16
0
def test_reproject_geometry(landpoly):
    """Reproject geometry."""
    with fiona.open(landpoly, "r") as src:
        for feature in src:

            # WGS84 to Spherical Mercator
            out_geom = reproject_geometry(shape(feature["geometry"]),
                                          CRS(src.crs),
                                          CRS().from_epsg(3857))
            assert out_geom.is_valid

            # WGS84 to LAEA
            out_geom = reproject_geometry(shape(feature["geometry"]),
                                          CRS(src.crs),
                                          CRS().from_epsg(3035))
            assert out_geom.is_valid

            # WGS84 to WGS84
            out_geom = reproject_geometry(shape(feature["geometry"]),
                                          CRS(src.crs),
                                          CRS().from_epsg(4326))
            assert out_geom.is_valid

    # WGS84 bounds to Spherical Mercator
    big_box = box(-180, -90, 180, 90)
    reproject_geometry(big_box, CRS().from_epsg(4326), CRS().from_epsg(3857))

    # WGS84 bounds to Spherical Mercator raising clip error
    with pytest.raises(RuntimeError):
        reproject_geometry(big_box,
                           CRS().from_epsg(4326),
                           CRS().from_epsg(3857),
                           error_on_clip=True)

    # empty geometry
    assert reproject_geometry(Polygon(),
                              CRS().from_epsg(4326),
                              CRS().from_epsg(3857)).is_empty
    assert reproject_geometry(Polygon(),
                              CRS().from_epsg(4326),
                              CRS().from_epsg(4326)).is_empty

    # CRS parameter
    big_box = box(-180, -90, 180, 90)
    assert reproject_geometry(big_box, 4326, 3857) == reproject_geometry(
        big_box, "4326", "3857")
    with pytest.raises(TypeError):
        reproject_geometry(big_box, 1.0, 1.0)
Пример #17
0
def _clip_bbox(clip_geometry, dst_crs=None):
    with fiona.open(clip_geometry) as src:
        return reproject_geometry(box(*src.bounds),
                                  src_crs=src.crs,
                                  dst_crs=dst_crs)
Пример #18
0
def convert(input_,
            output,
            zoom=None,
            bounds=None,
            bounds_crs=None,
            area=None,
            area_crs=None,
            point=None,
            point_crs=None,
            wkt_geometry=None,
            clip_geometry=None,
            bidx=None,
            output_pyramid=None,
            output_metatiling=None,
            output_format=None,
            output_dtype=None,
            output_geometry_type=None,
            creation_options=None,
            scale_ratio=None,
            scale_offset=None,
            resampling_method=None,
            overviews=False,
            overviews_resampling_method=None,
            cog=False,
            overwrite=False,
            logfile=None,
            verbose=False,
            no_pbar=False,
            debug=False,
            multi=None,
            vrt=False,
            idx_out_dir=None):
    try:
        input_info = _get_input_info(input_)
        output_info = _get_output_info(output)
    except Exception as e:
        raise click.BadArgumentUsage(e)

    # collect mapchete configuration
    mapchete_config = dict(
        process="mapchete.processes.convert",
        input=dict(inp=input_, clip=clip_geometry),
        pyramid=(dict(grid=output_pyramid,
                      metatiling=(output_metatiling or
                                  (input_info["pyramid"].get("metatiling", 1)
                                   if input_info["pyramid"] else 1)),
                      pixelbuffer=(input_info["pyramid"].get("pixelbuffer", 0)
                                   if input_info["pyramid"] else 0))
                 if output_pyramid else input_info["pyramid"]),
        output=dict(
            {
                k: v
                for k, v in input_info["output_params"].items()
                if k not in ["delimiters", "bounds", "mode"]
            },
            path=output,
            format=(output_format or output_info["driver"]
                    or input_info["output_params"]["format"]),
            dtype=output_dtype or input_info["output_params"].get("dtype"),
            **creation_options,
            **dict(overviews=True,
                   overviews_resampling=overviews_resampling_method)
            if overviews else dict(),
        ),
        config_dir=os.getcwd(),
        zoom_levels=zoom or input_info["zoom_levels"],
        scale_ratio=scale_ratio,
        scale_offset=scale_offset,
        resampling=resampling_method,
        band_indexes=bidx)

    # assert all required information is there
    if mapchete_config["output"]["format"] is None:
        # this happens if input file is e.g. JPEG2000 and output is a tile directory
        raise click.BadOptionUsage("output-format", "Output format required.")
    if mapchete_config["output"]["format"] == "GTiff":
        mapchete_config["output"].update(cog=cog)
    output_type = OUTPUT_FORMATS[mapchete_config["output"]
                                 ["format"]]["data_type"]
    if bidx is not None:
        mapchete_config["output"].update(bands=len(bidx))
    if mapchete_config["pyramid"] is None:
        raise click.BadOptionUsage("output-pyramid",
                                   "Output pyramid required.")
    elif mapchete_config["zoom_levels"] is None:
        try:
            mapchete_config.update(zoom_levels=dict(
                min=0,
                max=get_best_zoom_level(input_, mapchete_config["pyramid"]
                                        ["grid"])))
        except:
            raise click.BadOptionUsage("zoom", "Zoom levels required.")
    elif input_info["input_type"] != output_type:
        raise click.BadArgumentUsage(
            "Output format type (%s) is incompatible with input format (%s)." %
            (output_type, input_info["input_type"]))
    if output_metatiling:
        mapchete_config["output"].update(metatiling=output_metatiling)
    if input_info["output_params"].get("schema") and output_geometry_type:
        mapchete_config["output"]["schema"].update(
            geometry=output_geometry_type)

    # determine process bounds
    out_pyramid = BufferedTilePyramid.from_dict(mapchete_config["pyramid"])
    inp_bounds = (bounds or reproject_geometry(box(*input_info["bounds"]),
                                               src_crs=input_info["crs"],
                                               dst_crs=out_pyramid.crs).bounds
                  if input_info["bounds"] else out_pyramid.bounds)
    # if clip-geometry is available, intersect determined bounds with clip bounds
    if clip_geometry:
        clip_intersection = _clip_bbox(clip_geometry,
                                       dst_crs=out_pyramid.crs).intersection(
                                           box(*inp_bounds))
        if clip_intersection.is_empty:
            click.echo(
                "Process area is empty: clip bounds don't intersect with input bounds."
            )
            return
    # add process bounds and output type
    mapchete_config.update(
        bounds=(clip_intersection.bounds if clip_geometry else inp_bounds),
        bounds_crs=bounds_crs,
        clip_to_output_dtype=mapchete_config["output"].get("dtype", None))
    logger.debug("temporary config generated: %s", pformat(mapchete_config))

    utils._process_area(debug=debug,
                        mapchete_config=mapchete_config,
                        mode="overwrite" if overwrite else "continue",
                        zoom=zoom,
                        wkt_geometry=wkt_geometry,
                        point=point,
                        point_crs=point_crs,
                        bounds=bounds,
                        bounds_crs=bounds_crs,
                        area=area,
                        area_crs=area_crs,
                        multi=multi or cpu_count(),
                        verbose_dst=open(os.devnull, 'w')
                        if debug or not verbose else sys.stdout,
                        no_pbar=no_pbar,
                        vrt=vrt,
                        idx_out_dir=idx_out_dir)