示例#1
0
文件: merge.py 项目: baloola/ngeo-b
def generate_footprint_wkt(ds, simplification_factor=2):
    """ Generate a fooptrint from a raster, using black/no-data as exclusion
    """

    # create an empty boolean array initialized as 'False' to store where
    # values exist as a mask array.
    nodata_map = np.zeros((ds.RasterYSize, ds.RasterXSize), dtype=np.bool)

    for idx in range(1, ds.RasterCount + 1):
        band = ds.GetRasterBand(idx)
        raster_data = band.ReadAsArray()
        nodata = band.GetNoDataValue()

        if nodata is None:
            nodata = 0

        # apply the output to the map
        nodata_map |= (raster_data != nodata)

    # create a temporary in-memory dataset and write the nodata mask
    # into its single band
    with temporary_dataset(ds.RasterXSize + 2, ds.RasterYSize + 2, 1,
                           gdal.GDT_Byte) as tmp_ds:
        copy_projection(ds, tmp_ds)
        tmp_band = tmp_ds.GetRasterBand(1)
        tmp_band.WriteArray(nodata_map.astype(np.uint8))

        # create an OGR in memory layer to hold the created polygon
        sr = osr.SpatialReference()
        sr.ImportFromWkt(ds.GetProjectionRef())
        ogr_ds = ogr.GetDriverByName('Memory').CreateDataSource('out')
        layer = ogr_ds.CreateLayer('poly', sr.sr, ogr.wkbPolygon)
        fd = ogr.FieldDefn('DN', ogr.OFTInteger)
        layer.CreateField(fd)

        # polygonize the mask band and store the result in the OGR layer
        gdal.Polygonize(tmp_band, tmp_band, layer, 0)

    if layer.GetFeatureCount() != 1:
        # if there is more than one polygon, compute the minimum bounding polygon
        geometry = ogr.Geometry(ogr.wkbPolygon)
        while True:
            feature = layer.GetNextFeature()
            if not feature:
                break
            geometry = geometry.Union(feature.GetGeometryRef())

        # TODO: improve this for a better minimum bounding polygon
        geometry = geometry.ConvexHull()

    else:
        # obtain geometry from the first (and only) layer
        feature = layer.GetNextFeature()
        geometry = feature.GetGeometryRef()

    if geometry.GetGeometryType() not in (ogr.wkbPolygon, ogr.wkbMultiPolygon):
        raise RuntimeError("Error during poligonization. Wrong geometry "
                           "type.")

    # check if reprojection to latlon is necessary
    if not sr.IsGeographic():
        dst_sr = osr.SpatialReference()
        dst_sr.ImportFromEPSG(4326)
        try:
            geometry.TransformTo(dst_sr.sr)
        except RuntimeError:
            geometry.Transform(osr.CoordinateTransformation(sr.sr, dst_sr.sr))

    gt = ds.GetGeoTransform()
    resolution = min(abs(gt[1]), abs(gt[5]))

    simplification_value = simplification_factor * resolution

    # simplify the polygon. the tolerance value is *really* vague
    try:
        # SimplifyPreserveTopology() available since OGR 1.9.0
        geometry = geometry.SimplifyPreserveTopology(simplification_value)
    except AttributeError:
        # use GeoDjango bindings if OGR is too old
        geometry = ogr.CreateGeometryFromWkt(
            GEOSGeometry(geometry.ExportToWkt()).simplify(
                simplification_value, True).wkt)

    return geometry.ExportToWkt()
