Example #1
0
def test_raster_closer_than_resolution_to_roi():
    raster_close_to_roi = make_test_raster(
        1,
        [1],
        height=2255,
        width=6500,
        affine=Affine(1.000056241624503, -0.0001677700491717716,
                      251130.52371896777, -0.00011325628093143738,
                      -1.0000703876618153, 2703061.4308057753),
        crs=CRS.from_epsg(32613),
    )
    raster_intersecting_roi = make_test_raster(
        1,
        [1],
        height=3515,
        width=6497,
        affine=Affine(1.000063460933417, -2.935588943753421e-05,
                      250953.40276071787, -3.26265458078499e-05,
                      -1.000053742629815, 2703428.138070052),
        crs=CRS.from_epsg(32613),
    )
    roi = GeoVector.from_bounds(251726, 2696110, 256422, 2700806,
                                CRS.from_epsg(32613))
    merge_all(
        [raster_close_to_roi, raster_intersecting_roi],
        roi=roi,
        dest_resolution=(1, 1),
        merge_strategy=MergeStrategy.INTERSECTION,
    )
Example #2
0
def test_merge_all_different_crs(crop, recwarn):
    roi = GeoVector(
        Polygon.from_bounds(-6321833, -3092272, -6319273, -3089712),
        WEB_MERCATOR_CRS)
    affine = Affine.translation(-57, -26) * Affine.scale(0.00083, -0.00083)
    expected_resolution = 10
    expected_crs = WEB_MERCATOR_CRS

    # from memory
    raster_0 = make_test_raster(1, [1],
                                height=1200,
                                width=1200,
                                affine=affine,
                                crs=WGS84_CRS)
    result_0 = merge_all([raster_0],
                         roi=roi,
                         dest_resolution=expected_resolution,
                         crs=expected_crs,
                         crop=crop)
    assert (result_0.resolution() == expected_resolution)
    assert (result_0.crs == expected_crs)
    assert (result_0.footprint().envelope.almost_equals(roi.envelope,
                                                        decimal=3))

    # from file
    path = "/vsimem/raster_for_test.tif"
    result_0.save(path)
    raster_1 = GeoRaster2.open(path)
    result_1 = merge_all([raster_1],
                         roi=roi,
                         dest_resolution=expected_resolution,
                         crs=expected_crs,
                         crop=crop)

    assert (result_1.resolution() == expected_resolution)
    assert (result_1.crs == expected_crs)
    assert (result_1.footprint().envelope.almost_equals(roi.envelope,
                                                        decimal=3))
    assert (result_0 == result_1)

    # preserve the original resolution if dest_resolution is not provided
    raster_2 = make_test_raster(1, [1],
                                height=1200,
                                width=1200,
                                affine=affine,
                                crs=WGS84_CRS)
    result_2 = merge_all([raster_2], roi=roi, crs=expected_crs, crop=crop)
    assert pytest.approx(result_2.resolution()) == 97.9691
Example #3
0
def test_merge_multi_band_single_raster_returns_itself_for_all_strategies():
    for ms in MergeStrategy:
        raster = black_and_white_raster([1, 2, 3])
        raster2 = merge_all([raster],
                            roi=raster.footprint(),
                            merge_strategy=ms)
        assert (raster2 == raster)
