Exemple #1
0
def test_create_mosaic():
    """Create mosaic from tiles."""
    tp = BufferedTilePyramid("geodetic")
    # quick return mosaic if there is just one tile
    tile = tp.tile(3, 3, 3)
    data = np.ones(tile.shape)
    mosaic = create_mosaic([(tile, data)])
    assert isinstance(mosaic, ReferencedRaster)
    assert np.array_equal(data, mosaic.data)
    assert tile.affine == mosaic.affine
    # multiple tiles
    for pixelbuffer in [0, 10]:
        tp = BufferedTilePyramid("geodetic", pixelbuffer=pixelbuffer)
        tiles = [(tp.tile(5, row, col), np.ones(tp.tile(5, row, col).shape))
                 for row, col in product(range(4), range(4))]
        # 4x4 top left tiles from zoom 5 equal top left tile from zoom 3
        # also use tile generator
        mosaic = create_mosaic((t for t in tiles))
        assert isinstance(mosaic, ReferencedRaster)
        assert np.all(np.where(mosaic.data == 1, True, False))
        mosaic_bbox = box(
            mosaic.affine[2],
            mosaic.affine[5] + mosaic.data.shape[1] * mosaic.affine[4],
            mosaic.affine[2] + mosaic.data.shape[2] * mosaic.affine[0],
            mosaic.affine[5])
        control_bbox = box(*unary_union([t.bbox for t, _ in tiles]).bounds)
        assert mosaic_bbox.equals(control_bbox)
Exemple #2
0
def test_create_mosaic_errors():
    """Check error handling of create_mosaic()."""
    tp_geo = BufferedTilePyramid("geodetic")
    tp_mer = BufferedTilePyramid("mercator")
    geo_tile = tp_geo.tile(1, 0, 0)
    geo_tile.data = np.ndarray(geo_tile.shape)
    mer_tile = tp_mer.tile(1, 1, 0)
    mer_tile.data = np.ndarray(mer_tile.shape)
    # CRS error
    with pytest.raises(ValueError):
        raster.create_mosaic([geo_tile, mer_tile])
    # zoom error
    with pytest.raises(ValueError):
        diff_zoom = tp_geo.tile(2, 1, 0)
        diff_zoom.data = np.ndarray(diff_zoom.shape)
        raster.create_mosaic([geo_tile, diff_zoom])
    # tile data error
    with pytest.raises(TypeError):
        geo_tile.data = None
        # for one tile
        raster.create_mosaic([geo_tile])
    with pytest.raises(TypeError):
        geo_tile.data = None
        # for multiple tiles
        raster.create_mosaic([geo_tile, geo_tile])
    # tile data type error
    with pytest.raises(TypeError):
        geo_tile.data = np.ndarray(geo_tile.shape)
        diff_type = tp_geo.tile(1, 1, 0)
        diff_type.data = np.ndarray(diff_zoom.shape).astype("int")
        raster.create_mosaic([geo_tile, diff_type])
Exemple #3
0
def test_processing():
    """Test correct processing (read and write) outputs."""
    for cleantopo_process in [
            "testdata/cleantopo_tl.mapchete", "testdata/cleantopo_br.mapchete"
    ]:
        mp = mapchete.open(os.path.join(SCRIPTDIR, cleantopo_process))
        for zoom in range(6):
            tiles = []
            for tile in mp.get_process_tiles(zoom):
                output = mp.execute(tile)
                tiles.append(output)
                assert isinstance(output, BufferedTile)
                assert isinstance(output.data, ma.MaskedArray)
                assert output.data.shape == output.shape
                assert not ma.all(output.data.mask)
                mp.write(output)
            mosaic, mosaic_affine = create_mosaic(tiles)
            try:
                temp_vrt = os.path.join(OUT_DIR, str(zoom) + ".vrt")
                gdalbuildvrt = "gdalbuildvrt %s %s/%s/*/*.tif > /dev/null" % (
                    temp_vrt, OUT_DIR, zoom)
                os.system(gdalbuildvrt)
                with rasterio.open(temp_vrt, "r") as testfile:
                    for file_item, mosaic_item in zip(
                            testfile.meta["transform"], mosaic_affine):
                        assert file_item == mosaic_item
                    band = testfile.read(1, masked=True)
                    assert band.shape == mosaic.shape
                    assert ma.allclose(band, mosaic)
                    assert ma.allclose(band.mask, mosaic.mask)
            finally:
                shutil.rmtree(OUT_DIR, ignore_errors=True)
