Exemple #1
0
def test_rotation_matrix_pivot():
    """A rotation matrix with pivot has expected elements"""
    rot = Affine.rotation(90.0, pivot=(1.0, 1.0))
    exp = (Affine.translation(1.0, 1.0)
           * Affine.rotation(90.0)
           * Affine.translation(-1.0, -1.0))
    for r, e in zip(rot, exp):
        assert round(r, 15) == round(e, 15)
 def test_inverse(self):
     seq_almost_equal(~Affine.identity(), Affine.identity())
     seq_almost_equal(
         ~Affine.translation(2, -3), Affine.translation(-2, 3))
     seq_almost_equal(
         ~Affine.rotation(-33.3), Affine.rotation(33.3))
     t = Affine(1, 2, 3, 4, 5, 6)
     seq_almost_equal(~t * t,  Affine.identity())
Exemple #3
0
def test_roundtrip():
    point = (12, 5)
    trans = Affine.translation(3, 4)
    rot37 = Affine.rotation(37.)
    point_prime = (trans * rot37) * point
    roundtrip_point = ~(trans * rot37) * point_prime
    seq_almost_equal(point, roundtrip_point)
Exemple #4
0
def chop(bands, dst_transform, size, output_file, photometric):
    for y in range(0, bands.shape[1], size):
        for x in range(0, bands.shape[2], size):

            # crop a grid square ('patch')
            patch_affine = Affine(*dst_transform) * Affine.translation(x, y)
            patch = numpy.zeros((bands.shape[0], size, size), dtype=rasterio.uint16)
            my = min(y + size, bands.shape[1])
            mx = min(x + size, bands.shape[2])
            for i, band in enumerate(bands):
                patch[i, 0:my-y, 0:mx-x] = band[y:my, x:mx]


            if(numpy.max(patch) > 0):
                out = output_file.replace('{x}', str(x)).replace('{y}', str(y))
                with rasterio.open(out,
                                    mode='w', driver='GTiff',
                                    width=patch.shape[2],
                                    height=patch.shape[1],
                                    count=patch.shape[0],
                                    dtype=numpy.uint8,
                                    nodata=0,
                                    photometric=photometric,
                                    transform=patch_affine,
                                    crs=projection) as dst:
                    for i, band in enumerate(patch):
                        dst.write_band(i + 1, band.astype(rasterio.uint8))
def coordinates( fn ):
	'''
	take a raster file as input and return the centroid coords for each 
	of the grid cells as a pair of numpy 2d arrays (longitude, latitude)
	'''
	import rasterio
	import numpy as np
	from affine import Affine
	from pyproj import Proj, transform

	# Read raster
	with rasterio.open(fn) as r:
		T0 = r.affine  # upper-left pixel corner affine transform
		p1 = Proj(r.crs)
		A = r.read_band(1)  # pixel values

	# All rows and columns
	cols, rows = np.meshgrid(np.arange(A.shape[1]), np.arange(A.shape[0]))

	# Get affine transform for pixel centres
	T1 = T0 * Affine.translation(0.5, 0.5)
	# Function to convert pixel row/column index (from 0) to easting/northing at centre
	rc2en = lambda r, c: (c, r) * T1

	# All eastings and northings (there is probably a faster way to do this)
	eastings, northings = np.vectorize(rc2en, otypes=[np.float, np.float])(rows, cols)

	# Project all longitudes, latitudes
	# longs, lats = transform(p1, p1.to_latlong(), eastings, northings)
	# return longs, lats
	return eastings, northings
Exemple #6
0
def test_rotation_angle():
    assert Affine.identity().rotation_angle == 0.0
    assert Affine.scale(2).rotation_angle == 0.0
    assert Affine.scale(2, 1).rotation_angle == 0.0
    assert Affine.translation(32, -47).rotation_angle == pytest.approx(0.0)
    assert Affine.rotation(30).rotation_angle == pytest.approx(30)
    assert Affine.rotation(-150).rotation_angle == pytest.approx(-150)
Exemple #7
0
 def __init__(self, gzx, gzy, yld, ff, wind, wd, shear, tob=0, dunits='km', wunits='km/h', shearunits='m/s-km', yunits='kT'):
     self.translation = ~Affine.translation(convert_units(gzx, dunits, 'mi'), convert_units(gzy, dunits, 'mi')) # translate coordinates relative to GZ (st. mi)
     self.wd = wd # wind direction in degrees (0=N, 90=E, etc.)
     self.yld = convert_units(yld, yunits, 'MT') # yield (MT)
     self.ff = ff # fission fraction, 0 < ff <= 1.0
     self.wind = convert_units(wind, wunits, 'mph') # wind speed (mi/hr)
     self.shear = convert_units(shear, shearunits, 'mph/kilofoot') # wind shear in mi/hr-kilofoot
     self.tob = tob # time of burst (hrs)
     # FORTRAN is ugly in any language
     # Store these values in the WSEG10 object to avoid recalculating them
     d = np.log(self.yld) + 2.42 # physically meaningless, but occurs twice
     # According to Hanifen, "The cloud is initially formed because the nuclear
     # fireball vaporizes both the surface of the earth at ground zero and the
     # weapon itself. The activity contained in the cloud is both neutron induced
     # and fission. After formation, the fireball rises and begins to cool at its
     # outer edges faster than the center thereby creating the typical torroidal
     # currents associated with the nuclear cloud. WSEG arbitrarily assumes that
     # the cloud will rise to a maximum center height within fifteen minutes and
     # then stabilize." 
     self.H_c =  44 + 6.1 * np.log(yld) - 0.205 * abs(d) * d # cloud center height
     lnyield = np.log(self.yld)
     self.s_0 = np.exp(0.7 + lnyield / 3 - 3.25 / (4.0 + (lnyield + 5.4)**2)) #sigma_0
     self.s_02 = self.s_0**2
     self.s_h = 0.18 * self.H_c # sigma_h
     self.T_c = 1.0573203 * (12 * (self.H_c / 60) - 2.5 * (self.H_c / 60)**2) * (1 - 0.5 * np.exp(-1 * (self.H_c / 25)**2)) # time constant
     self.L_0 = wind * self.T_c # L_0, used by g(x)
     self.L_02 = self.L_0**2
     self.s_x2 = self.s_02 * (self.L_02 + 8 * self.s_02) / (self.L_02 + 2 * self.s_02)
     self.s_x = np.sqrt(self.s_x2) # sigma_x
     self.L_2 = self.L_02 + 2 * self.s_x2
     self.L = np.sqrt(self.L_2) # L
     self.n = (ff * self.L_02 + self.s_x2) / (self.L_02 + 0.5 * self.s_x2) # n
     self.a_1 = 1 / (1 + ((0.001 * self.H_c * wind) / self.s_0)) # alpha_1
 def test_is_rectilinear(self):
     assert Affine.identity().is_rectilinear
     assert Affine.scale(2.5, 6.1).is_rectilinear
     assert Affine.translation(4, -1).is_rectilinear
     assert Affine.rotation(90).is_rectilinear
     assert not Affine.shear(4, -1).is_rectilinear
     assert not Affine.rotation(-26).is_rectilinear
