Ejemplo n.º 1
0
    def envelope(self):

        dataset = self.getDataset()
        xform = dataset.GetGeoTransform()
        xScale = xform[1]
        yScale = xform[5]
        width = dataset.RasterXSize
        height = dataset.RasterYSize
        ulx = xform[0]
        uly = xform[3]
        lrx = ulx + width * xScale
        lry = uly + height * yScale

        envelope = Envelope()

        # ---
        # If this file is in 4326, we must swap the x-y to conform with GDAL
        # 3's strict conformity to the 4326 definition.
        # ---
        srs4326 = SpatialReference()
        srs4326.ImportFromEPSG(4326)

        if srs4326.IsSame(self._dataset.GetSpatialRef()):

            envelope.addPoint(uly, ulx, 0, self._dataset.GetSpatialRef())
            envelope.addPoint(lry, lrx, 0, self._dataset.GetSpatialRef())

        else:

            envelope.addPoint(ulx, uly, 0, self._dataset.GetSpatialRef())
            envelope.addPoint(lrx, lry, 0, self._dataset.GetSpatialRef())

        return envelope
Ejemplo n.º 2
0
    def testClipReproject(self):

        # Build the test file.
        imageFile = self._createTestFile()

        # Build the envelope.
        ulx = 367080
        uly = 4209230
        lrx = 509200
        lry = 4095100
        srs = SpatialReference()
        srs.ImportFromEPSG(32612)
        env = Envelope()
        env.addPoint(ulx, uly, 0, srs)
        env.addPoint(lrx, lry, 0, srs)

        # Reprojection parameter
        targetSRS = SpatialReference()
        targetSRS.ImportFromEPSG(4326)

        # Clip, reproject and resample.
        imageFile.clipReproject(env, targetSRS,)

        # Check the results.
        dataset = gdal.Open(imageFile.fileName(), gdalconst.GA_ReadOnly)

        if not dataset:
            raise RuntimeError('Unable to read ' + imageFile.fileName() + '.')

        xform = dataset.GetGeoTransform()
        xScale = xform[1]
        yScale = xform[5]
        width = dataset.RasterXSize
        height = dataset.RasterYSize
        clippedUlx = xform[0]
        clippedUly = xform[3]
        clippedLrx = clippedUlx + width * xScale
        clippedLry = clippedUly + height * yScale

        self.assertAlmostEqual(clippedUlx, -112.49369402670872, places=12)
        self.assertAlmostEqual(clippedUly, 38.03073206024332, places=11)
        self.assertAlmostEqual(clippedLrx, -110.89516946364738, places=12)
        self.assertAlmostEqual(clippedLry, 36.99265291293727, places=11)

        outSRS = SpatialReference()
        outSRS.ImportFromWkt(dataset.GetProjection())
        self.assertTrue(outSRS.IsSame(targetSRS))

        # Delete the test file.
        os.remove(imageFile.fileName())
Ejemplo n.º 3
0
    def testReproject(self):

        # Build the test file.
        imageFile = self._createTestFile()

        # Reproject.
        targetSRS = SpatialReference()
        targetSRS.ImportFromEPSG(4326)
        imageFile.clipReproject(outputSRS=targetSRS)

        # Check the SRS.
        dataset = gdal.Open(imageFile.fileName(), gdalconst.GA_ReadOnly)

        if not dataset:
            raise RuntimeError('Unable to read ' + imageFile.fileName() + '.')

        outSRS = SpatialReference()
        outSRS.ImportFromWkt(dataset.GetProjection())
        self.assertTrue(outSRS.IsSame(targetSRS))

        # Delete the test file.
        os.remove(imageFile.fileName())
Ejemplo n.º 4
0
def convert_geometry(
        geometry: ogr.Geometry,
        new_spatialreference: osr.SpatialReference) -> ogr.Geometry:
    """Converts the geometry to the new spatial reference if possible
    Arguments:
        geometry - The geometry to transform
        new_spatialreference - The spatial reference to change to
    Returns:
        The transformed geometry or the original geometry. If either the
        new Spatial Reference parameter is None, or the geometry doesn't
        have a spatial reference, then the original geometry is returned.
    """
    if not new_spatialreference or not geometry:
        return geometry

    return_geometry = geometry
    try:
        geom_sr = geometry.GetSpatialReference()
        if int(osgeo.__version__[0]) >= 3:
            # GDAL 3 changes axis order: https://github.com/OSGeo/gdal/issues/1546
            # pylint: disable=no-member
            geom_sr.SetAxisMappingStrategy(
                osgeo.osr.OAMS_TRADITIONAL_GIS_ORDER)
        if geom_sr and not new_spatialreference.IsSame(geom_sr):
            transform = osr.CreateCoordinateTransformation(
                geom_sr, new_spatialreference)
            new_geom = geometry.Clone()
            if new_geom:
                new_geom.Transform(transform)
                return_geometry = new_geom
    except Exception as ex:
        logging.warning("Exception caught while transforming geometries: %s",
                        str(ex))
        logging.warning("    Returning original geometry")

    return return_geometry