Exemple #4
0
def test_read_raster_window_input_list(cleantopo_br):
    process_zoom = 5
    conf = dict(**cleantopo_br.dict)
    conf["output"].update(metatiling=1)
    with mapchete.open(conf) as mp:
        mp.batch_process(process_zoom)
        tiles = [(tile, mp.config.output.get_path(tile))
                 for tile in mp.config.output_pyramid.tiles_from_bounds(
                     mp.config.bounds, process_zoom)
                 if path_exists(mp.config.output.get_path(tile))]
        upper_tile = next(mp.get_process_tiles(process_zoom - 1))
        assert len(tiles) > 1
        resampled = resample_from_array(in_raster=create_mosaic([
            (tile, read_raster_window(path, tile)) for tile, path in tiles
        ]),
                                        out_tile=upper_tile)
    resampled2 = read_raster_window([p for _, p in tiles],
                                    upper_tile,
                                    src_nodata=0,
                                    dst_nodata=0)
    assert resampled.dtype == resampled2.dtype
    assert resampled.shape == resampled2.shape
    assert np.array_equal(resampled.mask, resampled2.mask)
    # TODO slight rounding errors occur
    assert np.allclose(resampled, resampled2, rtol=0.01)
Exemple #5
0
    def read(self, **kwargs):
        """
        Read existing output data from a previous run.

        Returns
        -------
        process output : NumPy array (raster) or feature iterator (vector)
        """
        if self.tile.pixelbuffer > self.config.output.pixelbuffer:
            output_tiles = list(
                self.config.output_pyramid.tiles_from_bounds(
                    self.tile.bounds, self.tile.zoom))
        else:
            output_tiles = self.config.output_pyramid.intersecting(self.tile)
        if self.config.output.METADATA["data_type"] == "raster":
            return raster.extract_from_array(in_raster=raster.create_mosaic([
                (output_tile, self.config.output.read(output_tile))
                for output_tile in output_tiles
            ]),
                                             out_tile=self.tile)
        elif self.config.output.METADATA["data_type"] == "vector":
            return list(
                chain.from_iterable([
                    self.config.output.read(output_tile)
                    for output_tile in output_tiles
                ]))
Exemple #6
0
 def _interpolate_from_baselevel(self, tile=None, baselevel=None):
     starttime = time.time()
     # resample from parent tile
     if baselevel == "higher":
         parent_tile = tile.get_parent()
         process_data = raster.resample_from_array(
             in_raster=self.get_raw_output(parent_tile,
                                           _baselevel_readonly=True),
             in_affine=parent_tile.affine,
             out_tile=tile,
             resampling=self.config.baselevels["higher"],
             nodataval=self.config.output.nodata)
     # resample from children tiles
     elif baselevel == "lower":
         mosaic, mosaic_affine = raster.create_mosaic([
             (child_tile,
              self.get_raw_output(child_tile, _baselevel_readonly=True))
             for child_tile in self.config.baselevels["tile_pyramid"].tile(
                 *tile.id).get_children()
         ])
         process_data = raster.resample_from_array(
             in_raster=mosaic,
             in_affine=mosaic_affine,
             out_tile=tile,
             resampling=self.config.baselevels["lower"],
             nodataval=self.config.output.nodata)
     elapsed = "%ss" % (round((time.time() - starttime), 3))
     logger.debug((tile.id, "generated from baselevel", elapsed))
     return process_data
Exemple #7
0
 def _interpolate_from_baselevel(self, process_tile, baselevel):
     try:
         starttime = time.time()
         # resample from parent tile
         if baselevel == "higher":
             parent_tile = self.get_raw_output(process_tile.get_parent(),
                                               _baselevel_readonly=True)
             process_data = raster.resample_from_array(
                 parent_tile.data,
                 parent_tile.affine,
                 process_tile,
                 self.config.baselevels["higher"],
                 nodataval=self.config.output.nodata)
         # resample from children tiles
         elif baselevel == "lower":
             mosaic, mosaic_affine = raster.create_mosaic([
                 self.get_raw_output(child_tile, _baselevel_readonly=True)
                 for child_tile in process_tile.get_children()
             ])
             process_data = raster.resample_from_array(
                 mosaic,
                 mosaic_affine,
                 process_tile,
                 self.config.baselevels["lower"],
                 nodataval=self.config.output.nodata)
         elapsed = "%ss" % (round((time.time() - starttime), 3))
         LOGGER.debug(
             (process_tile.id, "generated from baselevel", elapsed))
     except Exception as e:
         elapsed = "%ss" % (round((time.time() - starttime), 3))
         LOGGER.error((process_tile.id, "baselevel error", e, elapsed))
         raise
     return process_data