Exemple #9
0
    def from_geopolygon(cls, geopolygon, resolution, crs=None, align=None):
        """
        :type geopolygon: GeoPolygon
        :param resolution: (x_resolution, y_resolution)
        :param CRS crs: CRS to use, if different from the geopolygon
        :param (float,float) align: Alight geobox such that point 'align' lies on the pixel boundary.
        :rtype: GeoBox
        """
        # TODO: currently only flipped Y-axis data is supported

        assert resolution[1] > 0
        assert resolution[0] < 0

        align = align or (0.0, 0.0)
        assert 0.0 <= align[1] <= abs(resolution[1])
        assert 0.0 <= align[0] <= abs(resolution[0])

        if crs is None:
            crs = geopolygon.crs
        else:
            geopolygon = geopolygon.to_crs(crs)

        def align_pix(val, res, off):
            return math.floor((val-off)/res) * res + off

        bounding_box = geopolygon.boundingbox
        left = align_pix(bounding_box.left, resolution[1], align[1])
        top = align_pix(bounding_box.top, resolution[0], align[0])
        affine = (Affine.translation(left, top) * Affine.scale(resolution[1], resolution[0]))
        return GeoBox(crs=crs,
                      affine=affine,
                      width=int(math.ceil((bounding_box.right-left)/resolution[1])),
                      height=int(math.ceil((bounding_box.bottom-top)/resolution[0])))
 def test_is_conformal(self):
     assert Affine.identity().is_conformal
     assert Affine.scale(2.5, 6.1).is_conformal
     assert Affine.translation(4, -1).is_conformal
     assert Affine.rotation(90).is_conformal
     assert Affine.rotation(-26).is_conformal
     assert not Affine.shear(4, -1).is_conformal
def coordinates( fn=None, meta=None, numpy_array=None, input_crs=None, to_latlong=False ):
    '''
    take a raster file as input and return the centroid coords for each 
    of the grid cells as a pair of numpy 2d arrays (longitude, latitude)
    User must give either:
        fn = path to the rasterio readable raster
    OR
        meta & numpy ndarray (usually obtained by rasterio.open(fn).read( 1 )) 
        where:
        meta = a rasterio style metadata dictionary ( rasterio.open(fn).meta )
        numpy_array = 2d numpy array representing a raster described by the meta
    input_crs = rasterio style proj4 dict, example: { 'init':'epsg:3338' }
    to_latlong = boolean.  If True all coordinates will be returned as EPSG:4326
                         If False all coordinates will be returned in input_crs
    returns:
        meshgrid of longitudes and latitudes
    borrowed from here: https://gis.stackexchange.com/a/129857
    ''' 
    
    import rasterio
    import numpy as np
    from affine import Affine
    from pyproj import Proj, transform

    if fn:
        # Read raster
        with rasterio.open( fn ) as r:
            T0 = r.affine  # upper-left pixel corner affine transform
            p1 = Proj( r.crs )
            A = r.read( 1 )  # pixel values

    elif (meta is not None) & (numpy_array is not None):
        A = numpy_array
        if input_crs != None:
            p1 = Proj( input_crs )
            T0 = meta[ 'affine' ]
        else:
            p1 = None
            T0 = meta[ 'affine' ]
    else:
        BaseException( 'check inputs' )

    # All rows and columns
    cols, rows = np.meshgrid(np.arange(A.shape[1]), np.arange(A.shape[0]))
    # Get affine transform for pixel centres
    T1 = T0 * Affine.translation( 0.5, 0.5 )
    # Function to convert pixel row/column index (from 0) to easting/northing at centre
    rc2en = lambda r, c: ( c, r ) * T1
    # All eastings and northings -- this is much faster than np.apply_along_axis
    eastings, northings = np.vectorize(rc2en, otypes=[np.float, np.float])(rows, cols)

    if to_latlong == False:
        return eastings, northings
    elif (to_latlong == True) & (input_crs != None):
        # Project all longitudes, latitudes
        longs, lats = transform(p1, p1.to_latlong(), eastings, northings)
        return longs, lats
    else:
        BaseException( 'cant reproject to latlong without an input_crs' )
Exemple #12
0
 def test_rotation_constructor_with_pivot(self):
     assert_equal(tuple(Affine.rotation(60)),
         tuple(Affine.rotation(60, pivot=(0, 0))))
     rot = Affine.rotation(27, pivot=(2, -4))
     r = math.radians(27)
     s, c = math.sin(r), math.cos(r)
     assert_equal(
         tuple(rot),
         (c, -s, 2 - 2 * c - 4 * s,
          s, c, -4 - 2 * s + 4 * c,
          0, 0, 1))
     assert_equal(tuple(Affine.rotation(0, (-3, 2))),
                  tuple(Affine.identity()))
     rot_pivot = Affine.rotation(27, pivot=(2, -4))
     trans_rot_trans = (Affine.translation(2, -4) * Affine.rotation(27) *
                        Affine.translation(-2, 4))
     seq_almost_equal(tuple(rot_pivot), tuple(trans_rot_trans))
