def getIntersectingFeatures(inputPath, inputPath2, outputPath, minIntersectionPercent=1, minAreaOfInput2Features=0): """ Puts feature from input1 in output, if it has a intersecting feature with input2. :param inputPath: :type inputPath: str :param inputPath2: :type inputPath2: str :param outputPath: :param minAreaOfInput2Features: need for too small houses like garden houses :type minAreaOfInput2Features: float | int :param minIntersectionPercent: :type minIntersectionPercent: float | int """ minIntersectionPercent /= 100. vectorDataSource = ogr.Open(inputPath, gdal.GA_ReadOnly) # type: ogr.DataSource vectorLayer = vectorDataSource.GetLayerByIndex(0) # type: ogr.Layer vectorDataSource2 = ogr.Open(inputPath2, gdal.GA_ReadOnly) # type: ogr.DataSource vectorLayer2 = vectorDataSource2.GetLayerByIndex(0) # type: ogr.Layer # create output shape file deleteFile(outputPath) outputDriver = vectorDataSource.GetDriver() outputDataSource = outputDriver.CreateDataSource( outputPath) # type: ogr.DataSource outputLayer = outputDataSource.CreateLayer( "intersecting", srs=vectorLayer.GetSpatialRef(), geom_type=vectorLayer.GetGeomType()) for feature1 in vectorLayer: # type: ogr.Feature geom1 = feature1.geometry() # type: ogr.Geometry areaOfIntersectingGeometries = 0 vectorLayer2.SetSpatialFilter(geom1) for feature2 in vectorLayer2: # type: ogr.Feature geom2 = feature2.geometry() # type: ogr.Geometry if geom2.Area() > minAreaOfInput2Features: if geom1.Intersects(geom2): areaOfIntersectingGeometries += geom1.Intersection( geom2).Area() if areaOfIntersectingGeometries / geom1.Area( ) > minIntersectionPercent: outputLayer.CreateFeature(feature1.Clone()) # save and cleanup outputDataSource.SyncToDisk() outputDataSource = None vectorDataSource = None vectorDataSource2 = None
def getPlantsAtCadastre(self, savePath, cadastrePolygonsPath, plantsMaskPath, distanceBetweenPoints=1, threshold=1): """ Creates plants at cadastre borders. Note: createPointsAlongLines uses qgis functionality, so there is dependency to qgis. :param savePath: :param cadastrePolygonsPath: :param plantsMaskPath: :param distanceBetweenPoints: :param threshold: """ filePathWithoutExtension, fileExtension = os.path.splitext(savePath) # Shrink to avoid overlapping borders polygonsBufferedPath = filePathWithoutExtension + "_bufferedPolygons.shp" createBuffers( inputPath=cadastrePolygonsPath, outputPath=polygonsBufferedPath, buffer=-0.5, outputFormat="ESRI Shapefile" ) # Convert polygons to lines linesPath = filePathWithoutExtension + "_lines.shp" convertPolygonsToLines(polygonsBufferedPath, linesPath) # Creating points along lines # todo: dont use qgis functionality here pointsPath = filePathWithoutExtension + "_points.shp" createPointsAlongLines( inputPath=linesPath, outputPath=pointsPath, distanceBetweenPoints=distanceBetweenPoints ) # Removing points where no vegetation is rasterDataSet = gdal.Open(plantsMaskPath) rasterData = np.array(rasterDataSet.ReadAsArray(), dtype=np.float16) rasterGeoTransform = rasterDataSet.GetGeoTransform() rasterDataSet = None getPointsGreaterThanThresholdInRasterImage( outputPath=savePath, vectorPath=pointsPath, rasterData=rasterData, rasterGeoTransform=rasterGeoTransform, pixelRadius=5, threshold=threshold ) # clean up deleteFile(polygonsBufferedPath) # deleteFile(linesPath) deleteFile(pointsPath)
def convertPolygonsToLines(inputPath, outputPath): """ Converts polygons of inputPath file to lines and saves at outputPath. Fields and Attributes will also be saved. Note: No multipolygon support right now. :type inputPath: str :type outputPath: str """ inputDataSource = ogr.Open(inputPath) # type: ogr.DataSource inputLayer = inputDataSource.GetLayerByIndex(0) # type: ogr.Layer # create output shape file deleteFile(outputPath) outputDriver = inputDataSource.GetDriver() outputDataSource = outputDriver.CreateDataSource( outputPath) # type: ogr.DataSource outputLayer = outputDataSource.CreateLayer("lines", srs=inputLayer.GetSpatialRef(), geom_type=ogr.wkbLineString) # create attribute fields firstFeature = inputLayer.GetNextFeature( ) # read first feature to get attributes if firstFeature is not None: for i in range(firstFeature.GetFieldCount()): fieldDef = firstFeature.GetFieldDefnRef(i) outputLayer.CreateField(fieldDef) inputLayer.ResetReading() for feature in inputLayer: # type: ogr.Feature # copy attributes outputFeature = ogr.Feature(inputLayer.GetLayerDefn()) for fieldId in range(0, feature.GetFieldCount()): outputFeature.SetField2(fieldId, feature.GetField(fieldId)) # create geometry if feature.geometry() is None: continue geom = feature.geometry().GetGeometryRef(0) # type: ogr.Geometry if geom is not None and geom.GetPointCount() > 0: line = ogr.Geometry(ogr.wkbLinearRing) for i in range(0, geom.GetPointCount()): x, y = geom.GetPoint_2D(i) line.AddPoint_2D(x, y) outputFeature.SetGeometry(line) outputLayer.CreateFeature(outputFeature) # save and cleanup outputDataSource.SyncToDisk() outputDataSource = None inputDataSource = None
def convertLinesToPolygons(inputPath, outputPath): """ Converts lines of inputPath file to polygons and saves at outputPath. Fields and Attributes will also be saved. :type inputPath: str :type outputPath: str """ inputDataSource = ogr.Open(inputPath) # type: ogr.DataSource inputLayer = inputDataSource.GetLayerByIndex(0) # type: ogr.Layer # create output shape file deleteFile(outputPath) outputDriver = inputDataSource.GetDriver() outputDataSource = outputDriver.CreateDataSource( outputPath) # type: ogr.DataSource outputLayer = outputDataSource.CreateLayer("polygons", srs=inputLayer.GetSpatialRef(), geom_type=ogr.wkbPolygon) # create attribute fields firstFeature = inputLayer.GetNextFeature( ) # read first feature to get attributes if firstFeature is not None: for i in range(firstFeature.GetFieldCount()): fieldDef = firstFeature.GetFieldDefnRef(i) outputLayer.CreateField(fieldDef) inputLayer.ResetReading() for feature in inputLayer: # type: ogr.Feature # copy attributes outputFeature = ogr.Feature(inputLayer.GetLayerDefn()) for fieldId in range(0, feature.GetFieldCount()): outputFeature.SetField2(fieldId, feature.GetField(fieldId)) # create geometry geom = feature.geometry() # type: ogr.Geometry if geom.IsRing(): outputGeom = ogr.Geometry(ogr.wkbPolygon) ring = ogr.Geometry(ogr.wkbLinearRing) for i in range(0, geom.GetPointCount()): x, y = geom.GetPoint_2D(i) ring.AddPoint_2D(x, y) outputGeom.AddGeometry(ring) outputFeature.SetGeometry(outputGeom) outputLayer.CreateFeature(outputFeature) # save and cleanup outputDataSource.SyncToDisk() outputDataSource = None inputDataSource = None
def convertToEPSG(inputPath, outputPath, outputFormat="GeoJSON", toEPSG=25832): """ Converts the given input path to the given EPSG and saves it in outputPath. :param inputPath: :type inputPath: str :param outputPath: :type outputPath: str :param outputFormat: ogr2ogr output format http://www.gdal.org/ogr2ogr.html :type outputFormat: str :param toEPSG: :type toEPSG: int """ deleteFile(outputPath) subprocess.call([ "ogr2ogr", "-f", outputFormat, "-t_srs", "EPSG:%d" % toEPSG, outputPath, inputPath ], startupinfo=getSubprocessStartUpInfo())
def convertRasterToVectorPolygons(dataPath, savePath, returnDataSource=False): """ Creates a vector shp file from given image file. It must be a saved image because gdals polygonize will only operate with GDALRasterBandShadow. :param dataPath: path where image to convert is. :type dataPath: str :param savePath: where shape file will be saved (ending .shp) :type savePath: str :param returnDataSource: if True, the shape file data source will be returned, else it will be closed :type returnDataSource: bool :return: :rtype: ogr.DataSource """ dataSet = gdal.Open(dataPath, gdal.GA_ReadOnly) vectorDriver = ogr.GetDriverByName("ESRI Shapefile") # type: ogr.Driver deleteFile(savePath) dataSource = vectorDriver.CreateDataSource( savePath) # type: ogr.DataSource srs = osr.SpatialReference() srs.ImportFromWkt(dataSet.GetProjection()) layer = dataSource.CreateLayer("polygonize", srs=srs) # type: ogr.Layer # create feature with attribute "DN", where polygonize will write in the original color a polygon was created from fieldName = 'DN' fd = ogr.FieldDefn(fieldName, ogr.OFTInteger) layer.CreateField(fd) fieldId = 0 gdal.Polygonize(dataSet.GetRasterBand(1), None, layer, fieldId, callback=None) dataSet = None if returnDataSource is True: return dataSource else: dataSource = None
def CopyExtentandEPSG(EEPSG, noEEPSG, targetPath): """ Copy extent and EPSG from input raster to output raster """ i = 0 while i < 2: source_extent = gdal.Open(EEPSG, GA_ReadOnly) target_extent = gdal.Open(noEEPSG, GA_Update) target_extent.SetGeoTransform(source_extent.GetGeoTransform()) i = i + 1 subprocess.call( 'gdalwarp ' + noEEPSG + ' ' + targetPath + ' -t_srs "+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"' ) # cleanup target_extent = None deleteFile(noEEPSG)
def createBuffers(inputPath, outputPath, outputFormat="GeoJSON", buffer=1): """ Buffers input data. :param inputPath: path to polygon vector layer :type inputPath: str :param outputPath: path where raster will be saved :type outputPath: str :param buffer: buffer amount in epsg units :type buffer: float """ inputDataSource = ogr.Open(inputPath) # type: ogr.DataSource inputLayer = inputDataSource.GetLayerByIndex(0) # type: ogr.Layer layerName = inputLayer.GetName() inputDataSource = None deleteFile(outputPath) subprocess.call([ "ogr2ogr", "-dialect", "sqlite", "-f", outputFormat, "-sql", "SELECT ST_Buffer( geometry , " + str(buffer) + " ),* FROM '" + layerName + "' ", outputPath, inputPath ], startupinfo=getSubprocessStartUpInfo())
def getPointsGreaterThanThresholdInRasterImage(outputPath, vectorPath, rasterData, rasterGeoTransform, pixelRadius=5, threshold=0): """ Samples rasterData with given points of a vector file and looks if these samples are higher than a given threshold. For each point in the vectorPath file do: - get position of point in the rasterData - at this position create a window with the pixelRadius - calculate the mean of all pixels in this window - if this mean is create than the given threshold, the point of the vectorPath file will be copied to output file :param outputPath: :type outputPath: str :param vectorPath: path to point vector data :type vectorPath: str :param rasterData: :type rasterData: np.ndarray :param rasterGeoTransform: :type rasterGeoTransform: tuple :param pixelRadius: :type pixelRadius: int :param threshold: :type threshold: """ vectorDataSource = ogr.Open(vectorPath, gdal.GA_Update) # type: ogr.DataSource vectorLayer = vectorDataSource.GetLayerByIndex(0) # type: ogr.Layer # create output shape file deleteFile(outputPath) outputDriver = vectorDataSource.GetDriver() outputDataSource = outputDriver.CreateDataSource( outputPath) # type: ogr.DataSource outputLayer = outputDataSource.CreateLayer("points", srs=vectorLayer.GetSpatialRef(), geom_type=ogr.wkbPoint) for feature in vectorLayer: # type: ogr.Feature xCoord, yCoord = feature.geometry().GetPoint_2D(0) xRasterPos, yRasterPos = coord2pixel(rasterGeoTransform, xCoord, yCoord) if 0 <= xRasterPos < rasterData.shape[ 1] and 0 <= yRasterPos < rasterData.shape[0]: imageCutout = rasterData[yRasterPos - pixelRadius:yRasterPos + pixelRadius, xRasterPos - pixelRadius:xRasterPos + pixelRadius] # type: np.ndarray mean = imageCutout.mean() if mean >= threshold: outputLayer.CreateFeature(feature.Clone()) # else: # vectorLayer.DeleteFeature(feature.GetFID()) # save and cleanup outputDataSource.SyncToDisk() outputDataSource = None vectorDataSource = None
def getVectorDataFromWFS(url, layerName, extent, outputPath, outputFormat="GeoJSON", epsg=25832, append=False, attributeFilter=""): """ Creates vector file from given WFS. If too many features (>10000) are requested, then the extent will be split up, so the features per extent decreases. This is done recursively. Because features can overlap at extent borders, the file will be searched for duplicates. These will be removed. :param url: url to WFS :type url: str :param layerName: name of the layer in the WFS which should be downloaded :type layerName: str :param extent: (minX, minY, maxX, maxY) :type extent: tuple[float, float, float, float] :param outputPath: save path :type outputPath: str :param outputFormat: ogr output format string like "GeoJSON" or "ESRI Shapefile", NOTE: the output format should allow appending data! :type outputFormat: str :param epsg: target epsg (in which extent is) :type epsg: int :param append: if True, the data will append to the output file :type append: bool :param attributeFilter: filtering of attributes with SQL WHERE like "Amtlicheflaeche > '300' AND Amtlicheflaeche < '2000'" :type attributeFilter: str """ MAX_FEATURE_COUNT = 9999999 # splitting not working anymore, don't know why, maybe its the server: # ERROR 1: Layer tlvermgeo:ALKIS_GESAMT_FKZ not found, and CreateLayer not supported by driver. # so data will now be caped at 10000 if not append: deleteFile(outputPath) # check how many features we want (we can only get 10000) wfsDriver = ogr.GetDriverByName('WFS') wfs = wfsDriver.Open("WFS:" + url) # type: ogr.DataSource wfsLayer = wfs.GetLayerByName(layerName) # type: ogr.Layer minX = extent[0] minY = extent[1] maxX = extent[2] maxY = extent[3] extentWkt = "POLYGON ((" + str(minX) + " " + str(minY) + "," \ "" + str(minX) + " " + str(maxY) + ", " \ "" + str( maxX) + " " + str(maxY) + ", " \ "" + str(maxX) + " " + str(minY) + "," \ "" + str(minX) + " " + str(minY) + "))" wfsLayer.SetSpatialFilter(ogr.CreateGeometryFromWkt(extentWkt)) wfsLayer.SetAttributeFilter(attributeFilter) featureCount = wfsLayer.GetFeatureCount() if featureCount > MAX_FEATURE_COUNT: # is maximal number of features exceeded, than split extent in 4 parts print "Too many features in WFS request %d, splitting up extent" % featureCount filePathWithoutExtension, fileExtension = os.path.splitext(outputPath) tempPath = filePathWithoutExtension + "_temp" + fileExtension wfs = None halfWidth = (maxX - minX) / 2 halfHeight = (maxY - minY) / 2 # lower left getVectorDataFromWFS(url, layerName=layerName, extent=(minX, minY, minX + halfWidth, minY + halfHeight), outputPath=tempPath, outputFormat=outputFormat, append=True, attributeFilter=attributeFilter) # lower right getVectorDataFromWFS(url, layerName=layerName, extent=(minX + halfWidth, minY, maxX, minY + halfHeight), outputPath=tempPath, outputFormat=outputFormat, append=True, attributeFilter=attributeFilter) # upper left getVectorDataFromWFS(url, layerName=layerName, extent=(minX, minY + halfHeight, minX + halfWidth, maxY), outputPath=tempPath, outputFormat=outputFormat, append=True, attributeFilter=attributeFilter) # upper right getVectorDataFromWFS(url, layerName=layerName, extent=(minX + halfWidth, minY + halfHeight, maxX, maxY), outputPath=tempPath, outputFormat=outputFormat, append=True, attributeFilter=attributeFilter) # remove duplicate entries from overlapping bounds tempDataSource = ogr.Open(tempPath) # type: ogr.DataSource tempLayer = tempDataSource.GetLayer() # type: ogr.Layer geometries = [] for feature in tempLayer: # type: ogr.Feature geometries.append(feature.geometry().ExportToWkt()) uniqueGeometriesWkt = [ attribute for attribute, count in Counter(geometries).items() if count > 1 ] # create final output file and add unique features outputDriver = ogr.GetDriverByName(outputFormat) outputDataSource = outputDriver.CreateDataSource( outputPath) # type: ogr.DataSource outputLayer = outputDataSource.CreateLayer( "cadastre", srs=tempLayer.GetSpatialRef(), geom_type=tempLayer.GetGeomType()) # type: ogr.Layer tempLayer.ResetReading() for feature in tempLayer: # type: ogr.Feature geomWkt = feature.geometry().ExportToWkt() if geomWkt in uniqueGeometriesWkt: uniqueGeometriesWkt.remove(geomWkt) outputLayer.CreateFeature(feature.Clone()) outputDataSource = None tempDataSource = None deleteFile(tempPath) return wfs = None subprocess.call([ "ogr2ogr", "-f", outputFormat, "-t_srs", "EPSG:%d" % epsg, "-spat", str(extent[0]), str(extent[1]), str(extent[2]), str(extent[3]), outputPath, "WFS:" + url, "-append", "-where", attributeFilter, layerName ], startupinfo=getSubprocessStartUpInfo())
def createLayer(geomType, name, memType="memory", deleteOldLayerFile=False, crs=25832, path=None, driverName="ESRI Shapefile"): """ Creates a QGIS layer and adds the layer. :param geomType: geometry type, valid values: 'polygon', 'linestring', 'point' :type geomType: str :param name: name of the layer :type name: str :param memType: 'memory' for layer in memory or 'ogr' for shape file on disk in temp folder :type memType: str :param deleteOldLayerFile: if True, temporary data will be deleted first :type deleteOldLayerFile: bool :param crs: epsg id of coordinate reference system :type crs: int :rtype: QgsVectorLayer """ geomType = geomType.lower() memType = memType.lower() removeMapLayersByName(name) if memType == "memory": layer = QgsVectorLayer(geomType + "?crs=epsg:" + str(crs), name, memType) elif memType == "ogr": if path is None: if driverName == "GeoJSON": path = getPathToTempFile(name + ".geojson") else: path = getPathToTempFile(name + ".shp") if QFileInfo(path).exists() is False or deleteOldLayerFile is True: if deleteOldLayerFile is True: deleteFile(path) # deleteFilesContainingName(name) if geomType == "polygon": geomType = QGis.WKBPolygon elif geomType == "linestring": geomType = QGis.WKBLineString elif geomType == "point": geomType = QGis.WKBPoint else: raise NotImplementedError( "Only polygon, linestring and point support for now, not " + memType) fields = QgsFields() fields.append(QgsField("id", QVariant.Int)) writer = QgsVectorFileWriter(path, "CP1250", fields, geomType, QgsCoordinateReferenceSystem(crs), driverName) print writer.errorMessage() del writer else: print "Using existing file: " + path layer = QgsVectorLayer(path, name, "ogr") else: raise TypeError("MemoryType not found") return layer