Exemple #8
0
def test_processing(mp_tmpdir, cleantopo_br, cleantopo_tl):
    """Test correct processing (read and write) outputs."""
    for cleantopo_process in [cleantopo_br.path, cleantopo_tl.path]:
        with mapchete.open(cleantopo_process) as mp:
            for zoom in range(6):
                tiles = []
                for tile in mp.get_process_tiles(zoom):
                    output = mp.execute(tile)
                    tiles.append((tile, output))
                    assert isinstance(output, ma.MaskedArray)
                    assert output.shape == output.shape
                    assert not ma.all(output.mask)
                    mp.write(tile, output)
                mosaic = create_mosaic(tiles)
                try:
                    temp_vrt = os.path.join(mp_tmpdir, str(zoom)+".vrt")
                    gdalbuildvrt = "gdalbuildvrt %s %s/%s/*/*.tif > /dev/null" % (
                        temp_vrt, mp.config.output.path, zoom)
                    os.system(gdalbuildvrt)
                    with rasterio.open(temp_vrt, "r") as testfile:
                        for file_item, mosaic_item in zip(
                            testfile.meta["transform"], mosaic.affine
                        ):
                            assert file_item == mosaic_item
                        band = testfile.read(1, masked=True)
                        assert band.shape == mosaic.data.shape
                        assert ma.allclose(band, mosaic.data)
                        assert ma.allclose(band.mask, mosaic.data.mask)
                finally:
                    shutil.rmtree(mp_tmpdir, ignore_errors=True)
Exemple #9
0
def test_old_style_process_class(mp_tmpdir, cleantopo_tl, old_style_process_py):
    """Test correct processing using MapcheteProcess class."""
    config = cleantopo_tl.dict
    config.update(process_file=old_style_process_py)
    with mapchete.open(config) as mp:
        for zoom in range(6):
            tiles = []
            for tile in mp.get_process_tiles(zoom):
                output = mp.execute(tile)
                tiles.append((tile, output))
                assert isinstance(output, ma.MaskedArray)
                assert output.shape == output.shape
                assert not ma.all(output.mask)
                mp.write(tile, output)
            mosaic, mosaic_affine = create_mosaic(tiles)
            try:
                temp_vrt = os.path.join(mp_tmpdir, str(zoom)+".vrt")
                gdalbuildvrt = "gdalbuildvrt %s %s/%s/*/*.tif > /dev/null" % (
                    temp_vrt, mp_tmpdir, zoom)
                os.system(gdalbuildvrt)
                with rasterio.open(temp_vrt, "r") as testfile:
                    for file_item, mosaic_item in zip(
                        testfile.meta["transform"], mosaic_affine
                    ):
                        assert file_item == mosaic_item
                    band = testfile.read(1, masked=True)
                    assert band.shape == mosaic.shape
                    assert ma.allclose(band, mosaic)
                    assert ma.allclose(band.mask, mosaic.mask)
            finally:
                shutil.rmtree(mp_tmpdir, ignore_errors=True)
Exemple #10
0
    def read(
        self, validity_check=False, indexes=None, resampling="nearest",
        dst_nodata=None, gdal_opts=None
    ):
        """
        Read reprojected & resampled input data.

        Parameters
        ----------
        validity_check : bool
            vector file: also run checks if reprojected geometry is valid,
            otherwise throw RuntimeError (default: True)

        indexes : list or int
            raster file: a list of band numbers; None will read all.
        resampling : string
            raster file: one of "nearest", "average", "bilinear" or "lanczos"
        dst_nodata : int or float, optional
            raster file: if not set, the nodata value from the source dataset
            will be used
        gdal_opts : dict
            raster file: GDAL options passed on to rasterio.Env()

        Returns
        -------
        data : list for vector files or numpy array for raster files
        """
        if self._file_type == "vector":
            if self.is_empty():
                return []
            return list(chain.from_iterable([
                read_vector_window(
                    _path, self.tile, validity_check=validity_check)
                for _, _path in self._tiles_paths
            ]))
        else:
            if self.is_empty():
                count = (len(indexes) if indexes else self._profile["count"], )
                return ma.masked_array(
                    data=np.full(
                        count + self.tile.shape, self._profile["nodata"],
                        dtype=self._profile["dtype"]),
                    mask=True
                )
            tiles = [
                (_tile, read_raster_window(
                    _path, _tile, indexes=indexes, resampling=resampling,
                    src_nodata=self._profile["nodata"], dst_nodata=dst_nodata,
                    gdal_opts=gdal_opts))
                for _tile, _path in self._tiles_paths
            ]
            return resample_from_array(
                in_raster=create_mosaic(
                    tiles=tiles, nodata=self._profile["nodata"]),
                out_tile=self.tile,
                resampling=resampling,
                nodataval=self._profile["nodata"])