def transform_from_latlon( lat, lon ):
	''' simple way to make an affine transform from lats and lons coords '''
	from affine import Affine
	lat = np.asarray( lat )
	lon = np.asarray(lon)
	trans = Affine.translation(lon[0], lat[0])
	scale = Affine.scale(lon[1] - lon[0], lat[1] - lat[0])
	return trans * scale
 def test_translation_constructor(self):
     trans = Affine.translation(2, -5)
     assert isinstance(trans, Affine)
     assert_equal(
         tuple(trans),
         (1, 0,  2,
          0, 1, -5,
          0, 0,  1))
Exemple #15
0
def test_associative():
    point = (12, 5)
    trans = Affine.translation(-10., -5.)
    rot90 = Affine.rotation(90.)
    result1 = rot90 * (trans * point)
    result2 = (rot90 * trans) * point
    seq_almost_equal(result1, (0., 2.))
    seq_almost_equal(result1, result2)
Exemple #16
0
def from_origin(west, north, xsize, ysize):
    """Return an Affine transformation given upper left and pixel sizes.

    Return an Affine transformation for a georeferenced raster given
    the coordinates of its upper left corner `west`, `north` and pixel
    sizes `xsize`, `ysize`.
    """
    return Affine.translation(west, north) * Affine.scale(xsize, -ysize)
 def test_determinant(self):
     assert_equal(Affine.identity().determinant, 1)
     assert_equal(Affine.scale(2).determinant, 4)
     assert_equal(Affine.scale(0).determinant, 0)
     assert_equal(Affine.scale(5, 1).determinant, 5)
     assert_equal(Affine.scale(-1, 1).determinant, -1)
     assert_equal(Affine.scale(-1, 0).determinant, 0)
     assert_almost_equal(Affine.rotation(77).determinant, 1)
     assert_almost_equal(Affine.translation(32, -47).determinant, 1)
Exemple #18
0
def from_bounds(west, south, east, north, width, height):
    """Return an Affine transformation given bounds, width and height.

    Return an Affine transformation for a georeferenced raster given
    its bounds `west`, `south`, `east`, `north` and its `width` and
    `height` in number of pixels.
    """
    return Affine.translation(west, north) * Affine.scale(
        (east - west) / width, (south - north) / height)
Exemple #19
0
 def test_determinant(self):
     assert Affine.identity().determinant == 1
     assert Affine.scale(2).determinant == 4
     assert Affine.scale(0).determinant == 0
     assert Affine.scale(5, 1).determinant == 5
     assert Affine.scale(-1, 1).determinant == -1
     assert Affine.scale(-1, 0).determinant == 0
     assert Affine.rotation(77).determinant == pytest.approx(1)
     assert Affine.translation(32, -47).determinant == pytest.approx(1)
Exemple #20
0
    def affine(self, pixelbuffer=0):
        """
        Return an Affine object of tile.

        - pixelbuffer: tile buffer in pixels
        """
        left = self.bounds(pixelbuffer=pixelbuffer)[0]
        top = self.bounds(pixelbuffer=pixelbuffer)[3]
        return Affine.translation(left, top) * Affine.scale(
            self.pixel_x_size, -self.pixel_y_size)
Exemple #21
0
def test_eccentricity_complex():
    assert \
        (Affine.scale(2, 3) * Affine.rotation(77)).eccentricity == \
        pytest.approx(math.sqrt(5) / 3)
    assert \
        (Affine.rotation(77) * Affine.scale(2, 3)).eccentricity == \
        pytest.approx(math.sqrt(5) / 3)
    assert \
        (Affine.translation(32, -47) * Affine.rotation(77) * Affine.scale(2, 3)).eccentricity == \
        pytest.approx(math.sqrt(5) / 3)
Exemple #22
0
def test_eccentricity():
    assert Affine.identity().eccentricity == 0.0
    assert Affine.scale(2).eccentricity == 0.0
    #assert_equal(Affine.scale(0).eccentricity, ?)
    assert Affine.scale(2, 1).eccentricity == pytest.approx(math.sqrt(3) / 2)
    assert Affine.scale(2, 3).eccentricity == pytest.approx(math.sqrt(5) / 3)
    assert Affine.scale(1, 0).eccentricity == 1.0
    assert Affine.rotation(77).eccentricity == pytest.approx(0.0)
    assert Affine.translation(32, -47).eccentricity == pytest.approx(0.0)
    assert Affine.scale(-1, 1).eccentricity == pytest.approx(0.0)
Exemple #23
0
 def test_is_degenerate(self):
     assert not Affine.identity().is_degenerate
     assert not Affine.translation(2, -1).is_degenerate
     assert not Affine.shear(0, -22.5).is_degenerate
     assert not Affine.rotation(88.7).is_degenerate
     assert not Affine.scale(0.5).is_degenerate
     assert Affine.scale(0).is_degenerate
     assert Affine.scale(-10, 0).is_degenerate
     assert Affine.scale(0, 300).is_degenerate
     assert Affine.scale(0).is_degenerate
     assert Affine.scale(0).is_degenerate
Exemple #24
0
def test_is_eo3(sample_doc, sample_doc_180):
    identity = list(Affine.translation(0, 0))
    assert is_doc_eo3(sample_doc) is True
    assert is_doc_eo3(sample_doc_180) is True

    # If there's no schema field at all, it's treated as legacy eo.
    assert is_doc_eo3({}) is False
    assert is_doc_eo3({'crs': 'EPSG:4326'}) is False
    assert is_doc_eo3({'crs': 'EPSG:4326', 'grids': {}}) is False

    with pytest.raises(ValueError, match="Unsupported dataset schema.*"):
        is_doc_eo3({'$schema': 'https://schemas.opendatacube.org/eo4'})
def make_test_raster(value=0, band_names=None, height=3, width=4, dtype=np.uint16,
                     crs=WEB_MERCATOR_CRS, affine=None, image=None):
    band_names = band_names or []
    if affine is None:
        affine = Affine.translation(10, 12) * Affine.scale(1, -1)
    if image is None:
        shape = [len(band_names), height, width]
        array = np.full(shape, value, dtype=dtype)
        mask = np.full(shape, False, dtype=bool)
        image = np.ma.array(data=array, mask=mask)
    raster = tl.GeoRaster2(image=image, affine=affine, crs=crs, band_names=band_names)
    return raster