Example #4
0
def test_merge_does_not_uncover_masked_pixels():
    # See https://github.com/satellogic/telluric/issues/65
    affine = Affine.translation(0, 2) * Affine.scale(1, -1)

    rs_a = GeoRaster2(
        image=np.ma.masked_array(
            [[[100, 89], [100, 89]], [[110, 99], [110, 99]]],
            [[[False, True], [False, True]], [[False, True], [False, True]]],
            dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['red', 'green'],
    )

    rs_b = GeoRaster2(
        image=np.array([[[0, 210], [0, 210]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['green'],
    )

    expected_image = np.ma.masked_array(
        [[[100, 89], [100, 89]], [[110, 99], [110, 99]]],
        [[[False, True], [False, True]], [[False, True], [False, True]]],
        dtype=np.uint8)

    result = merge_all([rs_a, rs_b],
                       rs_a.footprint()).limit_to_bands(['red', 'green'])

    assert_array_equal(np.ma.filled(result.image, 0),
                       np.ma.filled(expected_image, 0))
    assert_array_equal(result.image.mask, expected_image.mask)
Example #5
0
def test_merge_all_on_non_overlapping_rasters_returns_first_raster():
    affine1 = Affine.translation(10, 12) * Affine.scale(1, -1)
    affine2 = Affine.translation(100, 120) * Affine.scale(1, -1)
    raster1 = make_test_raster(value=1, band_names=['blue'], affine=affine1, height=30, width=40)
    raster2 = make_test_raster(value=2, band_names=['blue'], affine=affine2, height=30, width=40)
    merged = merge_all([raster1, raster2], raster1.footprint())
    assert merged == raster1
Example #6
0
def test_get_tile_merge_tiles(tile):
    raster1_path = './tests/data/raster/overlap1.tif'
    raster2_path = './tests/data/raster/overlap2.tif'
    raster1 = GeoRaster2.open(raster1_path)
    raster2 = GeoRaster2.open(raster2_path)

    features = [
        GeoFeature(raster1.footprint().reproject(new_crs=WGS84_CRS),
                   {'raster_url': raster1_path, 'created': datetime.now()}),
        GeoFeature(raster2.footprint().reproject(new_crs=WGS84_CRS),
                   {'raster_url': raster2_path, 'created': datetime.now()}),
    ]

    fc = FeatureCollection(features)
    bounds = mercantile.xy_bounds(*tile)
    eroi = GeoVector.from_bounds(xmin=bounds.left, xmax=bounds.right,
                                 ymin=bounds.bottom, ymax=bounds.top,
                                 crs=WEB_MERCATOR_CRS)
    expected_tile = merge_all([raster1.get_tile(*tile), raster2.get_tile(*tile)], roi=eroi)
    merged = fc.get_tile(*tile, sort_by='created')
    if merged is not None:
        assert merged == expected_tile
    else:
        assert expected_tile.image.mask.all()
        assert (expected_tile.image.data == 0).all()
Example #7
0
def test_merge_single_band_single_raster_returns_itself_for_all_strategies():
    for ms in MergeStrategy:
        raster = make_test_raster(88, [1])
        raster2 = merge_all([raster],
                            roi=raster.footprint(),
                            merge_strategy=ms)
        assert (raster2 == raster)
Example #8
0
 def _merge_rasters(self, rasters, z):
     # the import is here to eliminate recursive import
     from telluric.georaster import merge_all
     actual_roi = rasters[0].footprint()
     merge_params = {
         'dest_resolution': MERCATOR_RESOLUTION_MAPPING[z],
         'ul_corner': (actual_roi.left, actual_roi.top),
         'shape': (256, 256),
         'crs': WEB_MERCATOR_CRS,
     }
     return merge_all(rasters, **merge_params)
Example #9
0
def test_rasters_covering_different_overlapping_areas_on_y():
    affine_a = Affine.translation(1, 2) * Affine.scale(1, -1)
    raster_a = make_test_raster(1, [1], height=20, width=20, affine=affine_a)
    affine_b = Affine.translation(1, -9) * Affine.scale(1, -1)
    raster_b = make_test_raster(2, [1], height=20, width=20, affine=affine_b)
    roi = GeoVector.from_bounds(xmin=1, ymin=-29, xmax=21, ymax=2, crs=constants.WEB_MERCATOR_CRS)
    rasters = [raster_a, raster_b]
    merged = merge_all(rasters, roi)
    assert(merged.affine.almost_equals(affine_a))
    assert(not merged.image.mask.all())
    assert((merged.image.data[0, 0:20, 0:20] == 1).all())
    assert((merged.image.data[0, 21:30, 0:20] == 2).all())
Example #10
0
    def get_tile(self, x, y, z, sort_by=None, desc=False, bands=None):
        """Generate mercator tile from rasters in FeatureCollection.

        Parameters
        ----------
        x: int
            x coordinate of tile
        y: int
            y coordinate of tile
        z: int
            zoom level
        sort_by: str
            attribute in feature to sort by
        desc: bool
            True for descending order, False for ascending
        bands: list
            list of indices of requested bads, default None which returns all bands

        Returns
        -------
        GeoRaster2

        """
        bb = mercantile.xy_bounds(x, y, z)
        roi = GeoVector.from_bounds(xmin=bb.left,
                                    ymin=bb.bottom,
                                    xmax=bb.right,
                                    ymax=bb.top,
                                    crs=WEB_MERCATOR_CRS)

        filtered_fc = self.filter(roi)

        def _get_tiled_feature(feature):
            return feature.get_tiled_feature(x, y, z, bands)

        with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executer:
            tiled_features = list(
                executer.map(_get_tiled_feature,
                             filtered_fc,
                             timeout=CONCURRENCY_TIMEOUT))

        # tiled_features can be sort for different merge strategies
        if sort_by is not None:
            tiled_features = sorted(tiled_features,
                                    reverse=desc,
                                    key=lambda f: f[sort_by])

        tiles = [f['tile'] for f in tiled_features]
        if tiles:
            tile = merge_all(tiles, roi)
            return tile
        else:
            return None
Example #11
0
def test_merge_all_non_overlapping_has_correct_metadata():
    # See https://github.com/satellogic/telluric/issues/65
    affine = Affine.translation(0, 2) * Affine.scale(1, -1)

    rs1 = GeoRaster2(
        image=np.array([[[100, 0], [100, 0]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['red'],
        nodata=0,
    )

    rs2 = GeoRaster2(
        image=np.array([[[110, 0], [110, 0]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['green'],
        nodata=0,
    )

    rs3 = GeoRaster2(
        image=np.array([[[0, 200], [0, 200]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['red'],
        nodata=0,
    )

    rs4 = GeoRaster2(
        image=np.array([[[0, 210], [0, 210]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['green'],
        nodata=0,
    )

    expected_metadata = GeoRaster2(image=np.ma.masked_array([[
        [0, 2],
        [0, 2],
    ], [
        [1, 3],
        [1, 3],
    ]], np.ma.nomask),
                                   affine=affine,
                                   crs=WGS84_CRS,
                                   band_names=['red', 'green'])

    metadata = merge_all([rs1, rs2, rs3, rs4],
                         rs1.footprint(),
                         pixel_strategy=PixelStrategy.INDEX)

    assert metadata == expected_metadata
Example #12
0
def test_merge_all_non_overlapping_covers_all():
    # See https://github.com/satellogic/telluric/issues/65
    affine = Affine.translation(0, 2) * Affine.scale(1, -1)

    rs1 = GeoRaster2(
        image=np.array([[[100, 0], [100, 0]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['red'],
        nodata=0,
    )

    rs2 = GeoRaster2(
        image=np.array([[[110, 0], [110, 0]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['green'],
        nodata=0,
    )

    rs3 = GeoRaster2(
        image=np.array([[[0, 200], [0, 200]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['red'],
        nodata=0,
    )

    rs4 = GeoRaster2(
        image=np.array([[[0, 210], [0, 210]]], dtype=np.uint8),
        affine=affine,
        crs=WGS84_CRS,
        band_names=['green'],
        nodata=0,
    )

    expected_image = np.ma.masked_array(
        [[[100, 200], [100, 200]], [[110, 210], [110, 210]]], False)

    result = merge_all([rs1, rs2, rs3, rs4],
                       rs1.footprint()).limit_to_bands(['red', 'green'])

    assert_array_equal(result.image.data, expected_image.data)
    assert_array_equal(result.image.mask, expected_image.mask)
Example #13
0
def test_rasters_covering_different_areas_with_gap_on_x():
    affine_a = Affine.translation(1, 2) * Affine.scale(1, -1)
    raster_a = make_test_raster(1, [1], height=10, width=10, affine=affine_a)
    affine_b = Affine.translation(21, 2) * Affine.scale(1, -1)
    raster_b = make_test_raster(2, [1], height=10, width=10, affine=affine_b)
    roi = GeoVector.from_bounds(xmin=1,
                                ymin=-8,
                                xmax=30,
                                ymax=2,
                                crs=WEB_MERCATOR_CRS)
    rasters = [raster_a, raster_b]
    merged = merge_all(rasters, roi)
    assert (merged.affine.almost_equals(affine_a))
    assert (not merged.image.mask[0, 0:10, 0:10].all())
    assert (merged.image.mask[0, 0:10, 10:20].all())
    assert (not merged.image.mask[0, 0:10, 20:30].all())
    assert ((merged.image.data[0, 0:10, 0:10] == 1).all())
    assert ((merged.image.data[0, 0:10, 11:20] == 0).all())
    assert ((merged.image.data[0, 0:10, 21:30] == 2).all())
Example #14
0
def test_rasters_close_than_resolution_to_roi_2():
    one_affine = Affine(1, 0, 598847, 0, -1, 3471062)
    other_affine = Affine(0.9998121393135052104028, -0.000563382202975665213,
                          596893.24732190347276628, -0.000249934917683214408,
                          -1.000473374252140335016, 3466367.0648421039804816)
    one = make_test_raster(1, [1],
                           height=4696,
                           width=4696,
                           affine=one_affine,
                           crs=CRS.from_epsg(32641))
    other = make_test_raster(2, [1],
                             height=2616,
                             width=5402,
                             affine=other_affine,
                             crs=CRS.from_epsg(32641))
    roi = GeoVector.from_bounds(598847.0000000002,
                                3466365.999999999,
                                603542.9999999995,
                                3471062,
                                crs=one.crs)
    merged = merge_all([one, other], dest_resolution=(1, 1), roi=roi)
    assert merged == one
Example #15
0
def test_merge_multi_band_multi_size_raster_3():
    rasters = get_rasters()
    raster2 = merge_all(rasters, roi=rasters[3].footprint())
    assert(raster2 == rasters[3])
Example #16
0
def test_merge_multi_band_multi_raster_smaller_roi_returns_itself():
    rasters = [black_and_white_raster([1, 2, 3])]
    raster = black_and_white_raster([1, 2, 3], height=7, width=6)
    raster2 = merge_all(rasters, roi=raster.footprint())
    assert(raster2 == raster)
Example #17
0
def test_merge_multi_band_multi_raster_returns_itself():
    rasters = [black_and_white_raster([1, 2, 3]) for i in range(10)]
    raster = black_and_white_raster([1, 2, 3])
    raster2 = merge_all(rasters, roi=raster.footprint())
    assert(raster2 == black_and_white_raster([1, 2, 3]))
Example #18
0
    def rasterize(self,
                  dest_resolution,
                  *,
                  polygonize_width=0,
                  crs=WEB_MERCATOR_CRS,
                  fill_value=None,
                  bounds=None,
                  dtype=None,
                  **polygonize_kwargs):
        """Binarize a FeatureCollection and produce a raster with the target resolution.

        Parameters
        ----------
        dest_resolution: float
            Resolution in units of the CRS.
        polygonize_width : int, optional
            Width for the polygonized features (lines and points) in pixels, default to 0 (they won't appear).
        crs : ~rasterio.crs.CRS, dict (optional)
            Coordinate system, default to :py:data:`telluric.constants.WEB_MERCATOR_CRS`.
        fill_value : float or function, optional
            Value that represents data, default to None (will default to :py:data:`telluric.rasterization.FILL_VALUE`.
            If given a function, it must accept a single :py:class:`~telluric.features.GeoFeature` and return a numeric
            value.
        nodata_value : float, optional
            Nodata value, default to None (will default to :py:data:`telluric.rasterization.NODATA_VALUE`.
        bounds : GeoVector, optional
            Optional bounds for the target image, default to None (will use the FeatureCollection convex hull).
        dtype : numpy.dtype, optional
            dtype of the result, required only if fill_value is a function.
        polygonize_kwargs : dict
            Extra parameters to the polygonize function.

        """
        # Avoid circular imports
        from telluric.georaster import merge_all, MergeStrategy
        from telluric.rasterization import rasterize, NODATA_DEPRECATION_WARNING

        # Compute the size in real units and polygonize the features
        if not isinstance(polygonize_width, int):
            raise TypeError("The width in pixels must be an integer")

        if polygonize_kwargs.pop("nodata_value", None):
            warnings.warn(NODATA_DEPRECATION_WARNING, DeprecationWarning)

        # If the pixels width is 1, render points as squares to avoid missing data
        if polygonize_width == 1:
            polygonize_kwargs.update(cap_style_point=CAP_STYLE.square)

        # Reproject collection to target CRS
        if (self.crs is not None and self.crs != crs):
            reprojected = self.reproject(crs)
        else:
            reprojected = self

        width = polygonize_width * dest_resolution
        polygonized = [
            feature.polygonize(width, **polygonize_kwargs)
            for feature in reprojected
        ]

        # Discard the empty features
        shapes = [
            feature.geometry.get_shape(crs) for feature in polygonized
            if not feature.is_empty
        ]

        if bounds is None:
            bounds = self.envelope

        if bounds.area == 0.0:
            raise ValueError("Specify non-empty ROI")

        if not len(self):
            fill_value = None

        if callable(fill_value):
            if dtype is None:
                raise ValueError(
                    "dtype must be specified for multivalue rasterization")

            rasters = []
            for feature in self:
                rasters.append(
                    feature.geometry.rasterize(dest_resolution,
                                               fill_value=fill_value(feature),
                                               bounds=bounds,
                                               dtype=dtype,
                                               crs=crs))

            return merge_all(rasters,
                             bounds.reproject(crs),
                             dest_resolution,
                             merge_strategy=MergeStrategy.INTERSECTION)

        else:
            return rasterize(shapes,
                             crs,
                             bounds.get_shape(crs),
                             dest_resolution,
                             fill_value=fill_value,
                             dtype=dtype)