Exemple #11
0
 def _read_existing_output(self, tile, output_tiles):
     if self.config.output.METADATA["data_type"] == "raster":
         mosaic, affine = raster.create_mosaic([
             (output_tile, self.read(output_tile))
             for output_tile in output_tiles
         ])
         return raster.extract_from_array(mosaic, affine, tile)
     elif self.config.output.METADATA["data_type"] == "vector":
         return list(
             chain.from_iterable(
                 [self.read(output_tile) for output_tile in output_tiles]))
Exemple #12
0
    def _interpolate_from_baselevel(self, baselevel=None):
        # This is a special tile derived from a pyramid which has the pixelbuffer setting
        # from the output pyramid but metatiling from the process pyramid. This is due to
        # performance reasons as for the usual case overview tiles do not need the
        # process pyramid pixelbuffers.
        tile = self.config_baselevels["tile_pyramid"].tile(*self.tile.id)

        # get output_tiles that intersect with process tile
        output_tiles = (list(
            self.output_reader.pyramid.tiles_from_bounds(
                tile.bounds, tile.zoom)) if tile.pixelbuffer >
                        self.output_reader.pyramid.pixelbuffer else
                        self.output_reader.pyramid.intersecting(tile))

        with Timer() as t:
            # resample from parent tile
            if baselevel == "higher":
                parent_tile = self.tile.get_parent()
                process_data = raster.resample_from_array(
                    self.output_reader.read(parent_tile),
                    in_affine=parent_tile.affine,
                    out_tile=self.tile,
                    resampling=self.config_baselevels["higher"],
                    nodata=self.output_reader.output_params["nodata"])
            # resample from children tiles
            elif baselevel == "lower":
                if self.output_reader.pyramid.pixelbuffer:
                    lower_tiles = set([
                        y for y in chain(*[
                            self.output_reader.pyramid.tiles_from_bounds(
                                x.bounds, x.zoom + 1) for x in output_tiles
                        ])
                    ])
                else:
                    lower_tiles = [
                        y for y in chain(
                            *[x.get_children() for x in output_tiles])
                    ]
                mosaic = raster.create_mosaic(
                    [(lower_tile, self.output_reader.read(lower_tile))
                     for lower_tile in lower_tiles],
                    nodata=self.output_reader.output_params["nodata"])
                process_data = raster.resample_from_array(
                    in_raster=mosaic.data,
                    in_affine=mosaic.affine,
                    out_tile=self.tile,
                    resampling=self.config_baselevels["lower"],
                    nodata=self.output_reader.output_params["nodata"])
        logger.debug((self.tile.id, "generated from baselevel", str(t)))
        return process_data
Exemple #13
0
def test_create_mosaic_antimeridian():
    """Create mosaic using tiles on opposing antimeridian sides."""
    zoom = 5
    row = 0
    pixelbuffer = 5
    tp = BufferedTilePyramid("geodetic", pixelbuffer=pixelbuffer)
    west = tp.tile(zoom, row, 0)
    east = tp.tile(zoom, row, tp.matrix_width(zoom) - 1)
    for tile in [west, east]:
        tile.data = np.ones(tile.shape)
    mosaic = raster.create_mosaic([west, east])
    assert isinstance(mosaic, raster.ReferencedRaster)
    # Huge array gets initialized because the two tiles are on opposing sides
    # of the projection area. The below test should pass if the tiles are
    # stitched together next to each other.
    assert mosaic.data.shape == (1, west.height,
                                 west.width * 2 - 2 * pixelbuffer)
Exemple #14
0
 def extract_subset(self, input_data_tiles=None, out_tile=None):
     """
     Extract subset from multiple tiles.
     input_data_tiles : list of (``Tile``, process data) tuples
     out_tile : ``Tile``
     Returns
     -------
     NumPy array or list of features.
     """
     if self.METADATA["data_type"] == "raster":
         mosaic = create_mosaic(input_data_tiles)
         return extract_from_array(in_raster=prepare_array(
             mosaic.data,
             nodata=self.output_params["nodata"],
             dtype=self.output_params["dtype"]),
                                   in_affine=mosaic.affine,
                                   out_tile=out_tile)
     elif self.METADATA["data_type"] == "vector":
         return [
             feature for feature in list(
                 chain.from_iterable(
                     [features for _, features in input_data_tiles]))
             if shape(feature["geometry"]).intersects(out_tile.bbox)
         ]