def test_empty_raster_from_roi_affine_3_bands_high():
    affine = Affine.translation(10, 12) * Affine.scale(2, -2)
    raster = make_test_raster(88, [1, 3, 2],
                              affine=affine,
                              height=1301,
                              width=4)
    empty = GeoRaster2.empty_from_roi(band_names=raster.band_names,
                                      roi=raster.footprint(),
                                      resolution=2)
    assert (affine.almost_equals(empty.affine))
    assert (raster.crs == empty.crs)
    assert (raster.shape == empty.shape)
Exemple #27
0
def test_eccentricity_complex():
    assert \
        (Affine.scale(2, 3) * Affine.rotation(77)).eccentricity == \
        pytest.approx(math.sqrt(5) / 3)
    assert \
        (Affine.rotation(77) * Affine.scale(2, 3)).eccentricity == \
        pytest.approx(math.sqrt(5) / 3)
    assert \
        (Affine.translation(32, -47) *
         Affine.rotation(77) *
         Affine.scale(2, 3)).eccentricity == \
        pytest.approx(math.sqrt(5) / 3)
Exemple #28
0
    def __getitem__(self, item):
        indexes = [slice(index.start or 0, index.stop or size, index.step or 1)
                   for size, index in zip(self.shape, item)]
        for index in indexes:
            if index.step != 1:
                raise NotImplementedError('scaling not implemented, yet')

        affine = self.affine * Affine.translation(indexes[1].start, indexes[0].start)
        return GeoBox(width=indexes[1].stop - indexes[1].start,
                      height=indexes[0].stop - indexes[0].start,
                      affine=affine,
                      crs=self.crs)
Exemple #29
0
    def __py_resample(
        self, resolution: Union[Tuple[float, float], List[float], float]
    ) -> "Raster":
        """
        Resample raster using nearest neighbor
        Parameters
        -------
        resolution: tuple, list
            spatial resolution target

        Returns
        -------
        Raster
            Resampled
        """
        warnings.warn("this function will be removed in v1.0", DeprecationWarning)

        if isinstance(resolution, (float, int)):
            resampled_x_resolution = float(resolution)
            resampled_y_resolution = float(resolution)
        else:
            resampled_x_resolution = resolution[0]
            resampled_y_resolution = resolution[1]

        resampled_rows = round(self.y_extent / resampled_y_resolution)
        resampled_cols = round(self.x_extent / resampled_x_resolution)

        resampled_shape: Tuple[int, ...] = (resampled_rows, resampled_cols, self.layers)
        if self.layers == 1:
            resampled_shape = (resampled_rows, resampled_cols)

        resampled_array = np.zeros(
            resampled_rows * resampled_cols * self.layers, dtype=self.dtype
        ).reshape(resampled_shape)

        resampled_affine = Affine.translation(self.x_min, self.y_min) * Affine.scale(
            resampled_x_resolution, -resampled_y_resolution
        )

        for row in range(resampled_rows):
            for col in range(resampled_cols):
                x, y = rowcol2xy((row, col), resampled_affine)
                resampled_array[row, col] = self.xy_value(
                    x + (resampled_x_resolution / 2), y + (resampled_y_resolution / 2)
                )

        return Raster(
            resampled_array,
            (resampled_x_resolution, resampled_y_resolution),
            self.x_min,
            self.y_max,
            epsg=self.epsg,
        )
Exemple #30
0
    def buffered(self, ybuff, xbuff) -> 'GeoBox':
        """
        Produce a tile buffered by ybuff, xbuff (in CRS units)
        """
        by, bx = (_round_to_res(buf, res)
                  for buf, res in zip((ybuff, xbuff), self.resolution))
        affine = self.affine * Affine.translation(-bx, -by)

        return GeoBox(width=self.width + 2 * bx,
                      height=self.height + 2 * by,
                      affine=affine,
                      crs=self.crs)
    def affine(self):
        if self.size.x is None:
            self.scale_axis("x", 1.0)
        if self.size.y is None:
            self.scale_axis("y", 1.0)

        x_scale = (self.max.x - self.min.x) / self.size.x
        # Y axis is reversed: image coordinate conventions
        y_scale = (self.min.y - self.max.y) / self.size.y
        trans_aff = Affine.translation(self.min.x, self.max.y)
        scale_aff = Affine.scale(x_scale, y_scale)
        return trans_aff * scale_aff
Exemple #32
0
def test_patch_affine():
    eps = 1e-100
    assert(GeoRaster2._patch_affine(Affine.identity()) == Affine.translation(eps, eps))
    assert(GeoRaster2._patch_affine(Affine.translation(2 * eps, 3 * eps)) ==
           Affine.translation(2 * eps, 3 * eps))
    assert(GeoRaster2._patch_affine(Affine.translation(2, 3)) == Affine.translation(2, 3))
    assert(GeoRaster2._patch_affine(Affine.scale(1.0, -1)) ==
           Affine.translation(eps, -eps) * Affine.scale(1, -1))
    assert(GeoRaster2._patch_affine(Affine.scale(-1, 1)) ==
           Affine.translation(-eps, eps) * Affine.scale(-1, 1))
    assert(GeoRaster2._patch_affine(Affine.scale(-1, -1)) ==
           Affine.translation(-eps, -eps) * Affine.scale(-1, -1))
    assert(GeoRaster2._patch_affine(Affine.scale(1.1, -1)) == Affine.scale(1.1, -1))
    assert(GeoRaster2._patch_affine(Affine.scale(1, -1.1)) == Affine.scale(1, -1.1))
Exemple #33
0
def test_warp():
    src = np.zeros((128, 256), dtype='int16')

    src[10:20, 30:50] = 33

    dst = np.zeros_like(src)
    dst_ = warp_affine(src,
                       dst,
                       Affine.translation(+30, +10),
                       resampling='nearest')
    assert dst_ is dst
    assert (dst[:10, :20] == 33).all()
    assert (dst[10:, :] == 0).all()
    assert (dst[:, 20:] == 0).all()

    # check GDAL int8 limitation work-around
    src = src.astype('int8')
    dst = np.zeros_like(src)
    dst_ = warp_affine(src,
                       dst,
                       Affine.translation(+30, +10),
                       resampling='nearest')
    assert dst_ is dst
    assert (dst[:10, :20] == 33).all()
    assert (dst[10:, :] == 0).all()
    assert (dst[:, 20:] == 0).all()

    # check GDAL int8 limitation work-around, with no-data
    src = src.astype('int8')
    dst = np.zeros_like(src)
    dst_ = warp_affine(src,
                       dst,
                       Affine.translation(+30, +10),
                       resampling='nearest',
                       src_nodata=0,
                       dst_nodata=-3)
    assert dst_ is dst
    assert (dst[:10, :20] == 33).all()
    assert (dst[10:, :] == -3).all()
    assert (dst[:, 20:] == -3).all()