示例#2
0
    def apply(self, src_ds):
        # setup
        dst_sr = osr.SpatialReference()
        gcp_sr = osr.SpatialReference()

        dst_sr.ImportFromEPSG(self.srid if self.srid is not None
                              else self.gcp_srid)
        gcp_sr.ImportFromEPSG(self.gcp_srid)


        logger.debug("Using GCP Projection '%s'" % gcp_sr.ExportToWkt())
        logger.debug("Applying GCPs: MULTIPOINT(%s) -> MULTIPOINT(%s)"
                      % (", ".join([("(%f %f)") % (gcp.GCPX, gcp.GCPY) for gcp in self.gcps]) ,
                      ", ".join([("(%f %f)") % (gcp.GCPPixel, gcp.GCPLine) for gcp in self.gcps])))
        # set the GCPs
        src_ds.SetGCPs(self.gcps, gcp_sr.ExportToWkt())

        # Try to find and use the best transform method/order.
        # Orders are: -1 (TPS), 3, 2, and 1 (all GCP)
        # Loop over the min and max GCP number to order map.
        for min_gcpnum, max_gcpnum, order in [(3, None, -1), (10, None, 3), (6, None, 2), (3, None, 1)]:
            # if the number of GCP matches
            if len(self.gcps) >= min_gcpnum and (max_gcpnum is None or len(self.gcps) <= max_gcpnum):
                try:

                    if ( order < 0 ) :
                        # let the reftools suggest the right interpolator
                        rt_prm = rt.suggest_transformer( src_ds )
                    else:
                        # use the polynomial GCP interpolation as requested
                        rt_prm = { "method":rt.METHOD_GCP, "order":order }

                    logger.debug("Trying order '%i' {method:%s,order:%s}" % \
                        (order, rt.METHOD2STR[rt_prm["method"]] , rt_prm["order"] ) )
                    # get the suggested pixel size/geotransform
                    size_x, size_y, geotransform = rt.suggested_warp_output(
                        src_ds,
                        None,
                        dst_sr.ExportToWkt(),
                        **rt_prm
                    )
                    if size_x > 100000 or size_y > 100000:
                        raise RuntimeError("Calculated size exceeds limit.")
                    logger.debug("New size is '%i x %i'" % (size_x, size_y))

                    # create the output dataset
                    dst_ds = create_mem(size_x, size_y,
                                        src_ds.RasterCount,
                                        src_ds.GetRasterBand(1).DataType)

                    # reproject the image
                    dst_ds.SetProjection(dst_sr.ExportToWkt())
                    dst_ds.SetGeoTransform(geotransform)

                    rt.reproject_image(src_ds, "", dst_ds, "", **rt_prm )

                    copy_metadata(src_ds, dst_ds)

                    # retrieve the footprint from the given GCPs
                    footprint_wkt = rt.get_footprint_wkt(src_ds, **rt_prm )

                except RuntimeError as e:
                    logger.debug("Failed using order '%i'. Error was '%s'."
                                 % (order, str(e)))
                    # the given method was not applicable, use the next one
                    continue

                else:
                    logger.debug("Successfully used order '%i'" % order)
                    # the transform method was successful, exit the loop
                    break
        else:
            # no method worked, so raise an error
            raise GCPTransformException("Could not find a valid transform method.")

        # reproject the footprint to a lon/lat projection if necessary
        if not gcp_sr.IsGeographic():
            out_sr = osr.SpatialReference()
            out_sr.ImportFromEPSG(4326)
            geom = ogr.CreateGeometryFromWkt(footprint_wkt, gcp_sr)
            geom.TransformTo(out_sr)
            footprint_wkt = geom.ExportToWkt()

        logger.debug("Calculated footprint: '%s'." % footprint_wkt)

        return dst_ds, footprint_wkt