Ejemplo n.º 5
0
def main():
    args = docopt(__doc__, version=VERSION)

    logparams = {}
    if args['--debug']:
        logparams.update(level=logging.DEBUG)
    elif args['--info']:
        logparams.update(level=logging.INFO)
    else:
        logparams.update(level=logging.CRITICAL)

    if args['--logfile'] != '':
        logparams.update(filename=args['--logfile'])

    logging.basicConfig(**logparams)

    logger = logging.getLogger('extract_patches')
    logger.debug('input \n {}'.format(args))

    assert isinstance(logger, logging.Logger)

    shapefiles = collect_filenames(args['-i'])
    if len(shapefiles) == 0:
        logger.error('No matching shapefiles for inoput `{}`'.format(
            args['-i']))
        return

    raster = args['-r']

    try:
        size = [int(x) for x in args['--size'].split(',')]
        patch_width, patch_height = size
        logger.debug("Set patch size to {} x {}".format(
            patch_width, patch_height))
    except:
        logger.error("Unable to parse option '--size'")
        return

    try:
        scale = float(args['--scale'])
        assert scale > 0
        logger.debug("Set scale to {}".format(scale))
    except:
        logger.error("Unable to parse option '--scale'")
        return

    silent = args['--noprogress']

    output_folder = args['--odir']
    try:
        if not os.path.isdir(output_folder):
            os.makedirs(output_folder)
            logger.debug("Created output folder '{}'".format(output_folder))
        else:
            logger.debug(
                "Found existing output folder '{}'".format(output_folder))
    except:
        logger.error("Unable to find or create output directory `{}`".format(
            output_folder))
        return

    if args['--ojpg']:
        fmt = '.jpg'
    else:  # args['--otif']  (default)
        fmt = '.tif'
    logger.debug("Output format set to {}".format(fmt))

    clip = args['--vclip'] is not None
    if clip:
        clipmin, clipmax = [float(x) for x in args['--vclip'].split(',')]
        logger.debug("Clipping output to [{}, {}]".format(clipmin, clipmax))
    else:
        clipmin, clipmax = 0, 1
        logger.debug(
            "Not clipping output -- assuming range of value is [{},{}]".format(
                clipmin, clipmax))

    stretch = args['--vstretch'] is not None
    if stretch:
        stretchmin, stretchmax = [
            float(x) for x in args['--vstretch'].split(',')
        ]
        logger.debug("Output value range will be stretched to [{},{}]".format(
            stretchmin, stretchmax))
    else:
        logger.debug("Output values will not be stretched")

    if args['--csv']:
        csv_file_name = args['--csv']
        if os.path.isfile(csv_file_name):
            logger.error(
                "CSV File already exists; please remove or rename it first.")
            logger.debug("Writing to CSV File '{}'".format(csv_file_name))
            return
    else:
        csv_file_name = None
        logger.debug("No CSV output")

    # Estimate number of shape features
    count = 0
    if not silent:
        pbar = ProgressBar(
            len(shapefiles),
            ['Counting Features:',
             Percentage(), ' ',
             Bar(), ' ',
             ETA()])
        pbar.start()
    for i, s in enumerate(shapefiles):
        vector = ogr.Open(s)
        layer = vector.GetLayer()
        count += layer.GetFeatureCount()
        if not silent:
            pbar.update(i)
    if not silent:
        pbar.finish()

    logger.debug("Counted {} features in {} shapefiles".format(
        count, len(shapefiles)))

    # Write header for CSV file
    if csv_file_name is not None:
        with open(os.path.join(output_folder, csv_file_name), 'w') as csvf:
            csvf.write(
                'gx, gy, r1, r2, theta, patch_width, patch_height, image_namei\n'
            )

    with rasterio.open(raster) as rf:
        assert isinstance(rf, RasterReader)
        srs = SpatialReference(str(rf.crs_wkt))
        affine = rf.affine
        geo_to_pixels = ~affine

        logging.debug("Output CRS will be '''{}'''".format(
            srs.ExportToPrettyWkt()))

        if not silent:
            pbar = ProgressBar(
                count,
                ['Exporting Patches:',
                 Percentage(), ' ',
                 Bar(), ' ',
                 ETA()])
            pbar.start()
        for sf in shapefiles:
            logger.info("Processing input '{}'".format(sf))
            vector = ogr.Open(sf)
            assert isinstance(vector, ogr.DataSource)

            layer = vector.GetLayer()
            assert isinstance(layer, ogr.Layer)

            if not srs.IsSame(layer.GetSpatialRef()):
                logger.warning(
                    "Coordinate system mismatch (its ok, I will reproject)")

            for f in layer:
                if not silent:
                    pbar.update(pbar.currval + 1)
                geom = f.GetGeometryRef()
                assert isinstance(geom, ogr.Geometry)
                geom = geom.TransformTo(srs)
                points = geom.GetPoints()
                source = points[0]
                target = points[-1]
                sx, sy = geo_to_pixels * source
                tx, ty = geo_to_pixels * target
                if len(points) == 2:
                    cx, cy = (sx + tx) / 2, (sy + ty) / 2
                else:
                    cx, cy = geo_to_pixels * points[1]
                dx, dy = (tx - sx), (ty - sy)
                theta = degrees(
                    atan2(dy, dx)
                )  # In PIXELS, CCW from +x. Not necessarily CCW from E (or CW from N)
                r1 = hypot(tx - cx, ty - cy)
                r2 = hypot(cx - sx, cy - sy)
                r1, r2 = max(r1, r2), min(
                    r1, r2
                )  # For 3 points, we assume two radii. Else these are duplicates.
                gx, gy = affine * (
                    cx, cy
                )  # Geographic coordinates (e.g. lat lon) of the center.

                # We read a square slightly larger than the scaled version of our patch, so that
                # we can safely rotate the raster without missing pixels in the corners.

                box_radius = hypot(patch_width, patch_height) / (2.0 * scale)
                x0, x1 = int(floor(cx - box_radius)), int(ceil(cx +
                                                               box_radius))
                y0, y1 = int(floor(cy - box_radius)), int(ceil(cy +
                                                               box_radius))

                # save patch...

                kwargs = rf.meta
                patch_affine = (
                    affine * Affine.translation(cx, cy) *
                    Affine.rotation(angle=-theta) *
                    Affine.translation(-patch_width / 2., -patch_height / 2.))

                if fmt == '.tif':
                    kwargs.update(driver='GTiff',
                                  compress='lzw',
                                  dtype=numpy.float32)
                elif fmt == '.jpg':
                    kwargs.update(driver='JPEG', quality=90, dtype=numpy.uint8)

                kwargs.update(transform=patch_affine,
                              width=patch_width,
                              height=patch_height)

                box_radius *= scale
                name = hashlib.md5(str(patch_affine) + raster).hexdigest()
                image_name = os.path.join(output_folder, name + fmt)

                if csv_file_name is not None:
                    with open(os.path.join(output_folder, csv_file_name),
                              'a+') as csvf:
                        fields = gx, gy, r1, r2, theta, patch_width, patch_height, image_name
                        csvf.write(','.join([str(_) for _ in fields]) + '\n')

                with rasterio.open(image_name, 'w', **kwargs) as pf:
                    assert isinstance(pf, RasterUpdater)
                    for band in range(rf.count):
                        patch = rf.read(
                            band + 1,
                            window=((y0, y1), (x0, x1)),
                            boundless=True,
                        )
                        patch = patch.astype(numpy.float32)
                        patch_rotated = rotate(patch, theta, reshape=False)
                        patch_scaled = zoom(patch_rotated, scale)
                        i0 = int(round(box_radius - patch_height / 2.))
                        i1 = i0 + patch_height
                        j0 = int(round(box_radius - patch_width / 2.))
                        j1 = j0 + patch_width
                        patch_cropped = patch_scaled[i0:i1, j0:j1]

                        if clip:
                            patch_cropped = numpy.clip(patch_cropped, clipmin,
                                                       clipmax)
                        if stretch:
                            patch_cropped = (patch_cropped -
                                             clipmin) / (clipmax - clipmin)
                            patch_cropped = patch_cropped * (
                                stretchmax - stretchmin) + stretchmin

                        if fmt == '.jpg':
                            # JPEG does not support floating point output. All we can do is 8 bit
                            # (python has not 12bit array type)
                            patch_cropped = img_as_ubyte(
                                patch_cropped.clip(-1, 1))
                        pf.write(patch_cropped, band + 1)
        if not silent:
            pbar.finish()

        logger.debug("Finished.")