Exemple #34
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
Exemple #35
0
 def test_is_degenerate(self):
     from affine import EPSILON
     assert not Affine.identity().is_degenerate
     assert not Affine.translation(2, -1).is_degenerate
     assert not Affine.shear(0, -22.5).is_degenerate
     assert not Affine.rotation(88.7).is_degenerate
     assert not Affine.scale(0.5).is_degenerate
     assert Affine.scale(0).is_degenerate
     assert Affine.scale(-10, 0).is_degenerate
     assert Affine.scale(0, 300).is_degenerate
     assert Affine.scale(0).is_degenerate
     assert Affine.scale(0).is_degenerate
     assert Affine.scale(EPSILON).is_degenerate
Exemple #36
0
def geobox_union_conservative(geoboxes: List[GeoBox]) -> GeoBox:
    """ Union of geoboxes. Fails whenever incompatible grids are encountered. """
    if len(geoboxes) == 0:
        raise ValueError("No geoboxes supplied")

    reference, *_ = geoboxes

    bbox = bbox_union(bounding_box_in_pixel_domain(geobox, reference=reference)
                      for geobox in geoboxes)

    affine = reference.affine * Affine.translation(*bbox[:2])

    return GeoBox(width=bbox.width, height=bbox.height, affine=affine, crs=reference.crs)
Exemple #37
0
def gtif_toGPS(gtiffFile):
    with rasterio.open(gtiffFile) as r:
        T0 = r.affine  # upper-left pixel corner affine transform
        p1 = Proj(r.crs)
        A = r.read(1)  # pixel values

    cols, rows = np.meshgrid(np.arange(A.shape[1]), np.arange(A.shape[0]))
    T1 = T0 * Affine.translation(0.5, 0.5)
    rc2en = lambda r, c: (c, r) * T1
    eastings, northings = np.vectorize(rc2en, otypes=[np.float, np.float])(rows, cols)
    p2 = Proj(proj='latlong',datum='WGS84')
    longs, lats = transform(p1, p2, eastings, northings)
    return (longs,lats)
Exemple #38
0
 def transform(self, recalc=False):
     """Determine the affine of the `xarray.DataArray`"""
     if not recalc:
         try:
             # get affine from xarray rasterio
             return Affine(*self._obj.attrs["transform"][:6])
         except KeyError:
             pass
     src_bounds = self.bounds(recalc=recalc)
     src_left, _, _, src_top = src_bounds
     src_resolution_x, src_resolution_y = self.resolution(recalc=recalc)
     return Affine.translation(src_left, src_top) * Affine.scale(
         src_resolution_x, src_resolution_y)
Exemple #39
0
def test_from_bounds_rotation():
    """Get correct window when transform is rotated"""
    sqrt2 = math.sqrt(2.0)
    # An 8 unit square rotated cw 45 degrees around (0, 0).
    height = 4
    width = 4
    transform = (Affine.rotation(-45.0) * Affine.translation(-sqrt2, sqrt2) *
                 Affine.scale(sqrt2 / 2.0, -sqrt2 / 2.0))
    win = from_bounds(-2.0, -2.0, 2.0, 2.0, transform=transform)
    assert win.col_off == pytest.approx(-2.0)
    assert win.row_off == pytest.approx(-2.0)
    assert win.width == pytest.approx(2.0 * width)
    assert win.height == pytest.approx(2.0 * height)
Exemple #40
0
def convert_geo(dtm, points):
    """
		Function that converts pixel coordinates to geographic coordinates
	"""
    res = []
    T0 = dtm.transform
    T1 = T0 * Affine.translation(0.5, 0.5)
    for point in points:
        row1, col1, row2, col2 = point
        x1y1 = (row1, col1) * T1
        x2y2 = (row2, col2) * T1
        res.append(x1y1 + x2y2)
    return res
Exemple #41
0
def _inspect_coords(y, x, center=True, assume_unique=True):
    # returns transform, bounds, shape
    y_ = np.atleast_1d(y)
    x_ = np.atleast_1d(x)

    if not assume_unique:
        logger.warning('Computing unique values of coordinates...')
        y_ = np.unique(y_)
        x_ = np.unique(x_)

    minmax_y = y_[0], y_[-1]
    minmax_x = x_[0], x_[-1]

    if minmax_y[0] > minmax_y[1]:
        logger.warning('Unreversing y coordinate min/max')
        minmax_y = (minmax_y[1], minmax_y[0])

    # If spacing is not equal we guess from `(max - min) / n`
    dy = _check_spacing(y_)
    if dy is False:
        warnings.warn('"y" coordinate does not have equal spacing')
        ny = len(y_)
        dy = (minmax_y[0] - minmax_y[1]) / (ny - 1)
    else:
        ny = (minmax_y[0] - minmax_y[1]) / dy
        # np.unique returns sorted y so dy is negative, but not if unsorted
        if not assume_unique:
            dy *= -1

    dx = _check_spacing(x_)
    if dx is False:
        warnings.warn('"x" coordinate does not have equal spacing')
        nx = len(x_)
        dx = (minmax_x[1] - minmax_x[0]) / (nx - 1)
    else:
        nx = (minmax_x[1] - minmax_x[0]) / dx

    transform = Affine(dx, 0.0, minmax_x[0], 0., dy, minmax_y[1])

    if center:
        # affine transform is relative to upper-left, not pixel center
        transform = transform * Affine.translation(-0.5, -0.5)
        # create bounds that cover pixel area
        bounds = BoundingBox(minmax_x[0] - dx / 2, minmax_y[0] + dy / 2,
                             minmax_x[1] + dx / 2, minmax_y[1] - dy / 2)
    else:
        # create bounds that cover pixel area
        bounds = BoundingBox(minmax_x[0], minmax_y[0] + dy, minmax_x[1] + dx,
                             minmax_y[1])

    return transform, bounds, (nx, ny)