示例#3
0
    def process(self,
                input_filename,
                output_filename,
                geo_reference=None,
                generate_metadata=True,
                merge_with=None,
                original_footprint=None):

        # open the dataset and create an In-Memory Dataset as copy
        # to perform optimizations
        ds = create_mem_copy(gdal.Open(input_filename))

        gt = ds.GetGeoTransform()
        footprint_wkt = None

        if not geo_reference:
            if gt == (0.0, 1.0, 0.0, 0.0, 0.0, 1.0):
                if ds.GetGCPCount() > 0:
                    geo_reference = InternalGCPs()
                else:
                    raise ValueError("No geospatial reference for "
                                     "unreferenced dataset given.")

        if geo_reference:
            logger.debug("Applying geo reference '%s'." %
                         type(geo_reference).__name__)
            # footprint is always in EPSG:4326
            ds, footprint_wkt = geo_reference.apply(ds)

        # apply optimizations
        for optimization in self.get_optimizations(ds):
            logger.debug("Applying optimization '%s'." %
                         type(optimization).__name__)

            try:
                new_ds = optimization(ds)

                if new_ds is not ds:
                    # cleanup afterwards
                    cleanup_temp(ds)
                    ds = new_ds
            except:
                cleanup_temp(ds)
                raise

        # generate the footprint from the dataset
        if not footprint_wkt:
            logger.debug("Generating footprint.")
            footprint_wkt = self._generate_footprint_wkt(ds)
        # check that footprint is inside of extent of generated image
        # regenerate otherwise
        else:
            tmp_extent = getExtentFromRectifiedDS(ds)
            tmp_bbox = Polygon.from_bbox(
                (tmp_extent[0], tmp_extent[1], tmp_extent[2], tmp_extent[3]))
            # transform image bbox to EPSG:4326 if necessary
            proj = ds.GetProjection()
            srs = osr.SpatialReference()
            try:
                srs.ImportFromWkt(proj)
                srs.AutoIdentifyEPSG()
                ptype = "PROJCS" if srs.IsProjected() else "GEOGCS"
                srid = int(srs.GetAuthorityCode(ptype))
                if srid != '4326':
                    out_srs = osr.SpatialReference()
                    out_srs.ImportFromEPSG(4326)
                    transform = osr.CoordinateTransformation(srs, out_srs)
                    tmp_bbox2 = ogr.CreateGeometryFromWkt(tmp_bbox.wkt)
                    tmp_bbox2.Transform(transform)
                    tmp_bbox = GEOSGeometry(tmp_bbox2.ExportToWkt())
            except (RuntimeError, TypeError), e:
                logger.warn("Projection: %s" % proj)
                logger.warn("Failed to identify projection's EPSG code."
                            "%s: %s" % (type(e).__name__, str(e)))

            tmp_footprint = GEOSGeometry(footprint_wkt)
            if not tmp_bbox.contains(tmp_footprint):
                logger.debug("Re-generating footprint because not inside of "
                             "generated image.")
                footprint_wkt = tmp_footprint.intersection(tmp_bbox).wkt