Exemple #15
0
def test_create_mosaic_errors():
    """Check error handling of create_mosaic()."""
    tp_geo = BufferedTilePyramid("geodetic")
    tp_mer = BufferedTilePyramid("mercator")
    geo_tile = tp_geo.tile(1, 0, 0)
    geo_tile_data = np.ndarray(geo_tile.shape)
    mer_tile = tp_mer.tile(1, 1, 0)
    mer_tile_data = np.ndarray(mer_tile.shape)
    # tiles error
    with pytest.raises(TypeError):
        create_mosaic("invalid tiles")
    with pytest.raises(TypeError):
        create_mosaic(["invalid tiles"])
    # CRS error
    with pytest.raises(ValueError):
        create_mosaic([(geo_tile, geo_tile_data), (mer_tile, mer_tile_data)])
    # zoom error
    with pytest.raises(ValueError):
        diff_zoom = tp_geo.tile(2, 1, 0)
        diff_zoom_data = np.ndarray(diff_zoom.shape)
        create_mosaic([(geo_tile, geo_tile_data), (diff_zoom, diff_zoom_data)])
    # tile data error
    with pytest.raises(TypeError):
        # for one tile
        create_mosaic([(geo_tile, None)])
    with pytest.raises(TypeError):
        # for multiple tiles
        create_mosaic([(geo_tile, None), (geo_tile, None)])
    # tile data type error
    with pytest.raises(TypeError):
        diff_type = tp_geo.tile(1, 1, 0)
        diff_type_data = np.ndarray(diff_zoom.shape).astype("int")
        create_mosaic([(geo_tile, geo_tile_data), (diff_type, diff_type_data)])
Exemple #16
0
def test_create_mosaic_antimeridian():
    """Create mosaic using tiles on opposing antimeridian sides."""
    zoom = 5
    row = 0
    pixelbuffer = 5
    tp = BufferedTilePyramid("geodetic", pixelbuffer=pixelbuffer)
    west = tp.tile(zoom, row, 0)
    east = tp.tile(zoom, row, tp.matrix_width(zoom) - 1)
    mosaic = create_mosaic([(west, np.ones(west.shape).astype("uint8")),
                            (east, np.ones(east.shape).astype("uint8") * 2)])
    assert isinstance(mosaic, ReferencedRaster)

    # Huge array gets initialized because the two tiles are on opposing sides of the
    # projection area. The below test should pass if the tiles are stitched together next
    # to each other.
    assert mosaic.data.shape == (1, west.height,
                                 west.width * 2 - 2 * pixelbuffer)
    assert mosaic.data[0][0][0] == 2
    assert mosaic.data[0][0][-1] == 1

    # If tiles from opposing sides from Antimeridian are mosaicked it will happen that the
    # output mosaic exceeds the CRS bounds (obviously). In such a case the mosaicking
    # function shall make sure that the larger part of the output mosaic shall be inside
    # the CRS bounds.

    # (1) mosaic crosses Antimeridian in the West, larger part is on Western hemisphere:
    tiles_ids = [
        # Western hemisphere tiles
        (zoom, row, 0),
        (zoom, row, 1),
        # Eastern hemisphere tile
        (zoom, row, tp.matrix_width(zoom) - 1),
    ]
    tiles = [(tp.tile(*tile_id), np.ones(tp.tile(*tile_id).shape))
             for tile_id in tiles_ids]
    mosaic = create_mosaic(tiles)
    control_bounds = Bounds(
        # Eastern tile has to be shifted
        -(360 - tp.tile(*tiles_ids[2]).left),
        tp.tile(*tiles_ids[2]).bottom,
        tp.tile(*tiles_ids[1]).right,
        tp.tile(*tiles_ids[1]).top,
    )
    assert mosaic.bounds == control_bounds

    # (2) mosaic crosses Antimeridian in the West, larger part is on Eastern hemisphere:
    tiles_ids = [
        # Western hemisphere tile
        (zoom, row, 0),
        # Eastern hemisphere tiles
        (zoom, row, tp.matrix_width(zoom) - 1),
        (zoom, row, tp.matrix_width(zoom) - 2),
    ]
    tiles = [(tp.tile(*tile_id), np.ones(tp.tile(*tile_id).shape))
             for tile_id in tiles_ids]
    mosaic = create_mosaic(tiles)
    control_bounds = Bounds(
        tp.tile(*tiles_ids[2]).left,
        tp.tile(*tiles_ids[2]).bottom,
        # Western tile has to be shifted
        360 + tp.tile(*tiles_ids[0]).right,
        tp.tile(*tiles_ids[0]).top,
    )
    assert mosaic.bounds == control_bounds