Exemple #42
0
    def test_read_with_rasterfiledatasource(self, make_sample_geotiff,
                                            dst_nodata):
        sample_geotiff_path, geobox, written_data = make_sample_geotiff(
            dst_nodata)

        source = RasterFileDataSource(str(sample_geotiff_path), 1)

        dest = np.zeros_like(written_data)
        dst_transform = geobox.transform
        dst_projection = epsg3577
        dst_resampling = Resampling.nearest

        # Read exactly the hunk of data that we wrote
        _read_from_source(source, dest, dst_transform, dst_nodata,
                          dst_projection, dst_resampling)

        assert np.all(written_data == dest)

        # Try reading from partially outside of our area
        xoff = 50
        offset_transform = dst_transform * Affine.translation(xoff, 0)
        dest = np.zeros_like(written_data)

        _read_from_source(source, dest, offset_transform, dst_nodata,
                          dst_projection, dst_resampling)
        assert np.all(written_data[:, xoff:] == dest[:, :xoff])

        # Try reading from complete outside of our area, should return nodata
        xoff = 300
        offset_transform = dst_transform * Affine.translation(xoff, 0)
        dest = np.zeros_like(written_data)

        _read_from_source(source, dest, offset_transform, dst_nodata,
                          dst_projection, dst_resampling)
        if np.isnan(dst_nodata):
            assert np.all(np.isnan(dest))
        else:
            assert np.all(dst_nodata == dest)
 def __init__(self, raster):
     # load metadata
     self.gt = raster.GetGeoTransform()
     self.proj = raster.GetProjection()
     self.cols = raster.RasterXSize
     self.rows = raster.RasterYSize
     self.band = raster.GetRasterBand(1)
     self.no_data = self.band.GetNoDataValue()
     # get affine transformation
     self.T0 = Affine.from_gdal(*raster.GetGeoTransform())
     # cell-centered affine transformation
     self.T1 = self.T0 * Affine.translation(0.5, 0.5)
     # load data
     self.data = np.array(raster.ReadAsArray())
Exemple #44
0
def test_transformation():
    tf = Affine.translation(1, 2)

    # when src_affine==dst_affine should transform to itself:
    transformed_shape = projections.transform(source_shape,
                                              source_crs,
                                              src_affine=tf,
                                              dst_affine=tf)
    assert transformed_shape == source_shape

    transformed_shape = projections.transform(source_shape,
                                              source_crs,
                                              src_affine=tf)
    assert transformed_shape == Point(-1, -2)
Exemple #45
0
    def __init__(
        self,
        array: np.ndarray,
        resolution: Union[None, Tuple[float, float], List[float],
                          Tuple[float, ...], float] = None,
        x_min: Optional[float] = None,
        y_min: Optional[float] = None,
        x_max: Optional[float] = None,
        y_max: Optional[float] = None,
        epsg: int = 4326,
    ):
        if (resolution is None and x_min is None and y_min is None
                and x_max is None and y_max is None):
            raise ValueError(
                "Please define resolution and at least x minimum and y minimum"
            )

        if resolution is not None and x_min is None and y_min is None:
            raise ValueError("Please at least define x_min and y_min")

        if isinstance(resolution, float):
            self.resolution: Tuple[float, float] = (
                resolution,
                resolution,
            )
        elif resolution is not None and isinstance(resolution, Iterable):
            self.resolution = (resolution[0], resolution[1])

        if resolution is not None and x_min is not None and y_min is not None:
            self.x_min: float = x_min
            self.y_min: float = y_min
            self.x_max: float = x_min + (self.resolution[0] * array.shape[1])
            self.y_max: float = y_min + (self.resolution[1] * array.shape[0])
        elif (resolution is None and x_min is not None and y_min is not None
              and x_max is not None and y_max is not None):
            self.resolution = (
                (x_max - x_min) / array.shape[1],
                (y_max - y_min) / array.shape[0],
            )
            self.x_min = x_min
            self.y_min = y_min
            self.x_max = x_max
            self.y_max = y_max

        self.array = array
        self.epsg = epsg
        self.transform = Affine.translation(
            self.x_min, self.y_min) * Affine.scale(self.resolution[0],
                                                   -self.resolution[1])
        self.crs = CRS.from_epsg(epsg)
Exemple #46
0
def _transform_from_latlon(lon, lat):
    """perform an affine tranformation to the latitude/longitude coordinates"""

    from affine import Affine

    lat = np.asarray(lat)
    lon = np.asarray(lon)

    d_lon = lon[1] - lon[0]
    d_lat = lat[1] - lat[0]

    trans = Affine.translation(lon[0] - d_lon / 2, lat[0] - d_lat / 2)
    scale = Affine.scale(d_lon, d_lat)
    return trans * scale
Exemple #47
0
def coordinates(fn=None,
                meta=None,
                numpy_array=None,
                input_crs=None,
                to_latlong=False):
    '''
	take a raster file as input and return the centroid coords for each 
	of the grid cells as a pair of numpy 2d arrays (longitude, latitude)
	'''
    import rasterio
    import numpy as np
    from affine import Affine
    from pyproj import Proj, transform

    if fn:
        # Read raster
        with rasterio.open(fn) as r:
            T0 = r.affine  # upper-left pixel corner affine transform
            p1 = Proj(r.crs)
            A = r.read(1)  # pixel values

    elif (meta is not None) & (numpy_array is not None):
        A = numpy_array
        if input_crs != None:
            p1 = Proj(input_crs)
            T0 = meta['affine']
        else:
            p1 = None
            T0 = meta['affine']
    else:
        BaseException('check inputs')

    # All rows and columns
    cols, rows = np.meshgrid(np.arange(A.shape[1]), np.arange(A.shape[0]))
    # Get affine transform for pixel centres
    T1 = T0 * Affine.translation(0.5, 0.5)
    # Function to convert pixel row/column index (from 0) to easting/northing at centre
    rc2en = lambda r, c: (c, r) * T1
    # All eastings and northings (there is probably a faster way to do this)
    eastings, northings = np.vectorize(rc2en, otypes=[np.float,
                                                      np.float])(rows, cols)

    if to_latlong == False:
        return eastings, northings
    elif (to_latlong == True) & (input_crs != None):
        # Project all longitudes, latitudes
        longs, lats = transform(p1, p1.to_latlong(), eastings, northings)
        return longs, lats
    else:
        BaseException('cant reproject to latlong without an input_crs')