示例#4
0
    def _generate_footprint_wkt(self, ds):
        """ Generate a footprint from a raster, using black/no-data as
            exclusion
        """

        # create an empty boolean array initialized as 'False' to store where
        # values exist as a mask array.
        nodata_map = numpy.zeros((ds.RasterYSize, ds.RasterXSize),
                                 dtype=numpy.bool)

        for idx in range(1, ds.RasterCount + 1):
            band = ds.GetRasterBand(idx)
            raster_data = band.ReadAsArray()
            nodata = band.GetNoDataValue()

            if nodata is None:
                nodata = 0

            # apply the output to the map
            nodata_map |= (raster_data != nodata)

        # create a temporary in-memory dataset and write the nodata mask
        # into its single band
        tmp_ds = create_mem(ds.RasterXSize + 2, ds.RasterYSize + 2, 1,
                            gdal.GDT_Byte)
        copy_projection(ds, tmp_ds)
        tmp_band = tmp_ds.GetRasterBand(1)
        tmp_band.WriteArray(nodata_map.astype(numpy.uint8))

        # Remove unwanted small areas of nodata
        # www.gdal.org/gdal__alg_8h.html#a33309c0a316b223bd33ae5753cc7f616
        no_pixels = tmp_ds.RasterXSize * tmp_ds.RasterYSize
        threshold = 4
        max_threshold = (no_pixels / 16)
        if self.sieve_max_threshold > 0:
            max_threshold = self.sieve_max_threshold
        while threshold <= max_threshold and threshold < 2147483647:
            gdal.SieveFilter(tmp_band, None, tmp_band, threshold, 4)
            threshold *= 4

        #for debugging:
        #gdal.GetDriverByName('GTiff').CreateCopy('/tmp/test.tif', tmp_ds)

        # create an OGR in memory layer to hold the created polygon
        sr = osr.SpatialReference()
        sr.ImportFromWkt(ds.GetProjectionRef())
        ogr_ds = ogr.GetDriverByName('Memory').CreateDataSource('out')
        layer = ogr_ds.CreateLayer('poly', sr, ogr.wkbPolygon)
        fd = ogr.FieldDefn('DN', ogr.OFTInteger)
        layer.CreateField(fd)

        # polygonize the mask band and store the result in the OGR layer
        gdal.Polygonize(tmp_band, tmp_band, layer, 0)

        tmp_ds = None

        if layer.GetFeatureCount() > 1:
            # if there is more than one polygon, compute the minimum
            # bounding polygon
            logger.debug("Merging %s features in footprint." %
                         layer.GetFeatureCount())

            # union all features in one multi-polygon
            geometry = ogr.Geometry(ogr.wkbMultiPolygon)
            while True:
                feature = layer.GetNextFeature()
                if not feature:
                    break
                geometry.AddGeometry(feature.GetGeometryRef())
            geometry = geometry.UnionCascaded()

            # TODO: improve this for a better minimum bounding polygon
            geometry = geometry.ConvexHull()

        elif layer.GetFeatureCount() < 1:
            # there was an error during polygonization
            raise RuntimeError("Error during polygonization. No feature "
                               "obtained.")
        else:
            # obtain geometry from the first (and only) layer
            feature = layer.GetNextFeature()
            geometry = feature.GetGeometryRef()

        if geometry.GetGeometryType() != ogr.wkbPolygon:
            raise RuntimeError(
                "Error during polygonization. Wrong geometry "
                "type: %s" %
                ogr.GeometryTypeToName(geometry.GetGeometryType()))

        # check if reprojection to latlon is necessary
        if not sr.IsGeographic():
            dst_sr = osr.SpatialReference()
            dst_sr.ImportFromEPSG(4326)
            try:
                geometry.TransformTo(dst_sr)
            except RuntimeError:
                geometry.Transform(osr.CoordinateTransformation(sr, dst_sr))

        gt = ds.GetGeoTransform()
        resolution = min(abs(gt[1]), abs(gt[5]))

        simplification_value = self.simplification_factor * resolution

        #for debugging:
        #geometry.GetGeometryRef(0).GetPointCount()

        # simplify the polygon. the tolerance value is *really* vague
        try:
            # SimplifyPreserveTopology() available since OGR 1.9.0
            geometry = geometry.SimplifyPreserveTopology(simplification_value)
        except AttributeError:
            # use GeoDjango bindings if OGR is too old
            geometry = ogr.CreateGeometryFromWkt(
                GEOSGeometry(geometry.ExportToWkt()).simplify(
                    simplification_value, True).wkt)

        return geometry.ExportToWkt()
示例#5
0
                    # retrieve the footprint from the given GCPs
                    footprint_wkt = reftools.get_footprint_wkt(
                        src_ds, **rt_prm)

                except RuntimeError, e:
                    logger.debug("Failed using order '%i'. Error was '%s'." %
                                 (order, str(e)))
                    # the given method was not applicable, use the next one
                    continue

                else:
                    logger.debug("Successfully used order '%i'" % order)
                    # the transform method was successful, exit the loop
                    break
        else:
            # no method worked, so raise an error
            raise GCPTransformException(
                "Could not find a valid transform method.")

        # reproject the footprint to a lon/lat projection if necessary
        if not dst_sr.IsGeographic():
            out_sr = osr.SpatialReference()
            out_sr.ImportFromEPSG(4326)
            geom = ogr.CreateGeometryFromWkt(footprint_wkt, gcp_sr)
            geom.TransformTo(out_sr)
            footprint_wkt = geom.ExportToWkt()

        logger.debug("Calculated footprint: '%s'." % footprint_wkt)

        return dst_ds, footprint_wkt