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()
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
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
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()
# 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