Exemple #48
0
def clip_raster_to_valid_extent(ras):
    import numpy as np
    from affine import Affine

    # test if ras is path or raster_object
    if isinstance(ras, str):
        ras_in = ras
        ras = raster_load(ras_in)
    elif not isinstance(ras, rasterObj):
        raise Exception(
            'ras is not an instance of rasterObj or str (filepath), raster_to_pd() aborted.'
        )

    if ras.band_count > 1:
        # unstack data
        data = np.full((ras.rows, ras.cols, ras.band_count), ras.no_data)
        for ii in range(0, ras.band_count):
            data[:, :, ii] = ras.data[ii]
        valid = np.where(np.any(data != ras.no_data, axis=2))
    else:
        # nest data in list
        ras.data = [ras.data]
        valid = np.where(ras.data != ras.no_data)

    yc_min, xc_min = np.min(valid, axis=1)
    yc_max, xc_max = np.max(valid, axis=1)

    x_min, y_min = ras.T0 * (xc_min, yc_min)

    ras.gt = (x_min, ras.gt[1], ras.gt[2], y_min, ras.gt[4], ras.gt[5])

    new_data = []
    for ii in range(0, ras.band_count):
        band = np.full((yc_max - yc_min + 1, xc_max - xc_min + 1), ras.no_data)
        band[(valid[0] - yc_min, valid[1] - xc_min)] = ras.data[ii][valid]
        new_data.append(band)

    ras.data = new_data

    ras.rows, ras.cols = ras.data[0].shape

    if ras.band_count == 1:
        ras.data = ras.data[0]

    ras.T0 = Affine.from_gdal(*ras.gt)
    # cell-centered affine transformation
    ras.T1 = ras.T0 * Affine.translation(0.5, 0.5)

    return ras
Exemple #49
0
def shift_images(input_directory_path, output_directory_path):
    """
    Shift the images' coordinates.

    This function shifts the image coordinates and removes the 4th Alpha band
    from the photograph. It is used to center photographs from distant
    locations to a central location for manual digitizing for purposes of
    training an image classifier.

    Parameters
    ----------
    input_directory_path: string
        string representing filesystem directory where the photographs are
        located.
    
    output_directory_path: string
        string representing the filesystem directory where the output
        photographs will be written to disk.

    Returns
    -------
    NADA

    """
    x_coord = 476703
    y_coord = 1952787
    x_increment = 350
    y_increment = 350
    x_count = 0
    y_count = 0
    training_data = utility.get_training_data()
    for key in training_data:
        y_shift = y_count * y_increment
        for f in training_data[key]:
            with rasterio.open(input_directory_path + f + ".tif") as src:
                x_shift = x_count * x_increment
                af = src.transform
                meta = src.meta
                meta['count'] = 3
                meta['transform'] = Affine.translation(
                    x_coord + x_shift, y_coord - y_shift) * Affine.scale(
                        af[0], af[4])
                with rasterio.open(
                        output_directory_path + "training_" + f + ".tif", "w",
                        **meta) as dst:
                    dst.write(src.read((1, 2, 3)))
            x_count += 1
        y_count += 1
        x_count = 0
Exemple #50
0
    def __init__(self, surface, *al, **ad):
        self._renderer = self._dwg = surface

        self._bounds = Extents()
        self._padding = PADDING

        self._stack = []
        self._m = Affine.translation(0, 0)
        self._xy = (0, 0)
        self._mxy = self._m * self._xy
        self._lw = 0
        self._rgb = (0, 0, 0)
        self._ff = "sans-serif"
        self._fs = 10
        self._last_path = None
def transform_from_latlon(lat, lon):
    """ input 1D array of lat / lon and output an Affine transformation

    Arguments:
    ---------
    : lat (list, np.array)
        list of lat values
    : lon (list, np.array)
        list of lon values
    """
    lat = np.asarray(lat)
    lon = np.asarray(lon)
    trans = Affine.translation(lon[0], lat[0])
    scale = Affine.scale(lon[1] - lon[0], lat[1] - lat[0])
    return trans * scale
Exemple #52
0
def prediction_dataset(raster_path, fn):
    srcImage = gdal.Open(raster_path)
    geoTrans = srcImage.GetGeoTransform()
    mylist1 = list()
    T0 = Affine.from_gdal(*srcImage.GetGeoTransform())
    cols = srcImage.RasterYSize
    rows = srcImage.RasterXSize
    T1 = T0 * Affine.translation(0.5, 0.5)
    rc2xy = lambda r, c: (c, r
                          ) * T1  # Convert row and column to xy coordinates
    pointlist = []
    for i in range(cols):
        for j in range(rows):
            pointlist.append(rc2xy(i, j))

    # Convert the layer extent to image pixel coordinates
    # mylist = list()
    # typelist = list()
    polylist = pointWithinPolygon.poly_contain()
    for feat in pointlist:
        mylist = list()
        # point = ogr.Geometry(ogr.wkbPoint)
        # point.AddPoint(feat[0],feat[1])
        # point.ExportToWkb()
        # for poly in polylist:
        #if poly.Contains(point):
        for band in range(1, 4):
            rb = srcImage.GetRasterBand(band)
            geom = feat
            # print geom
            if geom != None:
                mx, my = geom[0], geom[1]
                # print mx,my
            ulX, ulY = world2Pixel(geoTrans, mx, my)
            # print ulX, ulY
            while ulY < srcImage.RasterYSize:
                intval = rb.ReadAsArray(ulX, ulY, 1, 1)
                mylist.extend(intval[0])
                # print feat
                break
        mylist1.append(mylist)
    df = pd.DataFrame(mylist1)
    # df = df.transpose()
    df.columns = ['R', 'G', 'B']
    df[fn + ' RGB'] = getRGB(df, fn)
    df = df.drop(['R', 'G', 'B'], axis=1)

    return df, pointlist
Exemple #53
0
    def from_geopolygon(cls, geopolygon, resolution, crs=None, align=True):
        """
        :type geopolygon: datacube.model.GeoPolygon
        :param resolution: (x_resolution, y_resolution)
        :param crs: CRS to use, if different from the geopolygon
        :param align: Should the geobox be aligned to pixels of the given resolution. This assumes an origin of (0,0).
        :type align: boolean
        :rtype: GeoBox
        """
        # TODO: currently only flipped Y-axis data is supported
        assert resolution[1] > 0
        assert resolution[0] < 0

        if crs is None:
            crs = geopolygon.crs
        else:
            geopolygon = geopolygon.to_crs(crs)
        
        bounding_box = geopolygon.boundingbox
#        print(bounding_box)
#        print("~~~")
        left, top = float(bounding_box.left), float(bounding_box.top)

        if align:
            left = math.floor(left / resolution[1]) * resolution[1]
            top = math.floor(top / resolution[0]) * resolution[0]
#            print(left)
            #print(top)
        #print(Affine.translation(left, top))
        #print(Affine.scale(resolution[1], resolution[0]))
        affine = (Affine.translation(left, top) * Affine.scale(resolution[1], resolution[0]))
#        print(affine)
        right, bottom = float(bounding_box.right), float(bounding_box.bottom)
        width, height = ~affine * (right, bottom)
        
#        print(right)
        
#        print(width)
        #width = 198
        #height = 288
        #print(height)
        if align:
            width = math.ceil(width)
            height = math.ceil(height)
        return GeoBox(crs=crs,
                      affine=affine,
                      width=int(width),
                      height=int(height))
Exemple #54
0
def transform(window, transform):
    """Construct an affine transform matrix relative to a window.

    Parameters
    ----------
    window : a Window or window tuple
        The input window.
    transform: Affine
        an affine transform matrix.

    Returns
    -------
    Affine
        The affine transform matrix for the given window
    """
    (r, _), (c, _) = window
    return transform * Affine.translation(c or 0, r or 0)
Exemple #55
0
def compute_transform(tiepoints, affine=False):
    """
    Takes an array of (old, new) position tuples
    and returns a translation matrix between the
    two configurations.
    """
    arr = lambda x: N.array([N.asarray(i) for i in x])
    old, new = (arr(i) for i in zip(*tiepoints))

    if affine:
        # Currently not working
        old_ = add_ones(old)
        trans_matrix, residuals = N.linalg.lstsq(old_,new)[:2]
        return Affine(*trans_matrix.transpose().flatten())
    else:
        offsets = N.mean(new-old,axis=0)
        return Affine.translation(*offsets)
Exemple #56
0
def extract_area(geom, dem, **kwargs):
    # RasterIO's image-reading algorithm uses the location
    # of polygon centers to determine the extent of polygons
    msk = geometry_mask(
        (mapping(geom),),
        dem.shape,
        dem.transform,
        invert=True)

    # shrink mask to the minimal area for efficient extraction
    offset, msk = offset_mask(msk)

    window = tuple((o,o+s)
        for o,s in zip(offset,msk.shape))

    # Currently just for a single band
    # We could generalize to multiple
    # bands if desired
    z = dem.read(1,
        window=window,
        masked=True)

    # mask out unused area
    z[msk == False] = N.ma.masked

    # Make vectors of rows and columns
    rows, cols = (N.arange(first,last,1)
            for first,last in window)
    # 2d arrays of x,y,z
    z = z.flatten()
    xyz = [i.flatten()
            for i in N.meshgrid(cols,rows)] + [z]
    x,y,z = tuple(i[z.mask == False] for i in xyz)

    # Transform into 3xn matrix of
    # flattened coordinate values
    coords = N.vstack((x,y,N.ones(z.shape)))

    # Get affine transform for pixel centers
    affine = dem.transform * Affine.translation(0.5, 0.5)
    # Transform coordinates to DEM's reference
    _ = N.array(affine).reshape((3,3))
    coords = N.dot(_,coords)
    coords[2] = z
    return coords.transpose()
def coordinates( fn=None, meta=None, numpy_array=None, input_crs=None, to_latlong=False ):
	'''
	take a raster file as input and return the centroid coords for each 
	of the grid cells as a pair of numpy 2d arrays (longitude, latitude)
	'''
	import rasterio
	import numpy as np
	from affine import Affine
	from pyproj import Proj, transform

	if fn:
		# Read raster
		with rasterio.open( fn ) as r:
			T0 = r.affine  # upper-left pixel corner affine transform
			p1 = Proj( r.crs )
			A = r.read_band( 1 )  # pixel values

	elif (meta is not None) & (numpy_array is not None):
		A = numpy_array
		if input_crs != None:
			p1 = Proj( input_crs )
			T0 = meta[ 'affine' ]
		else:
			p1 = None
			T0 = meta[ 'affine' ]
	else:
		BaseException( 'check inputs' )

	# All rows and columns
	cols, rows = np.meshgrid(np.arange(A.shape[1]), np.arange(A.shape[0]))
	# Get affine transform for pixel centres
	T1 = T0 * Affine.translation( 0.5, 0.5 )
	# Function to convert pixel row/column index (from 0) to easting/northing at centre
	rc2en = lambda r, c: ( c, r ) * T1
	# All eastings and northings (there is probably a faster way to do this)
	eastings, northings = np.vectorize(rc2en, otypes=[np.float, np.float])(rows, cols)

	if to_latlong == False:
		return eastings, northings
	elif (to_latlong == True) & (input_crs != None):
		# Project all longitudes, latitudes
		longs, lats = transform(p1, p1.to_latlong(), eastings, northings)
		return longs, lats
	else:
		BaseException( 'cant reproject to latlong without an input_crs' )
Exemple #58
0
def setup_geo():
    raster = Raster(paths.mask)
    mask_arr = raster.as_bool_array

    # get raster to provide geo data (need one that is not "Byte")
    root = paths.initial_inputs
    name = tiff_list(root)[0]
    raster = Raster(name, root=root)
    geo = raster.geo

    startc, endc, startr, endr = bounding_box(mask_arr)
    geo['rows'] = endr - startr
    geo['cols'] = endc - startc
    transform = get_tiff_transform(paths.mask)
    transform *= Affine.translation(startc, startr)
    geo['geotransform'] = transform.to_gdal()

    return geo, (startc, endc, startr, endr)