def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context) target_crs = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context) target_geom = QgsGeometry.fromRect(extent) fields = QgsFields() fields.append(QgsField('auth_id', QVariant.String, '', 20)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) # make intersection tests nice and fast engine = QgsGeometry.createGeometryEngine(target_geom.constGet()) engine.prepareGeometry() layer_bounds = QgsGeometry.fromRect(source.sourceExtent()) crses_to_check = QgsCoordinateReferenceSystem.validSrsIds() total = 100.0 / len(crses_to_check) found_results = 0 transform_context = QgsCoordinateTransformContext() for current, srs_id in enumerate(crses_to_check): if feedback.isCanceled(): break candidate_crs = QgsCoordinateReferenceSystem.fromSrsId(srs_id) if not candidate_crs.isValid(): continue transform_candidate = QgsCoordinateTransform(candidate_crs, target_crs, transform_context) transformed_bounds = QgsGeometry(layer_bounds) try: if not transformed_bounds.transform(transform_candidate) == 0: continue except: continue try: if engine.intersects(transformed_bounds.constGet()): feedback.pushInfo(self.tr('Found candidate CRS: {}').format(candidate_crs.authid())) f = QgsFeature(fields) f.setAttributes([candidate_crs.authid()]) sink.addFeature(f, QgsFeatureSink.FastInsert) found_results += 1 except: continue feedback.setProgress(int(current * total)) if found_results == 0: feedback.reportError(self.tr('No matching projections found')) return {self.OUTPUT: dest_id}
def testApproxFeatureCountAndExtent(self): """ Test perf improvement for for https://issues.qgis.org/issues/18402 """ tmpfile = os.path.join(self.basetestpath, 'testApproxFeatureCountAndExtent.gpkg') ds = ogr.GetDriverByName('GPKG').CreateDataSource(tmpfile) lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)')) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2 3)')) lyr.CreateFeature(f) fid = f.GetFID() f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(4 5)')) lyr.CreateFeature(f) lyr.DeleteFeature(fid) ds = None ds = ogr.Open(tmpfile, update=1) ds.ExecuteSQL('DROP TABLE gpkg_ogr_contents') ds = None os.environ['QGIS_GPKG_FC_THRESHOLD'] = '1' vl = QgsVectorLayer(u'{}'.format(tmpfile) + "|layername=" + "test", 'test', u'ogr') self.assertTrue(vl.isValid()) fc = vl.featureCount() del os.environ['QGIS_GPKG_FC_THRESHOLD'] self.assertEqual(fc, 3) # didn't notice the hole reference = QgsGeometry.fromRect(QgsRectangle(0, 1, 4, 5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0])
def __init__(self, xmin, ymin, xmax, ymax, x_segments, y_segments): self.vbands = [] self.hbands = [] self.vidx = QgsSpatialIndex() self.hidx = QgsSpatialIndex() xres = (xmax - xmin) / x_segments yres = (ymax - ymin) / y_segments self.xmin, self.ymax, self.xres, self.yres = xmin, ymax, xres, yres def addVBand(idx, geom): f = QgsFeature(idx) f.setGeometry(geom) self.vbands.append(f) self.vidx.insertFeature(f) def addHBand(idx, geom): f = QgsFeature(idx) f.setGeometry(geom) self.hbands.append(f) self.hidx.insertFeature(f) for x in range(x_segments): addVBand(x, QgsGeometry.fromRect(QgsRectangle(xmin + x * xres, ymin, xmin + (x + 1) * xres, ymax))) for y in range(y_segments): addHBand(y, QgsGeometry.fromRect(QgsRectangle(xmin, ymax - (y + 1) * yres, xmax, ymax - y * yres)))
def processAlgorithm(self, feedback): extent = str(self.getParameterValue(self.EXTENT)).split(',') spacing = float(self.getParameterValue(self.SPACING)) inset = float(self.getParameterValue(self.INSET)) randomize = self.getParameterValue(self.RANDOMIZE) isSpacing = self.getParameterValue(self.IS_SPACING) crsId = self.getParameterValue(self.CRS) crs = QgsCoordinateReferenceSystem() crs.createFromUserInput(crsId) extent = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, crs) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine(extent_geom.geometry()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if randomize: geom = QgsGeometry().fromPoint(QgsPoint( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry().fromPoint(QgsPoint(x, y)) if extent_engine.intersects(geom.geometry()): f.setAttribute('id', count) f.setGeometry(geom) writer.addFeature(f) x += pSpacing count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing del writer
def testCancelAndDestroy(self): """ test that nothing goes wrong if we destroy a canvas while a job is canceling """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") # add a ton of features for i in range(5000): f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() self.assertTrue(canvas.isDrawing()) canvas.stopRendering() del canvas
def layerExtent(self, source, sink, feedback): rect = source.sourceExtent() geometry = QgsGeometry.fromRect(rect) minx = rect.xMinimum() miny = rect.yMinimum() maxx = rect.xMaximum() maxy = rect.yMaximum() height = rect.height() width = rect.width() cntx = minx + width / 2.0 cnty = miny + height / 2.0 area = width * height perim = 2 * width + 2 * height feat = QgsFeature() feat.setGeometry(geometry) attrs = [ minx, miny, maxx, maxy, cntx, cnty, area, perim, height, width, ] feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert)
def __signal_pbCopyKml_clicked(self, cheked): # Extent Openlayers action = "map.getExtent().toGeometry().toString();" wkt = self.webViewMap.page().mainFrame().evaluateJavaScript(action) rect = QgsGeometry.fromWkt(wkt).boundingBox() srsGE = QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem.EpsgCrsId) coodTrans = QgsCoordinateTransform(self.__srsOL, srsGE, QgsProject.instance()) rect = coodTrans.transform( rect, QgsCoordinateTransform.ForwardTransform) line = QgsGeometry.fromRect(rect).asPolygon()[0] wkt = str(QgsGeometry.fromPolylineXY(line).asWkt()) # Kml proj4 = str(srsGE.toProj4()) kmlLine = bindogr.exportKml(wkt, proj4) kml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\ "<kml xmlns=\"http://www.opengis.net/kml/2.2\" " \ "xmlns:gx=\"http://www.google.com/kml/ext/2.2\" " \ "xmlns:kml=\"http://www.opengis.net/kml/2.2\" " \ "xmlns:atom=\"http://www.w3.org/2005/Atom\">" \ "<Placemark>" \ "<name>KML from Plugin Openlayers Overview for QGIS</name>" \ "<description>Extent of openlayers map from Plugin Openlayers \ Overview for QGIS</description>"\ "%s" \ "</Placemark></kml>" % kmlLine clipBoard = QApplication.clipboard() clipBoard.setText(kml)
def get_bounding_box(self): """ Get the geometry of the bbox in WGS84 @rtype: QGsRectangle in WGS84 @return: the extent of the map canvas """ # If mapCanvas is checked if self.radioButton_extentMapCanvas.isChecked(): geom_extent = iface.mapCanvas().extent() if hasattr(iface.mapCanvas(), "mapSettings"): source_crs = iface.mapCanvas().mapSettings().destinationCrs() else: source_crs = iface.mapCanvas().mapRenderer().destinationCrs() else: # Else if a layer is checked layer = self.comboBox_extentLayer.currentLayer() geom_extent = layer.extent() source_crs = layer.crs() geom_extent = QgsGeometry.fromRect(geom_extent) epsg_4326 = QgsCoordinateReferenceSystem('EPSG:4326') crs_transform = QgsCoordinateTransform(source_crs, epsg_4326) geom_extent.transform(crs_transform) return geom_extent.boundingBox()
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_LAYER)) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter( layer.fields().toList(), QgsWkbTypes.Polygon, layer.crs()) features = vector.features(layer) total = 100.0 / len(features) for current, input_feature in enumerate(features): output_feature = input_feature input_geometry = input_feature.geometry() if input_geometry: output_geometry = QgsGeometry.fromRect(input_geometry.boundingBox()) if not output_geometry: raise GeoAlgorithmExecutionException( self.tr('Error calculating bounding box')) output_feature.setGeometry(output_geometry) writer.addFeature(output_feature) progress.setPercentage(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): spacing = self.parameterAsDouble(parameters, self.SPACING, context) inset = self.parameterAsDouble(parameters, self.INSET, context) randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context) isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context) crs = self.parameterAsCrs(parameters, self.CRS, context) extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, crs) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine(extent_geom.constGet()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if feedback.isCanceled(): break if randomize: geom = QgsGeometry().fromPointXY(QgsPointXY( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry().fromPointXY(QgsPointXY(x, y)) if extent_engine.intersects(geom.constGet()): f.setAttribute('id', count) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) x += pSpacing count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing return {self.OUTPUT: dest_id}
def testExtent(self): reference = QgsGeometry.fromRect( QgsRectangle(-71.123, 66.33, -65.32, 78.3)) provider_extent = self.source.extent() self.assertAlmostEqual(provider_extent.xMinimum(), -71.123, 3) self.assertAlmostEqual(provider_extent.xMaximum(), -65.32, 3) self.assertAlmostEqual(provider_extent.yMinimum(), 66.33, 3) self.assertAlmostEqual(provider_extent.yMaximum(), 78.3, 3) if self.source.supportsSubsetString(): # with only one point subset = self.getSubsetString3() self.source.setSubsetString(subset) count = self.source.featureCount() provider_extent = self.source.extent() self.source.setSubsetString(None) self.assertEqual(count, 1) self.assertAlmostEqual(provider_extent.xMinimum(), -68.2, 3) self.assertAlmostEqual(provider_extent.xMaximum(), -68.2, 3) self.assertAlmostEqual(provider_extent.yMinimum(), 70.8, 3) self.assertAlmostEqual(provider_extent.yMaximum(), 70.8, 3) # with no points subset = self.getSubsetStringNoMatching() self.source.setSubsetString(subset) count = self.source.featureCount() provider_extent = self.source.extent() self.source.setSubsetString(None) self.assertEqual(count, 0) self.assertTrue(provider_extent.isNull())
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context, source.fields(), QgsWkbTypes.Polygon, source.sourceCrs()) features = source.getFeatures() total = 100.0 / source.featureCount() for current, input_feature in enumerate(features): if feedback.isCanceled(): break output_feature = input_feature input_geometry = input_feature.geometry() if input_geometry: output_geometry = QgsGeometry.fromRect(input_geometry.boundingBox()) if not output_geometry: raise GeoAlgorithmExecutionException( self.tr('Error calculating bounding box')) output_feature.setGeometry(output_geometry) sink.addFeature(output_feature) feedback.setProgress(int(current * total)) return {self.OUTPUT_LAYER: dest_id}
def get_bounding_box(self): """ Get the geometry of the bbox in WGS84 @rtype: QGsRectangle in WGS84 @return: the extent of the map canvas """ # If mapCanvas is checked if self.radioButton_extentMapCanvas.isChecked(): geom_extent = iface.mapCanvas().extent() if hasattr(iface.mapCanvas(), "mapSettings"): source_crs = iface.mapCanvas().mapSettings().destinationCrs() else: source_crs = iface.mapCanvas().mapRenderer().destinationCrs() else: # Else if a layer is checked index = self.comboBox_extentLayer.currentIndex() layer_id = self.comboBox_extentLayer.itemData(index) layers = iface.legendInterface().layers() for layer in layers: if layer.id() == layer_id: geom_extent = layer.extent() source_crs = layer.crs() break else: # the layer could be deleted before layer_name = self.comboBox_extentLayer.itemText(index) raise NoLayerException(suffix=layer_name) geom_extent = QgsGeometry.fromRect(geom_extent) epsg_4326 = QgsCoordinateReferenceSystem('EPSG:4326') crs_transform = QgsCoordinateTransform(source_crs, epsg_4326) geom_extent.transform(crs_transform) return geom_extent.boundingBox()
def testGeopackageExtentUpdate(self): """ test http://hub.qgis.org/issues/15273 """ tmpfile = os.path.join(self.basetestpath, "testGeopackageExtentUpdate.gpkg") ds = ogr.GetDriverByName("GPKG").CreateDataSource(tmpfile) lyr = ds.CreateLayer("test", geom_type=ogr.wkbPoint) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(0 0)")) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 1)")) lyr.CreateFeature(f) f = None f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT(1 0.5)")) lyr.CreateFeature(f) f = None gdal.ErrorReset() ds.ExecuteSQL("RECOMPUTE EXTENT ON test") has_error = gdal.GetLastErrorMsg() != "" ds = None if has_error: print("Too old GDAL trunk version. Please update") return vl = QgsVectorLayer(u"{}".format(tmpfile), u"test", u"ogr") # Test moving a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.changeGeometry(1, QgsGeometry.fromWkt("Point (0.5 0)"))) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 1.0)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0], ) # Test deleting a geometry that touches the bbox self.assertTrue(vl.startEditing()) self.assertTrue(vl.deleteFeature(2)) self.assertTrue(vl.commitChanges()) reference = QgsGeometry.fromRect(QgsRectangle(0.5, 0.0, 1.0, 0.5)) provider_extent = QgsGeometry.fromRect(vl.extent()) self.assertTrue( QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001), provider_extent.asPolygon()[0], )
def processAlgorithm(self, parameters, context, feedback): layer = self.parameterAsLayer(parameters, self.INPUT, context) fields = QgsFields() fields.append(QgsField('MINX', QVariant.Double)) fields.append(QgsField('MINY', QVariant.Double)) fields.append(QgsField('MAXX', QVariant.Double)) fields.append(QgsField('MAXY', QVariant.Double)) fields.append(QgsField('CNTX', QVariant.Double)) fields.append(QgsField('CNTY', QVariant.Double)) fields.append(QgsField('AREA', QVariant.Double)) fields.append(QgsField('PERIM', QVariant.Double)) fields.append(QgsField('HEIGHT', QVariant.Double)) fields.append(QgsField('WIDTH', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, layer.crs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) try: # may not be possible layer.updateExtents() except: pass rect = layer.extent() geometry = QgsGeometry.fromRect(rect) minx = rect.xMinimum() miny = rect.yMinimum() maxx = rect.xMaximum() maxy = rect.yMaximum() height = rect.height() width = rect.width() cntx = minx + width / 2.0 cnty = miny + height / 2.0 area = width * height perim = 2 * width + 2 * height feat = QgsFeature() feat.setGeometry(geometry) attrs = [ minx, miny, maxx, maxy, cntx, cnty, area, perim, height, width, ] feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def _outputGeometryFromGridId(self, grid_id): [lat, lon] = grid_to_latlon(int(grid_id)) return QgsGeometry.fromRect( QgsRectangle( lon - DEFAULT_HALF_GRID_SIZE, lat - DEFAULT_HALF_GRID_SIZE, lon + DEFAULT_HALF_GRID_SIZE, lat + DEFAULT_HALF_GRID_SIZE, ) )
def onClickedHighlight(self): def removeRB(): rb.reset( True ) self.qgisCanvas.scene().removeItem( rb ) rb = QgsRubberBand( self.qgisCanvas, QGis.Polygon) rb.setBorderColor( QColor( 255, 0, 0 ) ) rb.setWidth( 2 ) rb.setToGeometry( QgsGeometry.fromRect( self.canvas.extent() ), None ) QTimer.singleShot( 2000, removeRB )
def _extentsToLayer(self): """Memory layer for aggregation by using canvas extents as feature. We do this because the user elected to use no aggregation layer so we make a 'dummy' one which covers the whole study area extent. This layer is needed when postprocessing because we always want a vector layer to store aggregation information in. Returns: QgsMapLayer - a memory layer representing the extents of the clip. """ # Note: this code duplicates from Dock.viewportGeoArray - make DRY. TS myRect = self.iface.mapCanvas().extent() myCrs = QgsCoordinateReferenceSystem() myCrs.createFromEpsg(4326) myGeoExtent = extent_to_geo_array(myRect, myCrs) if not self.layer.isValid(): myMessage = self.tr( 'An exception occurred when creating the entire area layer.') raise (Exception(myMessage)) myProvider = self.layer.dataProvider() myAttrName = self.tr('Area') myProvider.addAttributes( [QgsField(myAttrName, QtCore.QVariant.String)]) self.layer.startEditing() # add a feature the size of the impact layer bounding box myFeature = QgsFeature() # noinspection PyCallByClass,PyTypeChecker,PyArgumentList myFeature.setGeometry(QgsGeometry.fromRect( QgsRectangle( QgsPoint(myGeoExtent[0], myGeoExtent[1]), QgsPoint(myGeoExtent[2], myGeoExtent[3])))) myFeature.setAttributeMap({0: QtCore.QVariant( self.tr('Entire area'))}) myProvider.addFeatures([myFeature]) self.layer.commitChanges() try: self.keywordIO.update_keywords( self.layer, {self.defaults['AGGR_ATTR_KEY']: myAttrName}) except InvalidParameterError: self.keywordIO.write_keywords( self.layer, {self.defaults['AGGR_ATTR_KEY']: myAttrName}) except KeywordDbError, e: raise e
def testWFSPolygons(self): """ Adds some polygons, then check and clear all """ layer_name = 'test_polygon' layer = self._getLayer(layer_name) wfs_layer = self._getWFSLayer(layer_name) feat1 = QgsFeature(wfs_layer.pendingFields()) feat1['id'] = 11 feat1.setGeometry(QgsGeometry.fromRect(QgsRectangle(QgsPoint(9, 45), QgsPoint(10, 46)))) feat2 = QgsFeature(wfs_layer.pendingFields()) feat2.setGeometry(QgsGeometry.fromRect(QgsRectangle(QgsPoint(9.5, 45.5), QgsPoint(10.5, 46.5)))) feat2['id'] = 12 old_features = [feat1, feat2] # Change feat1 new_feat1 = QgsFeature(wfs_layer.pendingFields()) new_feat1['id'] = 121 new_feat1.setGeometry(QgsGeometry.fromRect(QgsRectangle(QgsPoint(10, 46), QgsPoint(11.5, 47.5)))) new_features = [new_feat1, feat2] self._testLayer(wfs_layer, layer, old_features, new_features)
def processFeature(self, feature, feedback): input_geometry = feature.geometry() if input_geometry: output_geometry = QgsGeometry.fromRect(input_geometry.boundingBox()) if not output_geometry: raise QgsProcessingException( self.tr('Error calculating bounding box')) feature.setGeometry(output_geometry) return feature
def createFeature(self, feedback, feature_id, type, geometries, class_field=None): attrs = [feature_id] if class_field is not None: attrs.append(class_field) multi_point = QgsMultiPoint() for g in geometries: if feedback.isCanceled(): break vid = QgsVertexId() while True: if feedback.isCanceled(): break found, point = g.constGet().nextVertex(vid) if found: multi_point.addGeometry(point) else: break geometry = QgsGeometry(multi_point) output_geometry = None if type == 0: # envelope rect = geometry.boundingBox() output_geometry = QgsGeometry.fromRect(rect) attrs.append(rect.width()) attrs.append(rect.height()) attrs.append(rect.area()) attrs.append(rect.perimeter()) elif type == 1: # oriented rect output_geometry, area, angle, width, height = geometry.orientedMinimumBoundingBox() attrs.append(width) attrs.append(height) attrs.append(angle) attrs.append(area) attrs.append(2 * width + 2 * height) elif type == 2: # circle output_geometry, center, radius = geometry.minimalEnclosingCircle(segments=72) attrs.append(radius) attrs.append(math.pi * radius * radius) elif type == 3: # convex hull output_geometry = geometry.convexHull() attrs.append(output_geometry.constGet().area()) attrs.append(output_geometry.constGet().perimeter()) f = QgsFeature() f.setAttributes(attrs) f.setGeometry(output_geometry) return f
def doSearch(self): self.completion.preventSuggest() o = self.completion.selectedObject #print o if not o: return # Create a QGIS geom to represent object geom = None if 'geometryWkt' in o: wkt = o['geometryWkt'] # Fix invalid wkt if wkt.startswith('BOX'): wkt = 'LINESTRING' + wkt[3:] geom = QgsGeometry.fromRect( QgsGeometry.fromWkt(wkt).boundingBox() ) else: geom = QgsGeometry.fromWkt(wkt) elif 'xMin' in o: geom = QgsGeometry.fromRect( QgsRectangle(o['xMin'], o['yMin'], o['xMax'], o['yMax']) ) else: geom = QgsGeometry.fromPointXY(QgsPointXY(o['x'], o['y'])) # Zoom to feature bufgeom = geom.buffer(200.0, 2) bufgeom.transform(self.crsTransform) rect = bufgeom.boundingBox() mc = self.qgisIface.mapCanvas() mc.setExtent(rect) # Mark the spot geom.transform(self.crsTransform) self.setMarkerGeom(geom) mc.refresh()
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) extent = self.getParameterValue(self.TARGET_AREA).split(',') if not extent: extent = QgsProcessingUtils.combineLayerExtents([layer]) target_crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.TARGET_AREA_CRS)) target_geom = QgsGeometry.fromRect(QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3]))) output_file = self.getOutputValue(self.OUTPUT_HTML_FILE) # make intersection tests nice and fast engine = QgsGeometry.createGeometryEngine(target_geom.geometry()) engine.prepareGeometry() layer_bounds = QgsGeometry.fromRect(layer.extent()) results = [] for srs_id in QgsCoordinateReferenceSystem.validSrsIds(): candidate_crs = QgsCoordinateReferenceSystem.fromSrsId(srs_id) if not candidate_crs.isValid(): continue transform_candidate = QgsCoordinateTransform(candidate_crs, target_crs) transformed_bounds = QgsGeometry(layer_bounds) try: if not transformed_bounds.transform(transform_candidate) == 0: continue except: continue if engine.intersects(transformed_bounds.geometry()): results.append(candidate_crs.authid()) self.createHTML(output_file, results)
def spatialValue (self): try: if self._spatialValue == None: raise ValueError if self.spatialOperand == "gml:Point": return QgsGeometry.fromPoint(self._spatialValue).exportToWkt() elif self.spatialOperand == "gml:Envelope": return QgsGeometry.fromRect(self._spatialValue).exportToWkt() elif self.spatialOperand == "gml:Polygon": return QgsGeometry.fromPolygon(self._spatialValue).exportToWkt() elif self.spatialOperand == "gml:LineString": return QgsGeometry.fromPolyline(self._spatialValue).exportToWkt() else: raise ValueError except: return ""
def testDeferredUpdate(self): """ test that map canvas doesn't auto refresh on deferred layer update """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() while canvas.isDrawing(): app.processEvents() self.assertTrue(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas)) # add polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # deferred update - so expect that canvas will not been refreshed layer.triggerRepaint(True) timeout = time.time() + 0.1 while time.time() < timeout: # messy, but only way to check that canvas redraw doesn't occur self.assertFalse(canvas.isDrawing()) # canvas should still be empty self.assertTrue(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas)) # refresh canvas canvas.refresh() while not canvas.isDrawing(): app.processEvents() while canvas.isDrawing(): app.processEvents() # now we expect the canvas check to fail (since they'll be a new polygon rendered over it) self.assertFalse(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas))
def reprojectSearchArea(self, layer, geom): """ Reprojects search area if necessary, according to what is being searched. :param layer: (QgsVectorLayer) layer which target rectangle has to have same SRC. :param geom: (QgsRectangle) rectangle representing search area. """ #geom always have canvas coordinates epsg = self.canvas.mapSettings().destinationCrs().authid() #getting srid from something like 'EPSG:31983' srid = layer.crs().authid() if epsg == srid: return geom crsSrc = QgsCoordinateReferenceSystem(epsg) crsDest = QgsCoordinateReferenceSystem(srid) # Creating a transformer coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest) # here we have to put authid, not srid auxGeom = QgsGeometry.fromRect(geom) auxGeom.transform(coordinateTransformer) return auxGeom.boundingBox()
def accept(self): """User accepted the rectangle.""" mode = None if self.hazard_exposure_view_extent.isChecked(): mode = HAZARD_EXPOSURE_VIEW elif self.hazard_exposure_only.isChecked(): mode = HAZARD_EXPOSURE elif self.hazard_exposure_bookmark.isChecked(): mode = HAZARD_EXPOSURE_BOOKMARK elif self.hazard_exposure_user_extent.isChecked(): mode = HAZARD_EXPOSURE_BOUNDINGBOX settings = QSettings() settings.setValue('inasafe/analysis_extents_mode', mode) self.canvas.unsetMapTool(self.tool) if self.previous_map_tool != self.tool: self.canvas.setMapTool(self.previous_map_tool) if self.tool.rectangle() is not None: self.extent_defined.emit( self.tool.rectangle(), self.canvas.mapSettings().destinationCrs()) extent = QgsGeometry.fromRect(self.tool.rectangle()) LOGGER.info( 'Requested extent : {wkt}'.format(wkt=extent.exportToWkt())) else: self.clear_extent.emit() # State handlers for showing warning message bars settings.setValue( 'inasafe/show_extent_warnings', self.show_warnings.isChecked()) settings.setValue( 'inasafe/show_extent_confirmations', self.show_confirmations.isChecked()) self.tool.reset() self.extent_selector_closed.emit() super(ExtentSelectorDialog, self).accept()
def do_operation(self): """ perform footprint load operation """ grid_layer = self.inputs[0].value # make sure input is correct # NOTE: these checks cannot be performed at set input time # because the data layer maybe is not loaded yet self._test_layer_loaded(grid_layer) grid_fields = grid_layer.dataProvider().fields() output_layername = 'grid_%s' % get_unique_filename() output_file = self._tmp_dir + output_layername + '.shp' half_grid = DEFAULT_GRID_SIZE / 2.0 try: writer = QgsVectorFileWriter(output_file, "utf-8", grid_fields, QGis.WKBPolygon, grid_layer.crs(), "ESRI Shapefile") out_f = QgsFeature() for in_f in layer_features(grid_layer): in_point = in_f.geometry().asPoint() out_geom = QgsGeometry.fromRect(QgsRectangle(in_point.x()-half_grid, in_point.y()-half_grid, in_point.x()+half_grid, in_point.y()+half_grid)) out_f.setGeometry(out_geom) out_f.setAttributeMap(in_f.attributeMap()) writer.addFeature(out_f) del writer except Exception as err: logAPICall.log(str(err), logAPICall.ERROR) raise OperatorError('error writing out grid: %s' % err, self.__class__) # load shapefile as layer output_layer = load_shapefile(output_file, output_layername) if not output_layer: raise OperatorError('Error loading generated file %s' % (output_file), self.__class__) # store data in output self.outputs[0].value = output_layer self.outputs[1].value = output_file
def showItem(self, item): ogrFeature = item.data(Qt.UserRole) geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt()) if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint): self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PointGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(2) self.rubber.setToGeometry(geom, None) else: # dont show if it is larger than the canvas if self.plugin.canvas.extent().contains(geom.boundingBox()): pass else: geom = geom.intersection(QgsGeometry.fromRect(self.plugin.canvas.extent())) self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PolygonGeometry) self.rubber.setColor(QColor(50, 50, 255, 100)) self.rubber.setWidth(4) self.rubber.setToGeometry(geom, None)
def processAlgorithm(self, progress): progress.setInfo("Preparing the Overpass query") progress.setPercentage(0) server = self.getParameterValue(self.SERVER) query = self.getParameterValue(self.QUERY_STRING) nominatim = self.getParameterValue(self.NOMINATIM) # Extent of the layer extent = self.getParameterValue(self.EXTENT) if extent != "0,0,0,0": # x_min, x_max, y_min, y_max extent = [float(i) for i in extent.split(',')] # noinspection PyCallByClass geometry_extent = QgsGeometry.fromRect( QgsRectangle(extent[0], extent[2], extent[1], extent[3])) source_crs = iface.mapCanvas().mapRenderer().destinationCrs() crs_transform = QgsCoordinateTransform( source_crs, QgsCoordinateReferenceSystem("EPSG:4326")) geometry_extent.transform(crs_transform) extent = geometry_extent.boundingBox() else: extent = None if nominatim == "": nominatim = None # Make some transformation on the query ({{box}}, Nominatim, ... query = prepare_query(query, extent, nominatim) overpass_api = ConnexionOAPI(url=server, output="xml") progress.setInfo("Downloading data from Overpass") progress.setPercentage(5) osm_file = overpass_api.get_file_from_query(query) # Set the output file for Processing progress.setPercentage(100) self.setOutputValue(self.OUTPUT_FILE, osm_file)
def testMapTheme(self): canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") # add a polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # create a style sym1 = QgsFillSymbol.createSimple({'color': '#ffb200'}) renderer = QgsSingleSymbolRenderer(sym1) layer.setRenderer(renderer) canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # add some styles layer.styleManager().addStyleFromLayer('style1') sym2 = QgsFillSymbol.createSimple({'color': '#00b2ff'}) renderer2 = QgsSingleSymbolRenderer(sym2) layer.setRenderer(renderer2) layer.styleManager().addStyleFromLayer('style2') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme2', 'theme2', canvas)) layer.styleManager().setCurrentStyle('style1') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # OK, so all good with setting/rendering map styles # try setting canvas to a particular theme # make some themes... theme1 = QgsMapThemeCollection.MapThemeRecord() record1 = QgsMapThemeCollection.MapThemeLayerRecord(layer) record1.currentStyle = 'style1' record1.usingCurrentStyle = True theme1.setLayerRecords([record1]) theme2 = QgsMapThemeCollection.MapThemeRecord() record2 = QgsMapThemeCollection.MapThemeLayerRecord(layer) record2.currentStyle = 'style2' record2.usingCurrentStyle = True theme2.setLayerRecords([record2]) QgsProject.instance().mapThemeCollection().insert('theme1', theme1) QgsProject.instance().mapThemeCollection().insert('theme2', theme2) canvas.setTheme('theme2') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme2', 'theme2', canvas)) canvas.setTheme('theme1') canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # add another layer layer2 = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer2", "memory") f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer2.dataProvider().addFeatures([f])) # create a style sym1 = QgsFillSymbol.createSimple({'color': '#b2ff00'}) renderer = QgsSingleSymbolRenderer(sym1) layer2.setRenderer(renderer) # rerender canvas - should NOT show new layer canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # test again - this time refresh all layers canvas.refreshAllLayers() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas)) # add layer 2 to theme1 record3 = QgsMapThemeCollection.MapThemeLayerRecord(layer2) theme1.setLayerRecords([record3]) QgsProject.instance().mapThemeCollection().update('theme1', theme1) canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme3', 'theme3', canvas)) # change the appearance of an active style layer2.styleManager().addStyleFromLayer('original') layer2.styleManager().addStyleFromLayer('style4') record3.currentStyle = 'style4' record3.usingCurrentStyle = True theme1.setLayerRecords([record3]) QgsProject.instance().mapThemeCollection().update('theme1', theme1) canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme3', 'theme3', canvas)) layer2.styleManager().setCurrentStyle('style4') sym3 = QgsFillSymbol.createSimple({'color': '#b200b2'}) layer2.renderer().setSymbol(sym3) canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) # try setting layers while a theme is in place canvas.setLayers([layer]) canvas.refresh() # should be no change... setLayers should be ignored if canvas is following a theme! canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) # setLayerStyleOverrides while theme is in place canvas.setLayerStyleOverrides({layer2.id(): 'original'}) # should be no change... setLayerStyleOverrides should be ignored if canvas is following a theme! canvas.refresh() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) # clear theme canvas.setTheme('') canvas.refresh() canvas.waitWhileRendering() # should be different - we should now render project layers self.assertFalse(self.canvasImageCheck('theme4', 'theme4', canvas))
def btn_calculate(self): # Note that the super class has several tests in it - if they fail it # returns False, which would mean this function should stop execution # as well. ret = super(DlgCalculateSDISummaryTableAdmin, self).btn_calculate() if not ret: return ###################################################################### # Check that all needed input layers are selected if len(self.combo_layer_sqi.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a Soil Quality Indicator layer to your map before you can use the SDI calculation tool." )) return if len(self.combo_layer_vqi.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a Vegatation Quality Indicator layer to your map before you can use the SDI calculation tool." )) return if len(self.combo_layer_cqi.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a Climate Quality Indicator layer to your map before you can use the SDI calculation tool." )) return if len(self.combo_layer_mqi.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a Management Quality Indicator layer to your map before you can use the SDI calculation tool." )) return ####################################################################### # Check that the layers cover the full extent needed if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_sqi.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the Soil Quality Indicator layer." )) return if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_vqi.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the Vegetation Quality Indicator layer." )) return if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_cqi.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the Climate Quality Indicator layer." )) return if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_mqi.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the Management Quality Indicator layer." )) return out_f = self.get_save_raster() if not out_f: return self.close() crosses_180th, geojsons = self.aoi.bounding_box_gee_geojson() val = [] n = 1 if self.area_tab.area_fromfile.isChecked(): for f in self.aoi.get_layer_wgs84().getFeatures(): # Get an OGR geometry from the QGIS geometry geom = f.geometry() val.append(geom) n += 1 # stringify json object val_string = '{}'.format(json.loads(val[0].asJson())) # create ogr geometry val_geom = ogr.CreateGeometryFromJson(val_string) # simplify polygon to tolerance of 0.003 val_geom_simplified = val_geom.Simplify(0.003) # fetch coordinates from json coords = json.loads( val_geom_simplified.ExportToJson())['coordinates'] geometries = {"coordinates": coords, "type": "Polygon"} elif self.area_tab.area_fromadmin.isChecked(): geometries = { "coordinates": self.get_admin_poly_geojson()['geometry']['coordinates'][0], "type": "Polygon" } elif self.area_tab.area_frompoint.isChecked(): point = QgsPointXY( float(self.area_tab.area_frompoint_point_x.text()), float(self.area_tab.area_frompoint_point_y.text())) crs_src = QgsCoordinateReferenceSystem( self.area_tab.canvas.mapSettings().destinationCrs().authid()) point = QgsCoordinateTransform( crs_src, self.aoi.crs_dst, QgsProject.instance()).transform(point) geometries = json.loads(QgsGeometry.fromPointXY(point).asJson()) # write aoi geometry to file for masking output aoi_geom = { "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": {}, "geometry": geometries }] } aoi_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'aoi.geojson') with open(aoi_file, 'w') as filetowrite: filetowrite.write(json.dumps(aoi_geom)) ####################################################################### # Load all datasets to VRTs (to select only the needed bands) sqi_vrt = self.combo_layer_sqi.get_vrt() vqi_vrt = self.combo_layer_vqi.get_vrt() cqi_vrt = self.combo_layer_cqi.get_vrt() mqi_vrt = self.combo_layer_mqi.get_vrt() # Add the custom layers to a VRT in case they don't match in resolution, # and set proper output bounds in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name gdal.BuildVRT( in_vrt, [sqi_vrt, vqi_vrt, cqi_vrt, mqi_vrt], resolution='highest', resampleAlg=gdal.GRA_NearestNeighbour, outputBounds=self.aoi.get_aligned_output_bounds_deprecated( sqi_vrt), separate=True) lc_change_worker = StartWorker( SDIWorker, 'calculating Sensitivity Desertification index', in_vrt, out_f) if not lc_change_worker.success: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self.tr( "Error calculating Sensitivity Desertification index."), None) return band_info = [ BandInfo("Sensitivity Desertification Index", add_to_map=True) ] out_json = os.path.splitext(out_f)[0] + '.json' create_local_json_metadata(out_json, out_f, band_info) schema = BandInfoSchema() for band_number in range(len(band_info)): b = schema.dump(band_info[band_number]) if b['add_to_map']: # The +1 is because band numbers start at 1, not zero add_layer(out_f, band_number + 1, b)
def calculate_zonal_stats(raster_layer, polygon_layer): """Calculate zonal statics given two layers. :param raster_layer: A QGIS raster layer. :type raster_layer: QgsRasterLayer, QgsMapLayer :param polygon_layer: A QGIS vector layer containing polygons. :type polygon_layer: QgsVectorLayer, QgsMapLayer :returns: A data structure containing sum, mean, min, max, count of raster values for each polygonal area. :rtype: dict :raises: InvalidParameterError, InvalidGeometryError Note: * InvalidParameterError if incorrect inputs are received. * InvalidGeometryError if none geometry is found during calculations. * Any other exceptions are propagated. Example of output data structure: { 1: {'sum': 10, 'count': 20, 'min': 1, 'max': 4, 'mean': 2}, 2: {'sum': 10, 'count': 20, 'min': 1, 'max': 4, 'mean': 2}, 3 {'sum': 10, 'count': 20, 'min': 1, 'max': 4, 'mean': 2}} The key in the outer dict is the feature id .. note:: This is a python port of the zonal stats implementation in QGIS . See https://github.com/qgis/Quantum-GIS/blob/master/src/analysis/ vector/qgszonalstatistics.cpp .. note:: Currently not projection checks are made to ensure that both layers are in the same CRS - we assume they are. """ if not is_polygon_layer(polygon_layer): raise InvalidParameterError( tr('Zonal stats needs a polygon layer in order to compute ' 'statistics.')) if not is_raster_layer(raster_layer): raise InvalidParameterError( tr('Zonal stats needs a raster layer in order to compute statistics.' )) LOGGER.debug('Calculating zonal stats for:') LOGGER.debug('Raster: %s' % raster_layer.source()) LOGGER.debug('Vector: %s' % polygon_layer.source()) results = {} raster_source = raster_layer.source() feature_id = gdal.Open(str(raster_source), gdal.GA_ReadOnly) geo_transform = feature_id.GetGeoTransform() columns = feature_id.RasterXSize rows = feature_id.RasterYSize # Get first band. band = feature_id.GetRasterBand(1) no_data = band.GetNoDataValue() # print 'No data %s' % no_data cell_size_x = geo_transform[1] if cell_size_x < 0: cell_size_x = -cell_size_x cell_size_y = geo_transform[5] if cell_size_y < 0: cell_size_y = -cell_size_y raster_box = QgsRectangle(geo_transform[0], geo_transform[3] - (cell_size_y * rows), geo_transform[0] + (cell_size_x * columns), geo_transform[3]) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList raster_geometry = QgsGeometry.fromRect(raster_box) # Get vector layer provider = polygon_layer.dataProvider() if provider is None: message = tr('Could not obtain data provider from layer "%s"') % ( polygon_layer.source()) raise Exception(message) request = QgsFeatureRequest() crs = osr.SpatialReference() crs.ImportFromProj4(str(polygon_layer.crs().toProj4())) count = 0 for myFeature in provider.getFeatures(request): geometry = myFeature.geometry() if geometry is None: message = tr('Feature %d has no geometry or geometry is invalid' ) % (myFeature.id()) raise InvalidGeometryError(message) count += 1 feature_box = geometry.boundingBox().intersect(raster_box) print 'NEW AGGR: %s' % myFeature.id() # print 'Raster Box: %s' % raster_box.asWktCoordinates() # print 'Feature Box: %s' % feature_box.asWktCoordinates() offset_x, offset_y, cells_x, cells_y = intersection_box( raster_box, feature_box, cell_size_x, cell_size_y) # If the poly does not intersect the raster just continue if None in [offset_x, offset_y, cells_x, cells_y]: continue # avoid access to cells outside of the raster (may occur because of # rounding) if (offset_x + cells_x) > columns: offset_x = columns - offset_x if (offset_y + cells_y) > rows: cells_y = rows - offset_y intersected_geometry = raster_geometry.intersection(geometry) geometry_sum, count = numpy_stats(band, intersected_geometry, geo_transform, no_data, crs) if count <= 1: # The cell resolution is probably larger than the polygon area. # We switch to precise pixel - polygon intersection in this case geometry_sum, count = precise_stats(band, geometry, offset_x, offset_y, cells_x, cells_y, cell_size_x, cell_size_y, raster_box, no_data) # print geometry_sum, count if count == 0: mean = 0 else: mean = geometry_sum / count results[myFeature.id()] = { 'sum': geometry_sum, 'count': count, 'mean': mean } # noinspection PyUnusedLocal feature_id = None # Close return results
def statisticsFromPreciseIntersection(theBand, theGeometry, thePixelOffsetX, thePixelOffsetY, theCellsX, theCellsY, theCellSizeX, theCellSizeY, theRasterBox, theNoData): """Weighted pixel sum for polygon based on only intersecting parts. :param theBand: A valid band from a raster layer. :type theBand: GDALRasterBand :param theGeometry: A valid polygon geometry. :type theGeometry: QgsGeometry :param thePixelOffsetX: Left offset for raster window. :type thePixelOffsetX: int :param thePixelOffsetY: Offset from bottom for raster window. :type thePixelOffsetY: int :param theCellsX: Width of the raster window. :type theCellsX: int :param theCellsY: Height of the raster window. :type theCellsY: int :param theCellSizeX: Size in the x direction of a single cell. :type theCellSizeX: float :param theCellSizeY: Size in the y direciton of a single cell. :type theCellSizeY: float :param theRasterBox: Box defining the extents of the raster. :type theRasterBox: QgsRectangle :param theNoData: Value for nodata in the raster. :type theNoData: int, float :returns: Sum, Count - sum of the values of all pixels and the count of pixels that intersect with the geometry. :rtype: (float, int) """ myCurrentY = (theRasterBox.yMaximum() - thePixelOffsetY * theCellSizeY - theCellSizeY / 2) myHalfCellSizeX = theCellSizeX / 2.0 myHalfCellsSizeY = theCellSizeY / 2.0 myPixelArea = theCellSizeX * theCellSizeY myCellsToReadX = theCellsX myCellsToReadY = 1 # read in a single row at a time myBufferXSize = 1 myBufferYSize = 1 myCount = 0 mySum = 0.0 for row in range(0, theCellsY): myCurrentX = (theRasterBox.xMinimum() + theCellSizeX / 2.0 + thePixelOffsetX * theCellSizeX) for col in range(0, theCellsX): # Read a single pixel myScanline = theBand.ReadRaster(thePixelOffsetX + col, thePixelOffsetY + row, myCellsToReadX, myCellsToReadY, myBufferXSize, myBufferYSize, gdal.GDT_Float32) # Note that the returned scanline is of type string, and contains # xsize*4 bytes of raw binary floating point data. This can be # converted to Python values using the struct module from the # standard library: #print myScanline if myScanline != '': myValues = struct.unpack('f', myScanline) # tuple returned myValue = myValues[0] else: continue if myValue == theNoData: #print 'myValue is nodata (%s)' % theNoData continue #print 'Value of cell in precise intersection: %s' % myValue # noinspection PyCallByClass,PyTypeChecker myPixelGeometry = QgsGeometry.fromRect( QgsRectangle(myCurrentX - myHalfCellSizeX, myCurrentY - myHalfCellsSizeY, myCurrentX + myHalfCellSizeX, myCurrentY + myHalfCellsSizeY)) if myPixelGeometry: myIntersectionGeometry = myPixelGeometry.intersection( theGeometry) if myIntersectionGeometry: myIntersectionArea = myIntersectionGeometry.area() #print 'Intersection Area: %s' % myIntersectionArea if myIntersectionArea >= 0.0: myWeight = myIntersectionArea / myPixelArea #print 'Weight: %s' % myWeight myCount += myWeight #print 'myCount: %s' % myCount mySum += myValue * myWeight #print 'myValue: %s' % myValue myCurrentX += theCellSizeY myCurrentY -= theCellsY return mySum, myCount
def btn_calculate(self): ###################################################################### # Check that all needed output files are selected if not self.output_file_table.text(): QtWidgets.QMessageBox.information( None, self.tr("Error"), self.tr("Choose an output file for the summary table.")) return # Note that the super class has several tests in it - if they fail it # returns False, which would mean this function should stop execution # as well. ret = super(DlgCalculateTCSummaryTable, self).btn_calculate() if not ret: return ###################################################################### # Check that all needed input layers are selected if len(self.combo_layer_f_loss.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a forest loss layer to your map before you can use the carbon change summary tool." )) return if len(self.combo_layer_tc.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a total carbon layer to your map before you can use the carbon change summary tool." )) return ####################################################################### # Check that the layers cover the full extent needed if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_f_loss.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the forest loss layer." )) return if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_tc.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the total carbon layer." )) return ####################################################################### # Check that all of the productivity layers have the same resolution # and CRS def res(layer): return (round(layer.rasterUnitsPerPixelX(), 10), round(layer.rasterUnitsPerPixelY(), 10)) if res(self.combo_layer_f_loss.get_layer()) != res( self.combo_layer_tc.get_layer()): QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Resolutions of forest loss and total carbon layers do not match." )) return self.close() # Load all datasets to VRTs (to select only the needed bands) f_loss_vrt = self.combo_layer_f_loss.get_vrt() tc_vrt = self.combo_layer_tc.get_vrt() # Figure out start and end dates year_start = self.combo_layer_f_loss.get_band_info( )['metadata']['year_start'] year_end = self.combo_layer_f_loss.get_band_info( )['metadata']['year_end'] summary_task = SummaryTask(self.aoi, year_start, year_end, f_loss_vrt, tc_vrt, self.output_file_table.text()) log("Adding task to task manager") QgsApplication.taskManager().addTask(summary_task) if summary_task.status() not in [QgsTask.Complete, QgsTask.Terminated]: QCoreApplication.processEvents() # while QgsApplication.taskManager().countActiveTasks() > 0: # QCoreApplication.processEvents() return True
def calculatePolygonProfile(self, geometry, crs, model, library): self.model = model self.library = library self.removeClosedLayers(model) if geometry is None or geometry.isEmpty(): return PlottingTool().clearData(self.dockwidget, model, library) self.profiles = [] #creating the plots of profiles for i in range(0 , model.rowCount()): self.profiles.append( {"layer": model.item(i,3).data(Qt.EditRole) } ) self.profiles[i]["l"] = [] for statistic in self.getPolygonProfileStatNames(): self.profiles[i][statistic] = [] # Get intersection between polygon geometry and raster following ZonalStatistics code rasterDS = gdal.Open(self.profiles[i]["layer"].source(), gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() cellXSize = abs(geoTransform[1]) cellYSize = abs(geoTransform[5]) rasterXSize = rasterDS.RasterXSize rasterYSize = rasterDS.RasterYSize rasterBBox = QgsRectangle(geoTransform[0], geoTransform[3] - cellYSize * rasterYSize, geoTransform[0] + cellXSize * rasterXSize, geoTransform[3]) rasterGeom = QgsGeometry.fromRect(rasterBBox) memVectorDriver = ogr.GetDriverByName('Memory') memRasterDriver = gdal.GetDriverByName('MEM') intersectedGeom = rasterGeom.intersection(geometry) ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.asWkt()) bbox = intersectedGeom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startColumn, startRow) = self.mapToPixel(xMin, yMax, geoTransform) (endColumn, endRow) = self.mapToPixel(xMax, yMin, geoTransform) width = endColumn - startColumn height = endRow - startRow if width == 0 or height == 0: return srcOffset = (startColumn, startRow, width, height) newGeoTransform = ( geoTransform[0] + srcOffset[0] * geoTransform[1], geoTransform[1], 0.0, geoTransform[3] + srcOffset[1] * geoTransform[5], 0.0, geoTransform[5], ) # Create a temporary vector layer in memory memVDS = memVectorDriver.CreateDataSource('out') memLayer = memVDS.CreateLayer('poly', crs, ogr.wkbPolygon) ft = ogr.Feature(memLayer.GetLayerDefn()) ft.SetGeometry(ogrGeom) memLayer.CreateFeature(ft) ft.Destroy() # Rasterize it rasterizedDS = memRasterDriver.Create('', srcOffset[2], srcOffset[3], 1, gdal.GDT_Byte) rasterizedDS.SetGeoTransform(newGeoTransform) gdal.RasterizeLayer(rasterizedDS, [1], memLayer, burn_values=[1]) rasterizedArray = rasterizedDS.ReadAsArray() for bandNumber in range(1, rasterDS.RasterCount+1): rasterBand = rasterDS.GetRasterBand(bandNumber) noData = rasterBand.GetNoDataValue() if noData is None: noData = np.nan scale = rasterBand.GetScale() if scale is None: scale = 1.0 offset = rasterBand.GetOffset() if offset is None: offset = 0.0 srcArray = rasterBand.ReadAsArray(*srcOffset) srcArray = srcArray*scale+offset masked = np.ma.MaskedArray(srcArray, mask=np.logical_or.reduce(( srcArray == noData, np.logical_not(rasterizedArray), np.isnan(srcArray)))) self.profiles[i]["l"].append(bandNumber) self.profiles[i]["count"].append(float(masked.count())) self.profiles[i]["max"].append(float(masked.max())) self.profiles[i]["mean"].append(float(masked.mean())) self.profiles[i]["median"].append(float(np.ma.median(masked))) self.profiles[i]["min"].append(float(masked.min())) self.profiles[i]["range"].append(float(masked.max()) - float(masked.min())) self.profiles[i]["std"].append(float(masked.std())) self.profiles[i]["sum"].append(float(masked.sum())) self.profiles[i]["unique"].append(np.unique(masked.compressed()).size) self.profiles[i]["var"].append(float(masked.var())) memVDS = None rasterizedDS = None rasterDS = None self.setXAxisSteps() PlottingTool().attachCurves(self.dockwidget, self.profiles, model, library) if self.dockwidget.cboAutoScale.isChecked(): PlottingTool().reScalePlot(self.dockwidget, self.profiles, model, library) self.setupTableTab(model)
def run(self): """Risk plugin for classified polygon hazard on land cover. Counts area of land cover types exposed to hazard zones. :returns: Impact layer :rtype: Vector """ # Identify hazard and exposure layers hazard = self.hazard.layer exposure = self.exposure.layer type_attr = self.exposure.keyword('field') self.hazard_class_attribute = self.hazard.keyword('field') hazard_value_to_class = {} self.hazard_class_mapping = self.hazard.keyword('value_map') for key, values in self.hazard_class_mapping.items(): for value in values: hazard_value_to_class[value] = self.hazard_columns[key] # prepare objects for re-projection of geometries crs_wgs84 = QgsCoordinateReferenceSystem('EPSG:4326') hazard_to_exposure = QgsCoordinateTransform(hazard.crs(), exposure.crs()) wgs84_to_hazard = QgsCoordinateTransform(crs_wgs84, hazard.crs()) wgs84_to_exposure = QgsCoordinateTransform(crs_wgs84, exposure.crs()) extent = QgsRectangle(self.requested_extent[0], self.requested_extent[1], self.requested_extent[2], self.requested_extent[3]) extent_hazard = wgs84_to_hazard.transformBoundingBox(extent) extent_exposure = wgs84_to_exposure.transformBoundingBox(extent) extent_exposure_geom = QgsGeometry.fromRect(extent_exposure) # make spatial index of hazard hazard_index = QgsSpatialIndex() hazard_features = {} for f in hazard.getFeatures(QgsFeatureRequest(extent_hazard)): f.geometry().transform(hazard_to_exposure) hazard_index.insertFeature(f) hazard_features[f.id()] = QgsFeature(f) # create impact layer filename = unique_filename(suffix='.shp') impact_fields = exposure.dataProvider().fields() impact_fields.append(QgsField(self.target_field, QVariant.String)) writer = QgsVectorFileWriter(filename, 'utf-8', impact_fields, QGis.WKBPolygon, exposure.crs()) # Iterate over all exposure polygons and calculate the impact. _calculate_landcover_impact(exposure, extent_exposure, extent_exposure_geom, self.hazard_class_attribute, hazard_features, hazard_index, hazard_value_to_class, impact_fields, writer) del writer impact_layer = QgsVectorLayer(filename, 'Impacted Land Cover', 'ogr') if impact_layer.featureCount() == 0: raise ZeroImpactException() zone_field = None if self.aggregator: zone_field = self.aggregator.exposure_aggregation_field # This is not the standard way to use mixins # Martin preferred to call it directly - normally it is called with # multiple inheritance. Thats ok but we need to monkey patch the # notes function as it is not overloaded by this class mixin = LandCoverReportMixin( question=self.question, impact_layer=impact_layer, target_field=self.target_field, ordered_columns=self.hazard_columns.values(), affected_columns=self.affected_hazard_columns, land_cover_field=type_attr, zone_field=zone_field) mixin.notes = self.notes impact_data = mixin.generate_data() # Define style for the impact layer style_classes = [ dict(label=self.hazard_columns['low'], value=self.hazard_columns['low'], colour='#acffb6', border_color='#000000', transparency=0, size=0.5), dict(label=self.hazard_columns['medium'], value=self.hazard_columns['medium'], colour='#ffe691', border_color='#000000', transparency=0, size=0.5), dict(label=self.hazard_columns['high'], value=self.hazard_columns['high'], colour='#F31A1C', border_color='#000000', transparency=0, size=0.5), ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') extra_keywords = { 'map_title': self.map_title(), 'target_field': self.target_field } impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector(data=impact_layer, name=self.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context) target_crs = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context) target_geom = QgsGeometry.fromRect(extent) fields = QgsFields() fields.append(QgsField('auth_id', QVariant.String, '', 20)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) # make intersection tests nice and fast engine = QgsGeometry.createGeometryEngine(target_geom.constGet()) engine.prepareGeometry() layer_bounds = QgsGeometry.fromRect(source.sourceExtent()) crses_to_check = QgsCoordinateReferenceSystem.validSrsIds() total = 100.0 / len(crses_to_check) found_results = 0 transform_context = QgsCoordinateTransformContext() for current, srs_id in enumerate(crses_to_check): if feedback.isCanceled(): break candidate_crs = QgsCoordinateReferenceSystem.fromSrsId(srs_id) if not candidate_crs.isValid(): continue transform_candidate = QgsCoordinateTransform( candidate_crs, target_crs, transform_context) transformed_bounds = QgsGeometry(layer_bounds) try: if not transformed_bounds.transform(transform_candidate) == 0: continue except: continue try: if engine.intersects(transformed_bounds.constGet()): feedback.pushInfo( self.tr('Found candidate CRS: {}').format( candidate_crs.authid())) f = QgsFeature(fields) f.setAttributes([candidate_crs.authid()]) sink.addFeature(f, QgsFeatureSink.FastInsert) found_results += 1 except: continue feedback.setProgress(int(current * total)) if found_results == 0: feedback.reportError(self.tr('No matching projections found')) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): """ Based on code by Matthew Perry https://gist.github.com/perrygeo/5667173 """ layer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT_VECTOR)) rasterPath = str(self.getParameterValue(self.INPUT_RASTER)) bandNumber = self.getParameterValue(self.RASTER_BAND) columnPrefix = self.getParameterValue(self.COLUMN_PREFIX) useGlobalExtent = self.getParameterValue(self.GLOBAL_EXTENT) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterBand = rasterDS.GetRasterBand(bandNumber) noData = rasterBand.GetNoDataValue() cellXSize = abs(geoTransform[1]) cellYSize = abs(geoTransform[5]) rasterXSize = rasterDS.RasterXSize rasterYSize = rasterDS.RasterYSize rasterBBox = QgsRectangle(geoTransform[0], geoTransform[3] - cellYSize * rasterYSize, geoTransform[0] + cellXSize * rasterXSize, geoTransform[3]) rasterGeom = QgsGeometry.fromRect(rasterBBox) crs = osr.SpatialReference() crs.ImportFromProj4(str(layer.crs().toProj4())) if useGlobalExtent: xMin = rasterBBox.xMinimum() xMax = rasterBBox.xMaximum() yMin = rasterBBox.yMinimum() yMax = rasterBBox.yMaximum() (startColumn, startRow) = mapToPixel(xMin, yMax, geoTransform) (endColumn, endRow) = mapToPixel(xMax, yMin, geoTransform) width = endColumn - startColumn height = endRow - startRow srcOffset = (startColumn, startRow, width, height) srcArray = rasterBand.ReadAsArray(*srcOffset) srcArray = srcArray * rasterBand.GetScale() + rasterBand.GetOffset( ) newGeoTransform = ( geoTransform[0] + srcOffset[0] * geoTransform[1], geoTransform[1], 0.0, geoTransform[3] + srcOffset[1] * geoTransform[5], 0.0, geoTransform[5], ) memVectorDriver = ogr.GetDriverByName('Memory') memRasterDriver = gdal.GetDriverByName('MEM') fields = layer.fields() (idxMin, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'min', 21, 6) (idxMax, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'max', 21, 6) (idxSum, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'sum', 21, 6) (idxCount, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'count', 21, 6) (idxMean, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'mean', 21, 6) (idxStd, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'std', 21, 6) (idxUnique, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'unique', 21, 6) (idxRange, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'range', 21, 6) (idxVar, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'var', 21, 6) (idxMedian, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'median', 21, 6) if hasSciPy: (idxMode, fields) = vector.findOrCreateField(layer, fields, columnPrefix + 'mode', 21, 6) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter( fields.toList(), layer.wkbType(), layer.crs()) outFeat = QgsFeature() outFeat.initAttributes(len(fields)) outFeat.setFields(fields) features = vector.features(layer) total = 100.0 / len(features) for current, f in enumerate(features): geom = f.geometry() intersectedGeom = rasterGeom.intersection(geom) ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.exportToWkt()) if not useGlobalExtent: bbox = intersectedGeom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startColumn, startRow) = mapToPixel(xMin, yMax, geoTransform) (endColumn, endRow) = mapToPixel(xMax, yMin, geoTransform) width = endColumn - startColumn height = endRow - startRow if width == 0 or height == 0: continue srcOffset = (startColumn, startRow, width, height) srcArray = rasterBand.ReadAsArray(*srcOffset) srcArray = srcArray * rasterBand.GetScale( ) + rasterBand.GetOffset() newGeoTransform = ( geoTransform[0] + srcOffset[0] * geoTransform[1], geoTransform[1], 0.0, geoTransform[3] + srcOffset[1] * geoTransform[5], 0.0, geoTransform[5], ) # Create a temporary vector layer in memory memVDS = memVectorDriver.CreateDataSource('out') memLayer = memVDS.CreateLayer('poly', crs, ogr.wkbPolygon) ft = ogr.Feature(memLayer.GetLayerDefn()) ft.SetGeometry(ogrGeom) memLayer.CreateFeature(ft) ft.Destroy() # Rasterize it rasterizedDS = memRasterDriver.Create('', srcOffset[2], srcOffset[3], 1, gdal.GDT_Byte) rasterizedDS.SetGeoTransform(newGeoTransform) gdal.RasterizeLayer(rasterizedDS, [1], memLayer, burn_values=[1]) rasterizedArray = rasterizedDS.ReadAsArray() srcArray = numpy.nan_to_num(srcArray) masked = numpy.ma.MaskedArray( srcArray, mask=numpy.logical_or(srcArray == noData, numpy.logical_not(rasterizedArray))) outFeat.setGeometry(geom) attrs = f.attributes() v = float(masked.min()) attrs.insert(idxMin, None if numpy.isnan(v) else v) v = float(masked.max()) attrs.insert(idxMax, None if numpy.isnan(v) else v) v = float(masked.sum()) attrs.insert(idxSum, None if numpy.isnan(v) else v) attrs.insert(idxCount, int(masked.count())) v = float(masked.mean()) attrs.insert(idxMean, None if numpy.isnan(v) else v) v = float(masked.std()) attrs.insert(idxStd, None if numpy.isnan(v) else v) attrs.insert(idxUnique, numpy.unique(masked.compressed()).size) v = float(masked.max()) - float(masked.min()) attrs.insert(idxRange, None if numpy.isnan(v) else v) v = float(masked.var()) attrs.insert(idxVar, None if numpy.isnan(v) else v) v = float(numpy.ma.median(masked)) attrs.insert(idxMedian, None if numpy.isnan(v) else v) if hasSciPy: attrs.insert(idxMode, float(mode(masked, axis=None)[0][0])) outFeat.setAttributes(attrs) writer.addFeature(outFeat) memVDS = None rasterizedDS = None progress.setPercentage(int(current * total)) rasterDS = None del writer
def processAlgorithm(self, parameters, context, feedback): spacing = self.parameterAsDouble(parameters, self.SPACING, context) inset = self.parameterAsDouble(parameters, self.INSET, context) randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context) isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context) crs = self.parameterAsCrs(parameters, self.CRS, context) extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, crs) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 id = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine(extent_geom.constGet()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if feedback.isCanceled(): break if randomize: geom = QgsGeometry(QgsPoint( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry(QgsPoint(x, y)) if extent_engine.intersects(geom.constGet()): f.setAttributes([id]) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) x += pSpacing id += 1 count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): rasterPath = self.getParameterValue(self.INPUT_DEM) layer = dataobjects.getObjectFromUri( self.getParameterValue(self.BOUNDARY_LAYER)) step = self.getParameterValue(self.STEP) percentage = self.getParameterValue(self.USE_PERCENTAGE) outputPath = self.getOutputValue(self.OUTPUT_DIRECTORY) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterBand = rasterDS.GetRasterBand(1) noData = rasterBand.GetNoDataValue() cellXSize = abs(geoTransform[1]) cellYSize = abs(geoTransform[5]) rasterXSize = rasterDS.RasterXSize rasterYSize = rasterDS.RasterYSize rasterBBox = QgsRectangle(geoTransform[0], geoTransform[3] - cellYSize * rasterYSize, geoTransform[0] + cellXSize * rasterXSize, geoTransform[3]) rasterGeom = QgsGeometry.fromRect(rasterBBox) crs = osr.SpatialReference() crs.ImportFromProj4(str(layer.crs().toProj4())) memVectorDriver = ogr.GetDriverByName('Memory') memRasterDriver = gdal.GetDriverByName('MEM') features = vector.features(layer) count = len(features) total = 100.0 / float(count) for count, f in enumerate(features): geom = f.geometry() intersectedGeom = rasterGeom.intersection(geom) if intersectedGeom.isGeosEmpty(): progress.setInfo( self.tr('Feature %d does not intersect raster or ' 'entirely located in NODATA area' % f.id())) continue fName = os.path.join( outputPath, 'hystogram_%s_%s.csv' % (layer.name(), f.id())) ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.exportToWkt()) bbox = intersectedGeom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startColumn, startRow) = raster.mapToPixel(xMin, yMax, geoTransform) (endColumn, endRow) = raster.mapToPixel(xMax, yMin, geoTransform) width = endColumn - startColumn height = endRow - startRow srcOffset = (startColumn, startRow, width, height) srcArray = rasterBand.ReadAsArray(*srcOffset) if srcOffset[2] == 0 or srcOffset[3] == 0: progress.setInfo( self.tr('Feature %d does is smaller than raster ' 'cell size' % f.id())) continue newGeoTransform = (geoTransform[0] + srcOffset[0] * geoTransform[1], geoTransform[1], 0.0, geoTransform[3] + srcOffset[1] * geoTransform[5], 0.0, geoTransform[5]) memVDS = memVectorDriver.CreateDataSource('out') memLayer = memVDS.CreateLayer('poly', crs, ogr.wkbPolygon) ft = ogr.Feature(memLayer.GetLayerDefn()) ft.SetGeometry(ogrGeom) memLayer.CreateFeature(ft) ft.Destroy() rasterizedDS = memRasterDriver.Create('', srcOffset[2], srcOffset[3], 1, gdal.GDT_Byte) rasterizedDS.SetGeoTransform(newGeoTransform) gdal.RasterizeLayer(rasterizedDS, [1], memLayer, burn_values=[1]) rasterizedArray = rasterizedDS.ReadAsArray() srcArray = numpy.nan_to_num(srcArray) masked = numpy.ma.MaskedArray( srcArray, mask=numpy.logical_or(srcArray == noData, numpy.logical_not(rasterizedArray))) self.calculateHypsometry(f.id(), fName, progress, masked, cellXSize, cellYSize, percentage, step) memVDS = None rasterizedDS = None progress.setPercentage(int(count * total)) rasterDS = None
def calculate_locally(self): trans_matrix = [[ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67 ], self.lc_define_deg_tab.trans_matrix_get()] # Remap the persistence classes so they are sequential, making them # easier to assign a clear color ramp in QGIS persistence_remap = [[ 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56, 57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77 ], [ 1, 12, 13, 14, 15, 16, 17, 21, 2, 23, 24, 25, 26, 27, 31, 32, 3, 34, 35, 36, 37, 41, 42, 43, 4, 45, 46, 47, 51, 52, 53, 54, 5, 56, 57, 61, 62, 63, 64, 65, 6, 67, 71, 72, 73, 74, 75, 76, 7 ]] if len(self.lc_setup_tab.use_custom_initial.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add an initial land cover layer to your map before you can run the calculation." ), None) return if len(self.lc_setup_tab.use_custom_final.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a final land cover layer to your map before you can run the calculation." ), None) return # Select the initial and final bands from initial and final datasets # (in case there is more than one lc band per dataset) lc_initial_vrt = self.lc_setup_tab.use_custom_initial.get_vrt() lc_final_vrt = self.lc_setup_tab.use_custom_final.get_vrt() year_baseline = self.lc_setup_tab.get_initial_year() year_target = self.lc_setup_tab.get_final_year() if int(year_baseline) >= int(year_target): QtWidgets.QMessageBox.information( None, self.tr("Warning"), self. tr('The initial year ({}) is greater than or equal to the target year ({}) - this analysis might generate strange results.' .format(year_baseline, year_target))) if self.aoi.calc_frac_overlap( QgsGeometry.fromRect(self.lc_setup_tab.use_custom_initial. get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the initial land cover layer." ), None) return if self.aoi.calc_frac_overlap( QgsGeometry.fromRect(self.lc_setup_tab.use_custom_initial. get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the final land cover layer." ), None) return out_f = self.get_save_raster() if not out_f: return self.close() # Add the lc layers to a VRT in case they don't match in resolution, # and set proper output bounds in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name gdal.BuildVRT( in_vrt, [lc_initial_vrt, lc_final_vrt], resolution='highest', resampleAlg=gdal.GRA_NearestNeighbour, outputBounds=self.aoi.get_aligned_output_bounds_deprecated( lc_initial_vrt), separate=True) lc_change_worker = StartWorker(LandCoverChangeWorker, 'calculating land cover change', in_vrt, out_f, trans_matrix, persistence_remap) if not lc_change_worker.success: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self.tr("Error calculating land cover change."), None) return band_info = [ BandInfo("Land cover (degradation)", add_to_map=True, metadata={ 'year_baseline': year_baseline, 'year_target': year_target }), BandInfo("Land cover (7 class)", metadata={'year': year_baseline}), BandInfo("Land cover (7 class)", metadata={'year': year_target}), BandInfo("Land cover transitions", add_to_map=True, metadata={ 'year_baseline': year_baseline, 'year_target': year_target }) ] out_json = os.path.splitext(out_f)[0] + '.json' create_local_json_metadata(out_json, out_f, band_info) schema = BandInfoSchema() for band_number in range(len(band_info)): b = schema.dump(band_info[band_number]) if b['add_to_map']: # The +1 is because band numbers start at 1, not zero add_layer(out_f, band_number + 1, b)
def testExtent(self): reference = QgsGeometry.fromRect( QgsRectangle(-71.123, 66.33, -65.32, 78.3)) provider_extent = QgsGeometry.fromRect(self.source.extent()) self.assertTrue(QgsGeometry.compare(provider_extent.asPolygon()[0], reference.asPolygon()[0], 0.00001))
def testRefreshOnTimer(self): """ test that map canvas refreshes with auto refreshing layers """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() self.assertTrue(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas)) # add polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # set auto refresh on layer layer.setAutoRefreshInterval(100) layer.setAutoRefreshEnabled(True) timeout = time.time() + 1 # expect canvas to auto refresh... while not canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) while canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) # add a polygon to layer f = QgsFeature() f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45))) self.assertTrue(layer.dataProvider().addFeatures([f])) # wait for canvas auto refresh while not canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) while canvas.isDrawing(): app.processEvents() self.assertTrue(time.time() < timeout) # now canvas should look different... self.assertFalse(self.canvasImageCheck('empty_canvas', 'empty_canvas', canvas)) # switch off auto refresh layer.setAutoRefreshEnabled(False) timeout = time.time() + 0.5 while time.time() < timeout: # messy, but only way to check that canvas redraw doesn't occur self.assertFalse(canvas.isDrawing())
def btn_calculate(self): ###################################################################### # Check that all needed output files are selected if not self.output_file_layer.text(): QtWidgets.QMessageBox.information( None, self.tr("Error"), self. tr("Choose an output file for the biomass difference layers.")) return if not self.output_file_table.text(): QtWidgets.QMessageBox.information( None, self.tr("Error"), self.tr("Choose an output file for the summary table.")) return # Note that the super class has several tests in it - if they fail it # returns False, which would mean this function should stop execution # as well. ret = super(DlgCalculateRestBiomassSummaryTable, self).btn_calculate() if not ret: return ###################################################################### # Check that all needed input layers are selected if len(self.combo_layer_biomass_diff.layer_list) == 0: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("You must add a biomass layer to your map before you can use the summary tool." )) return ####################################################################### # Check that the layers cover the full extent needed if self.aoi.calc_frac_overlap( QgsGeometry.fromRect( self.combo_layer_biomass_diff.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self. tr("Area of interest is not entirely within the biomass layer." )) return self.close() ####################################################################### # Prep files in_file = self.combo_layer_biomass_diff.get_data_file() # Remember the first value is an indication of whether dataset is # wrapped across 180th meridian wkts = self.aoi.meridian_split('layer', 'wkt', warn=False)[1] bbs = self.aoi.get_aligned_output_bounds(in_file) output_biomass_diff_tifs = [] output_biomass_diff_json = self.output_file_layer.text() for n in range(len(wkts)): if len(wkts) > 1: output_biomass_diff_tif = os.path.splitext( output_biomass_diff_json)[0] + '_{}.tif'.format(n) else: output_biomass_diff_tif = os.path.splitext( output_biomass_diff_json)[0] + '.tif' output_biomass_diff_tifs.append(output_biomass_diff_tif) log(u'Saving clipped biomass file to {}'.format( output_biomass_diff_tif)) geojson = json_geom_to_geojson( QgsGeometry.fromWkt(wkts[n]).asJson()) clip_worker = StartWorker( ClipWorker, 'masking layers (part {} of {})'.format(n + 1, len(wkts)), in_file, output_biomass_diff_tif, geojson, bbs[n]) if not clip_worker.success: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self.tr("Error masking input layers.")) return ###################################################################### # Calculate biomass change summary table log('Calculating summary table...') rest_summary_worker = StartWorker( RestBiomassSummaryWorker, 'calculating summary table (part {} of {})'.format( n + 1, len(wkts)), output_biomass_diff_tif) if not rest_summary_worker.success: QtWidgets.QMessageBox.critical( None, self.tr("Error"), self.tr("Error calculating biomass change summary table.")) return else: if n == 0: biomass_initial, \ biomass_change, \ area_site = rest_summary_worker.get_return() else: this_biomass_initial, \ this_biomass_change, \ this_area_site = rest_summary_worker.get_return() biomass_initial = biomass_initial + this_biomass_initial biomass_change = biomass_change + this_biomass_change area_site = area_site + this_area_site log('area_site: {}'.format(area_site)) log('biomass_initial: {}'.format(biomass_initial)) log('biomass_change: {}'.format(biomass_change)) # Figure out how many years of restoration this data is for, take this # from the second band in the in file band_infos = get_band_infos(in_file) length_yr = band_infos[1]['metadata']['years'] # And make a list of the restoration types rest_types = [ band_info['metadata']['type'] for band_info in band_infos[1:len(band_infos)] ] make_summary_table(self.output_file_table.text(), biomass_initial, biomass_change, area_site, length_yr, rest_types) # Add the biomass_dif layers to the map if len(output_biomass_diff_tifs) == 1: output_file = output_biomass_diff_tifs[0] else: output_file = os.path.splitext( output_biomass_diff_json)[0] + '.vrt' gdal.BuildVRT(output_file, output_biomass_diff_tifs) # Update the band infos to use the masking value (-32767) as the file # no data value, so that stretches are more likely to compute correctly for item in band_infos: item['no_data_value'] = -32767 create_local_json_metadata( output_biomass_diff_json, output_file, band_infos, metadata={ 'task_name': self.options_tab.task_name.text(), 'task_notes': self.options_tab.task_notes.toPlainText() }) schema = BandInfoSchema() for n in range(1, len(band_infos)): add_layer(output_file, n + 1, schema.dump(band_infos[n])) return True
def processAlgorithm(self, feedback): extent = str(self.getParameterValue(self.EXTENT)).split(',') spacing = float(self.getParameterValue(self.SPACING)) inset = float(self.getParameterValue(self.INSET)) randomize = self.getParameterValue(self.RANDOMIZE) isSpacing = self.getParameterValue(self.IS_SPACING) extent = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) mapCRS = iface.mapCanvas().mapSettings().destinationCrs() writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, mapCRS) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine( extent_geom.geometry()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if randomize: geom = QgsGeometry().fromPoint( QgsPoint( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry().fromPoint(QgsPoint(x, y)) if extent_engine.intersects(geom.geometry()): f.setAttribute('id', count) f.setGeometry(geom) writer.addFeature(f) x += pSpacing count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing del writer
def download(self, url): # Test if permalink is valid pattern = r"^(https?:\/\/(\w+[\w\-\.\:\/])+)\?((\&+)?(\w+)\=?([\w\-\.\:\,]+?)?)+(\&+)?$" if not re.match(pattern, self.url): raise Exception(u"Le permalien n'est pas valide.") # Extract params from url params = parse_qs(urlparse(self.url).query) # Check mandatory parameters try: context = str(params[r"context"][0]) center = params[r"centre"][0] except: raise Exception( u"Les paramètres \'Context\' et \'Centre\' sont obligatoires.") auth_contexts = [ r"metropole", r"guadeloupe", r"stmartin", r"stbarthelemy", r"guyane", r"reunion", r"mayotte" ] # Check if context is valid if context not in auth_contexts: raise Exception( u"La valeur \'%s\' est incorrecte.\n\n" u"\'Context\' doit prentre une des %s valeurs suivantes: " u"%s" % (context, len(auth_contexts), ", ".join(auth_contexts))) self.zone = context if self.zone in [r"guadeloupe", r"stmartin", r"stbarthelemy"]: self.zone = r"antilles" # Check if XY are valid if not re.match(r"^\-?\d+,\-?\d+$", center): raise Exception(u"Les coordonnées XY du centre sont incorrectes.") # Extract XY (¢re) xcenter = int(center.split(r",")[0]) ycenter = int(center.split(r",")[1]) # Compute the bbox xmin = xcenter - self.conn.extract_lim / 2 xmax = xcenter + self.conn.extract_lim / 2 ymin = ycenter - self.conn.extract_lim / 2 ymax = ycenter + self.conn.extract_lim / 2 # Transform coordinates in WGS84 bbox = tools.reproj(QgsRectangle(xmin, ymin, xmax, ymax), 3857, 4326) # Extract RFU (Send the request) resp = self.conn.extraction(bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()) if resp.code != 200: raise Exception(resp.read()) tree = EltTree.fromstring(resp.read()) # Check if error err = tree.find(r"./erreur") if err: raise Exception(err.text) # Create the layer: "Masque d'extraction" self.l_bbox = QgsVectorLayer(r"Polygon?crs=epsg:4326&index=yes", u"Zone de travail", r"memory") p_bbox = self.l_bbox.dataProvider() simple_symbol = QgsFillSymbolV2.createSimple({ r"color": r"116,97,87,255", r"style": r"b_diagonal", r"outline_style": r"no" }) renderer_bbox = QgsInvertedPolygonRenderer( QgsSingleSymbolRendererV2(simple_symbol)) self.l_bbox.setRendererV2(renderer_bbox) ft_bbox = QgsFeature() ft_bbox.setGeometry( QgsGeometry.fromRect( QgsRectangle(bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum()))) p_bbox.addFeatures([ft_bbox]) self.l_bbox.updateFields() self.l_bbox.updateExtents() # Create layers.. self.layers = self.extract_layers(tree) self.l_vertex = self.layers[0] self.l_edge = self.layers[1] # Add layer to the registry self.map_layer_registry.addMapLayers( [self.l_vertex, self.l_edge, self.l_bbox]) # Set the map canvas layer set self.canvas.setLayerSet([ QgsMapCanvasLayer(self.l_vertex), QgsMapCanvasLayer(self.l_edge), QgsMapCanvasLayer(self.l_bbox) ]) # Set extent self.canvas.setExtent( QgsRectangle(bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum())) self.features_vertex_backed_up = \ dict((ft[r"fid"], ft) for ft in self.get_features(self.l_vertex)) self.features_edge_backed_up = \ dict((ft[r"fid"], ft) for ft in self.get_features(self.l_edge)) # Get Capabitilies resp = self.conn.get_capabilities(self.zone) if resp.code != 200: raise Exception(resp.read()) tree = EltTree.fromstring(resp.read()) err = tree.find(r"./erreur") if err: raise Exception(err.text) for entry in tree.findall(r"./classe_rattachement/classe"): t = (entry.attrib[r"som_precision_rattachement"], entry.text) self.precision_class.append(t) for entry in tree.findall( r"./representation_plane_sommet_autorise/representation_plane_sommet" ): t = (entry.attrib[r"som_representation_plane"], entry.attrib[r"epsg_crs_id"], entry.text) self.ellips_acronym.append(t) for entry in tree.findall(r"./nature_sommet_conseille/nature"): self.nature.append(entry.text) for entry in tree.findall( r"./som_ge_createur_autorise/som_ge_createur"): t = (entry.attrib[r"num_ge"], entry.text) self.auth_creator.append(t) try: ft = next(ft for ft in self.l_vertex.getFeatures()) ft_attrib = tools.attrib_as_kv(ft.fields(), ft.attributes()) self.dflt_ellips_acronym = ft_attrib[r"som_representation_plane"] except: self.dflt_ellips_acronym = None for i, e in enumerate(self.ellips_acronym): self.projComboBox.addItem(e[2]) if not self.dflt_ellips_acronym: continue if self.dflt_ellips_acronym == e[0]: # Check projection in combobox self.projComboBox.setCurrentIndex(i) # Activate 'On The Fly' self.canvas.setCrsTransformEnabled(True) # Then change the CRS in canvas crs = QgsCoordinateReferenceSystem( int(e[1]), QgsCoordinateReferenceSystem.EpsgCrsId) self.canvas.setDestinationCrs(crs) # Then, start editing mode.. for layer in self.layers: if not layer.isEditable(): layer.startEditing() self.projComboBox.setDisabled(False) self.permalinkLineEdit.setDisabled(True) self.downloadPushButton.setDisabled(True) self.resetPushButton.setDisabled(False) self.uploadPushButton.setDisabled(False) self.downloadPushButton.clicked.disconnect(self.on_downloaded) self.permalinkLineEdit.returnPressed.disconnect(self.on_downloaded) self.resetPushButton.clicked.connect(self.on_reset) self.uploadPushButton.clicked.connect(self.on_uploaded) self.downloaded.emit() return True
def testDistrictBoundaryMatches(self): """ Test retrieving district boundary matches """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") f = QgsFeature() f.setAttributes(['a']) f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 15, 45))) f2 = QgsFeature() f2.setAttributes(['a']) f2.setGeometry(QgsGeometry.fromRect(QgsRectangle(15, 25, 18, 45))) f3 = QgsFeature() f3.setAttributes(['b']) f3.setGeometry( QgsGeometry.fromWkt( 'Polygon((18 30.01 19 35, 20 30, 19 25, 18 29.99, 20 30, 18 30.01))' )) success, (f, f2, f3) = layer.dataProvider().addFeatures([f, f2, f3]) self.assertTrue(success) canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() handler = RedistrictHandler(layer, 'fldtxt') registry = DistrictRegistry(districts=['a', 'b']) tool = InteractiveRedistrictingTool(canvas, handler, district_registry=registry) # point inside a feature self.assertFalse(tool.get_district_boundary_matches(QgsPointXY(10, 30))) self.assertFalse( [f for f in tool.get_target_features_from_matches([])]) # pylint: disable=unnecessary-comprehension self.assertFalse(tool.get_districts_from_matches([])) self.assertFalse(tool.matches_are_valid_for_boundary([])) # point directly on boundary matches = tool.get_district_boundary_matches(QgsPointXY(15, 30)) self.assertTrue(matches) self.assertCountEqual([match.featureId() for match in matches], [f.id(), f2.id()]) self.assertCountEqual( [f.id() for f in tool.get_target_features_from_matches(matches)], [f.id(), f2.id()]) self.assertCountEqual(tool.get_districts_from_matches(matches), ['a']) # not a valid boundary match - both features have same district! self.assertFalse(tool.matches_are_valid_for_boundary(matches)) # point just offset from boundary matches = tool.get_district_boundary_matches(QgsPointXY(15.1, 30)) self.assertTrue(matches) self.assertCountEqual([match.featureId() for match in matches], [f.id(), f2.id()]) self.assertCountEqual( [f.id() for f in tool.get_target_features_from_matches(matches)], [f.id(), f2.id()]) self.assertCountEqual(tool.get_districts_from_matches(matches), ['a']) self.assertFalse(tool.matches_are_valid_for_boundary(matches)) # unique matches only matches = tool.get_district_boundary_matches(QgsPointXY(18, 30)) self.assertTrue(matches) self.assertCountEqual([match.featureId() for match in matches], [f2.id(), f3.id()]) self.assertCountEqual( [f.id() for f in tool.get_target_features_from_matches(matches)], [f2.id(), f3.id()]) self.assertCountEqual(tool.get_districts_from_matches(matches), ['a', 'b']) # valid boundary match - both features have different districts self.assertTrue(tool.matches_are_valid_for_boundary(matches))
def bounding_box_geom(self): 'Returns bounding box in chosen destination coordinate system' return QgsGeometry.fromRect(self.l.extent())
def testInteraction(self): # pylint: disable=too-many-statements """ Test tool interaction """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) canvas.setFrameStyle(0) canvas.resize(600, 400) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string", "layer", "memory") f = QgsFeature() f.setAttributes(['a']) f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 32, 15, 45))) f2 = QgsFeature() f2.setAttributes(['b']) f2.setGeometry(QgsGeometry.fromRect(QgsRectangle(15, 25, 18, 45))) success, (f, f2) = layer.dataProvider().addFeatures([f, f2]) self.assertTrue(success) canvas.setLayers([layer]) canvas.setExtent(QgsRectangle(10, 30, 20, 35)) canvas.show() handler = RedistrictHandler(layer, 'fldtxt') factory = DecoratorFactory() registry = DistrictRegistry(districts=['a', 'b']) tool = InteractiveRedistrictingTool(canvas, handler, district_registry=registry, decorator_factory=factory) # mouse over a feature's interior point = canvas.mapSettings().mapToPixel().transform(20, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertFalse(tool.is_active) self.assertFalse(tool.snap_indicator.match().isValid()) # mouse over a single feature's boundary (not valid district boundary) point = canvas.mapSettings().mapToPixel().transform(5, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertFalse(tool.is_active) self.assertFalse(tool.snap_indicator.match().isValid()) # mouse over a two feature's boundary (valid district boundary) point = canvas.mapSettings().mapToPixel().transform(15, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertFalse(tool.is_active) self.assertTrue(tool.snap_indicator.match().isValid()) # avoid segfault tool.snap_indicator.setMatch(QgsPointLocator.Match()) # clicks to ignore point = canvas.mapSettings().mapToPixel().transform(10, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.MidButton) tool.canvasPressEvent(event) self.assertFalse(tool.is_active) event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.RightButton) tool.canvasPressEvent(event) self.assertFalse(tool.is_active) # click over bad area point = canvas.mapSettings().mapToPixel().transform(10, 30) event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.LeftButton) tool.canvasPressEvent(event) self.assertFalse(tool.is_active) # click over feature area layer.startEditing() point = canvas.mapSettings().mapToPixel().transform(10, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.LeftButton) tool.canvasPressEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.click_point.x(), 10) self.assertEqual(tool.click_point.y(), 33) self.assertEqual(tool.districts, {'a'}) self.assertFalse(tool.modified) # now move over current feature - should do nothing! point = canvas.mapSettings().mapToPixel().transform(10, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertTrue(tool.is_active) self.assertFalse(tool.modified) # move over other feature self.assertEqual(layer.getFeature(f2.id())[0], 'b') point = canvas.mapSettings().mapToPixel().transform(16, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.modified, {f2.id()}) self.assertEqual(tool.current_district, 'a') self.assertEqual(layer.getFeature(f2.id())[0], 'a') # move over nothing point = canvas.mapSettings().mapToPixel().transform(26, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.modified, {f2.id()}) # left click - commit changes event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.LeftButton) tool.canvasPressEvent(event) self.assertFalse(tool.is_active) layer.rollBack() layer.startEditing() # add a decorator tool.decorator_factory = TestDecoratorFactory() # now try with clicks over boundary point = canvas.mapSettings().mapToPixel().transform(15, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.LeftButton) tool.canvasPressEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.click_point.x(), 15) self.assertEqual(tool.click_point.y(), 33) self.assertEqual(tool.districts, {'a', 'b'}) self.assertFalse(tool.modified) # move left self.assertEqual(layer.getFeature(f.id())[0], 'a') self.assertEqual(layer.getFeature(f2.id())[0], 'b') point = canvas.mapSettings().mapToPixel().transform(10, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.modified, {f.id()}) self.assertEqual(tool.current_district, 'b') self.assertEqual(layer.getFeature(f.id())[0], 'b') self.assertEqual(layer.getFeature(f2.id())[0], 'b') # move over nothing point = canvas.mapSettings().mapToPixel().transform(26, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.modified, {f.id()}) # right click - discard changes event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.RightButton) tool.canvasPressEvent(event) self.assertFalse(tool.is_active) self.assertEqual(layer.getFeature(f.id())[0], 'a') self.assertEqual(layer.getFeature(f2.id())[0], 'b') # try again, move right point = canvas.mapSettings().mapToPixel().transform(15, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.LeftButton) tool.canvasPressEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.click_point.x(), 15) self.assertEqual(tool.click_point.y(), 33) self.assertEqual(tool.districts, {'a', 'b'}) self.assertFalse(tool.modified) # move right self.assertEqual(layer.getFeature(f.id())[0], 'a') self.assertEqual(layer.getFeature(f2.id())[0], 'b') point = canvas.mapSettings().mapToPixel().transform(17, 33) event = QgsMapMouseEvent(canvas, QEvent.MouseMove, QPoint(point.x(), point.y())) tool.canvasMoveEvent(event) self.assertTrue(tool.is_active) self.assertEqual(tool.modified, {f2.id()}) self.assertEqual(tool.current_district, 'a') self.assertEqual(layer.getFeature(f.id())[0], 'a') self.assertEqual(layer.getFeature(f2.id())[0], 'a') event = QgsMapMouseEvent(canvas, QEvent.MouseButtonPress, QPoint(point.x(), point.y()), Qt.RightButton) tool.canvasPressEvent(event) self.assertFalse(tool.is_active) self.assertEqual(layer.getFeature(f.id())[0], 'a') self.assertEqual(layer.getFeature(f2.id())[0], 'b') layer.rollBack()
def processAlgorithm(self, parameters, context, feedback): """ """ self.points_input_layer = self.parameterAsVectorLayer( parameters, QScoutValueGrabberAlgorithm.POINTS_INPUT, context) grid_w = self.parameterAsDouble(parameters, self.GRID_CELL_W_INPUT, context) grid_h = self.parameterAsDouble(parameters, self.GRID_CELL_H_INPUT, context) fields_to_use = self.parameterAsFields(parameters, self.FIELDS_TO_USE_INPUT, context) ag_idx = self.parameterAsEnum(parameters, self.AGGREGATION_FUNCTION_INPUT, context) bounds = self.parameterAsExtent(parameters, self.GRID_EXTENT_INPUT, context) file_out = self.parameterAsFileOutput(parameters, self.FILE_OUTPUT, context) if bounds is None or bounds.area() == 0: # if the user didn't provide an extent, use the extent of the points layer bounds = self.points_input_layer.extent() else: # if the user provided an extent, it will be in the project CRS, which isn't nessecarily the same as the # points layer, so we gotta run a conversion bounds_crs = self.parameterAsExtentCrs(parameters, self.GRID_EXTENT_INPUT, context) bounds_crs_convert = QgsCoordinateTransform( bounds_crs, self.points_input_layer.crs(), QgsProject.instance().transformContext()) bounds = bounds_crs_convert.transformBoundingBox(bounds) aggregation_class = AGGREGATION_FUNCTIONS[list( AGGREGATION_FUNCTIONS.keys())[ag_idx]] if aggregation_class is not None: aggregator = aggregation_class(self) else: # load custom aggregation function ag_func_file = self.parameterAsFile( parameters, self.CUSTOM_AGGREGATION_FUNCTION_INPUT, context) spec = spec_from_file_location( ag_func_file[ag_func_file.find(sep):ag_func_file.find(".")], ag_func_file) module = module_from_spec(spec) spec.loader.exec_module(module) aggregator = module.Aggregator(self) assert grid_w > 0, "Grid width must be greater than zero." assert grid_h > 0, "Grid height must be greater than zero.s" input_fields = [] self.output_fields = QgsFields() if aggregator.manual_field_ag(): for field_name, field_dtype in aggregator.return_vals(): self.feature_output_fields().add(field_name, field_dtype) input_fields = [f.name() for f in self.points_input_layer.fields()] for field in self.points_input_layer.fields(): if field.name() in fields_to_use: assert field.type() in ALLOWED_TYPES or aggregation_class is None, \ "Wrong dtype %s. Only int or double field types are supported." % field.typeName() # allow aggregators for non-numeric data types if using a custom aggregation class for return_val_name, return_val_dtype in aggregator.return_vals( ): return_val_dtype = field.type( ) if return_val_dtype is None else return_val_dtype self.feature_output_fields().append( QgsField(return_val_name + field.name(), return_val_dtype)) input_fields.append(field.name()) assert len(input_fields) == self.output_fields.size() grid_cells = self.setup_grid(bounds, grid_w, grid_h) xstart = floor(bounds.xMinimum()) ystart = floor(bounds.yMinimum()) df_w = ceil(bounds.width() / grid_w) + 1 df_h = ceil(bounds.height() / grid_h) + 1 fprogress = 0 ftotal = self.points_input_layer.featureCount() + (df_w * df_h) for feature in self.feature_input(): if feedback.isCanceled(): return { self.AGGREGATE_GRID_OUTPUT: None, self.FILE_OUTPUT: None } if feature.hasGeometry() and feature.geometry() is not None: point = feature.geometry().asPoint() x = int((point.x() - xstart) / grid_w) y = int((point.y() - ystart) / grid_h) if (x, y) in grid_cells: vals_dict = { f: (feature[f].value() if not QVariant.isNull(feature[f]) else np.NAN) if isinstance(feature[f], QVariant) else feature[f] for f in input_fields } grid_cells[(x, y)].add_point(point, vals_dict) else: feedback.pushInfo("(%s, %s) outside bounds." % (point.x(), point.y())) else: feedback.pushInfo("Feature %s has no geometry. Skipping." % feature.id()) fprogress += 1 feedback.setProgress(100 * int(fprogress / ftotal)) dest_id = self.create_sink(parameters, self.AGGREGATE_GRID_OUTPUT, context, QgsWkbTypes.Polygon) count = 0 if file_out: grid_frame = pd.DataFrame(index=range(len(grid_cells)), columns=["x", "y"] + [f.name() for f in self.output_fields] + ["Point Count"], dtype=np.float32) i = 0 for x, y in grid_cells: if feedback.isCanceled(): return { self.AGGREGATE_GRID_OUTPUT: None, self.FILE_OUTPUT: None } cell_coords = (x, y) cell = grid_cells[cell_coords] feature = QgsFeature(count) feature.setGeometry(QgsGeometry.fromRect(cell.rect)) cell_values = aggregator.aggregate(cell) if file_out: centroid = cell.rect.center() grid_frame.iloc[i]["x"] = centroid.x() grid_frame.iloc[i]["y"] = centroid.y() grid_frame.iloc[i]["Point Count"] = cell.point_count() grid_frame.iloc[i][[f.name() for f in self.output_fields]] = cell_values i += 1 assert len(cell_values) == self.feature_output_fields().size() feature.setAttributes(cell_values) count = self.append_to_feature_output(feature, count) fprogress += 1 feedback.setProgress(100 * int(fprogress / ftotal)) if file_out: if file_out.endswith(".xlsx"): grid_frame.to_excel(file_out, index=False) else: grid_frame.to_csv(file_out, index=False) with open(file_out) as fout: pass # make and close file io object else: fout = None feedback.pushWarning("File path '%s' invalid." % file_out) return {self.AGGREGATE_GRID_OUTPUT: dest_id, self.FILE_OUTPUT: fout}
def loadObstaclesDEM(self, obstacleLayersDEM, surfaceLayers): progressMessageBar = define._messagBar.createMessage( "Loading DEM Obstacles...") self.progress = QProgressBar() self.progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) progressMessageBar.layout().addWidget(self.progress) define._messagBar.pushWidget(progressMessageBar, define._messagBar.INFO) maxium = 0 offset = 0.0 self.progress.setValue(0) wCount = 0 hCount = 0 for ly in obstacleLayersDEM: # if isinstance(ly, QgsRasterLayer): # if demEvaluateAg == QMessageBox.No: # continue demDataProvider = ly.dataProvider() boundDem = demDataProvider.extent() xMin = boundDem.xMinimum() xMax = boundDem.xMaximum() yMin = boundDem.yMinimum() yMax = boundDem.yMaximum() bound = QgisHelper.getIntersectExtent( ly, QgsGeometry.fromRect(boundDem), surfaceLayers) # boundGeom = QgsGeometry.fromRect(bound) if bound == None: continue # if define._units != ly.crs().mapUnits(): # # if ly.crs().mapUnits() == QGis.Meters: # minPoint = QgisHelper.Degree2Meter(xMin, yMin) # maxPoint = QgisHelper.Degree2Meter(xMax, yMax) # bound = QgsRectangle(minPoint, maxPoint) # else: # minPoint = QgisHelper.Meter2Degree(xMin, yMin) # maxPoint = QgisHelper.Meter2Degree(xMax, yMax) # bound = QgsRectangle(minPoint, maxPoint) # if define._canvas.mapUnits() == ly.crs().mapUnits(): # if define._mapCrs != None and define._mapCrs != ly.crs(): # minPoint = QgisHelper.CrsTransformPoint(xMin, yMin, define._mapCrs, ly.crs()) # maxPoint = QgisHelper.CrsTransformPoint(xMax, yMax, define._mapCrs, ly.crs()) # bound = QgsRectangle(minPoint, maxPoint) block = ly.dataProvider().block(0, ly.extent(), ly.width(), ly.height()) xMinimum = ly.dataProvider().extent().xMinimum() yMaximum = ly.dataProvider().extent().yMaximum() yMinimum = ly.dataProvider().extent().yMinimum() xMaximum = ly.dataProvider().extent().xMaximum() xOffSet = ly.extent().width() / ly.width() yOffSet = ly.extent().height() / ly.height() offset = xOffSet if bound.xMinimum() < xMinimum: wStartNumber = 0 xStartValue = xMinimum else: wStartNumber = int((bound.xMinimum() - xMinimum) / xOffSet) xStartValue = bound.xMinimum() if yMaximum < bound.yMaximum(): hStartNumber = 0 yStartValue = yMaximum else: hStartNumber = int((yMaximum - bound.yMaximum()) / yOffSet) yStartValue = bound.yMaximum() if bound.xMaximum() > xMaximum: xEndValue = xMaximum else: xEndValue = bound.xMaximum() if yMinimum > bound.yMinimum(): yEndValue = yMinimum else: yEndValue = bound.yMinimum() wCount = int(math.fabs(xEndValue - xStartValue) / xOffSet) hCount = int(math.fabs(yEndValue - yStartValue) / yOffSet) pixelCount = hCount maxium += pixelCount cellSizeWnd = cellsizeWnd(offset, wCount * hCount, maxium * 0.04) cellSizeWnd.setWindowTitle("Input Cell Size") result = cellSizeWnd.exec_() if result == 1: offset = cellSizeWnd.cellsize maxium = cellSizeWnd.cellCount + 2 # print cellSizeWnd.textedit1.text() if maxium == 0: return False self.progress.setMaximum(maxium) for obstacleLayer in obstacleLayersDEM: obstacleUnits = obstacleLayer.crs().mapUnits() # if isinstance(obstacleLayer, QgsRasterLayer): # if demEvaluateAg == QMessageBox.No or demEvaluateAg == None: # continue # bound = QgisHelper.getMultiExtent(surfaceLayers) demDataProvider = obstacleLayer.dataProvider() boundDem = demDataProvider.extent() bound = QgisHelper.getIntersectExtent( obstacleLayer, QgsGeometry.fromRect(boundDem), surfaceLayers) if bound == None: continue boundGeom = QgsGeometry.fromRect(bound) if not boundGeom.intersects(QgsGeometry.fromRect(boundDem)): continue # if define._units != obstacleLayer.crs().mapUnits(): # xMin = bound.xMinimum() # xMax = bound.xMaximum() # yMin = bound.yMinimum() # yMax = bound.yMaximum() # if obstacleLayer.crs().mapUnits() == QGis.Meters: # minPoint = QgisHelper.Degree2Meter(xMin, yMin) # maxPoint = QgisHelper.Degree2Meter(xMax, yMax) # bound = QgsRectangle(minPoint, maxPoint) # else: # minPoint = QgisHelper.Meter2Degree(xMin, yMin) # maxPoint = QgisHelper.Meter2Degree(xMax, yMax) # bound = QgsRectangle(minPoint, maxPoint) # if define._canvas.mapUnits() == obstacleLayer.crs().mapUnits(): # if define._mapCrs != None and define._mapCrs != obstacleLayer.crs(): # minPoint = QgisHelper.CrsTransformPoint(xMin, yMin, define._mapCrs, obstacleLayer.crs()) # maxPoint = QgisHelper.CrsTransformPoint(xMax, yMax, define._mapCrs, obstacleLayer.crs()) # bound = QgsRectangle(minPoint, maxPoint) block = obstacleLayer.dataProvider().block(1, obstacleLayer.extent(), obstacleLayer.width(), obstacleLayer.height()) xMinimum = obstacleLayer.extent().xMinimum() yMaximum = obstacleLayer.extent().yMaximum() yMinimum = obstacleLayer.extent().yMinimum() xMaximum = obstacleLayer.extent().xMaximum() xOffSet = obstacleLayer.extent().width() / obstacleLayer.width() yOffSet = obstacleLayer.extent().height() / obstacleLayer.height() if bound.xMinimum() < xMinimum: wStartNumber = 0 xStartValue = xMinimum else: wStartNumber = int((bound.xMinimum() - xMinimum) / xOffSet) xStartValue = bound.xMinimum() if yMaximum < bound.yMaximum(): hStartNumber = 0 yStartValue = yMaximum else: hStartNumber = int((yMaximum - bound.yMaximum()) / yOffSet) yStartValue = bound.yMaximum() if bound.xMaximum() > xMaximum: xEndValue = xMaximum else: xEndValue = bound.xMaximum() if yMinimum > bound.yMinimum(): yEndValue = yMinimum else: yEndValue = bound.yMinimum() wCount = int(math.fabs(xEndValue - xStartValue) / offset) hCount = int(math.fabs(yEndValue - yStartValue) / offset) xPixelWidth = 0.0 yPixelWidth = 0.0 featureID = 0 i = 0 # j = 0 i0 = 0 j = 0 # i1 = int(hCount/2) # threads = [] # # i2 = int(hCount/3 * 2) # lock = threading.Lock() # # for n in range(2): # i0 = int(hCount/3 * n) # i1 = int(hCount/3 * (n + 1)) # if i0 == i1: # continue # thread0 = threading.Thread(target=processDEMMethod, args=(lock,i0,j,i1,wCount,block,wStartNumber,hStartNumber,xStartValue,yStartValue,xOffSet, yOffSet,obstacleLayer,self,obstacleUnits,ObstacleTable.MocMultiplier,)) # thread0.start() # threads.append(thread0) # i = i1 while i <= hCount - 1: j = 0 while j <= wCount - 1: # altitude = block.value(wStartNumber, hStartNumber) name = "DEM" # startTime = time.time() # print "startTime" + str(startTime) if block.isNoData(j * int(offset / xOffSet) + wStartNumber, i * int(offset / xOffSet) + hStartNumber): j += 1 continue altitude = block.value( j * int(offset / xOffSet) + wStartNumber, i * int(offset / xOffSet) + hStartNumber) trees = define._treesDEM tolerance = define._toleranceDEM point = QgsPoint( xStartValue + (j * int(offset / xOffSet)) * xOffSet + xOffSet / 2, yStartValue - (i * int(offset / xOffSet)) * yOffSet - yOffSet / 2) position = Point3D() positionDegree = Point3D() if obstacleUnits == QGis.Meters: if define._canvas.mapSettings().destinationCrs( ) != obstacleLayer.crs(): position = QgisHelper.CrsTransformPoint( point.x(), point.y(), obstacleLayer.crs(), define._canvas.mapSettings().destinationCrs(), altitude) if define._canvas.mapUnits() == QGis.Meters: if define._canvas.mapSettings().destinationCrs( ) != obstacleLayer.crs(): position = QgisHelper.CrsTransformPoint( point.x(), point.y(), obstacleLayer.crs(), define._canvas.mapSettings().destinationCrs(), altitude) else: position = Point3D(point.x(), point.y()) # if obstacleUnits != define._units: # positionDegree = QgisHelper.Meter2DegreePoint3D(position) else: if define._canvas.mapSettings().destinationCrs( ) != obstacleLayer.crs(): positionDegree = QgisHelper.CrsTransformPoint( point.x(), point.y(), obstacleLayer.crs(), define._canvas.mapSettings().destinationCrs(), altitude) else: positionDegree = Point3D(point.x(), point.y()) # if obstacleUnits != define._units: # position = QgisHelper.Degree2MeterPoint3D(positionDegree) featureId = featureID layerId = obstacleLayer.id() obstacle = Obstacle(name, position, layerId, featureId, None, trees, ObstacleTable.MocMultiplier, tolerance) obstacle.positionDegree = positionDegree if self.manualPolygon != None: if not self.manualPolygon.contains(obstacle.position): continue # middleTime = time.time() # print "middleTime" + str(middleTime) # obstacle.positionDegree = positionDegree self.checkObstacle(obstacle) self.progress.setValue(self.progress.value() + 1) QApplication.processEvents() # endTime = time.time() # print "endTime" + str(endTime) j += 1 featureID += 1 # obstacle = None # lock.acquire() # try: # finally: # lock.release() i += 1 # threadFinishedFlag = False # while not threadFinishedFlag: # QThread.sleep(0.5) # if(thread0.isFinished() and thread1.isFinished()): # threadFinishedFlag = True # for thread in threads: # thread.join() self.progress.setValue(maxium) define._messagBar.hide() self.manualPolygon = None
def geometry(self): geom = QgsGeometry.fromRect(self._unrotated_rect) if self._rotation: geom.rotate(-self._rotation, self._center) return geom
def _clip_vector_layer(layer, extent, extra_keywords=None, explode_flag=True, hard_clip_flag=False, explode_attribute=None): """Clip a Hazard or Exposure layer to the extents provided. The layer must be a vector layer or an exception will be thrown. The output layer will always be in WGS84/Geographic. :param layer: A valid QGIS vector or raster layer :type layer: :param extent: Either an array representing the exposure layer extents in the form [xmin, ymin, xmax, ymax]. It is assumed that the coordinates are in EPSG:4326 although currently no checks are made to enforce this. or: A QgsGeometry of type polygon. **Polygon clipping is currently only supported for vector datasets.** :type extent: list(float, float, float, float) :param extra_keywords: Optional keywords dictionary to be added to output layer. :type extra_keywords: dict :param explode_flag: A bool specifying whether multipart features should be 'exploded' into singleparts. **This parameter is ignored for raster layer clipping.** :type explode_flag: bool :param hard_clip_flag: A bool specifying whether line and polygon features that extend beyond the extents should be clipped such that they are reduced in size to the part of the geometry that intersects the extent only. Default is False. **This parameter is ignored for raster layer clipping.** :type hard_clip_flag: bool :param explode_attribute: A str specifying to which attribute #1, #2 and so on will be added in case of explode_flag being true. The attribute is modified only if there are at least 2 parts. :type explode_attribute: str :returns: Clipped layer (placed in the system temp dir). The output layer will be reprojected to EPSG:4326 if needed. :rtype: QgsVectorLayer """ if not layer or not extent: message = tr('Layer or Extent passed to clip is None.') raise InvalidParameterError(message) if layer.type() != QgsMapLayer.VectorLayer: message = tr('Expected a vector layer but received a %s.' % str(layer.type())) raise InvalidParameterError(message) # handle, file_name = tempfile.mkstemp('.sqlite', 'clip_', # temp_dir()) handle, file_name = tempfile.mkstemp('.shp', 'clip_', temp_dir()) # Ensure the file is deleted before we try to write to it # fixes windows specific issue where you get a message like this # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory. # This is because mkstemp creates the file handle and leaves # the file open. os.close(handle) os.remove(file_name) # Get the clip extents in the layer's native CRS geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) transform = QgsCoordinateTransform(geo_crs, layer.crs()) allowed_clip_values = [QGis.WKBPolygon, QGis.WKBPolygon25D] if type(extent) is list: rectangle = QgsRectangle(extent[0], extent[1], extent[2], extent[3]) # noinspection PyCallByClass # noinspection PyTypeChecker polygon = QgsGeometry.fromRect(rectangle) elif (type(extent) is QgsGeometry and extent.wkbType in allowed_clip_values): rectangle = extent.boundingBox().toRectF() polygon = extent else: raise InvalidClipGeometryError( tr('Clip geometry must be an extent or a single part' 'polygon based geometry.')) projected_extent = transform.transformBoundingBox(rectangle) # Get vector layer provider = layer.dataProvider() if provider is None: message = tr('Could not obtain data provider from ' 'layer "%s"' % layer.source()) raise Exception(message) # Get the layer field list, select by our extent then write to disk # .. todo:: FIXME - for different geometry types we should implement # different clipping behaviour e.g. reject polygons that # intersect the edge of the bbox. Tim request = QgsFeatureRequest() if not projected_extent.isEmpty(): request.setFilterRect(projected_extent) request.setFlags(QgsFeatureRequest.ExactIntersect) field_list = provider.fields() writer = QgsVectorFileWriter( file_name, 'UTF-8', field_list, layer.wkbType(), geo_crs, # 'SQLite') # FIXME (Ole): This works but is far too slow 'ESRI Shapefile') if writer.hasError() != QgsVectorFileWriter.NoError: message = tr('Error when creating shapefile: <br>Filename:' '%s<br>Error: %s' % (file_name, writer.hasError())) raise Exception(message) # Reverse the coordinate xform now so that we can convert # geometries from layer crs to geocrs. transform = QgsCoordinateTransform(layer.crs(), geo_crs) # Retrieve every feature with its geometry and attributes count = 0 has_multipart = False for feature in provider.getFeatures(request): geometry = feature.geometry() # Loop through the parts adding them to the output file # we write out single part features unless explode_flag is False if explode_flag: geometry_list = explode_multipart_geometry(geometry) else: geometry_list = [geometry] for part_index, part in enumerate(geometry_list): part.transform(transform) if hard_clip_flag: # Remove any dangling bits so only intersecting area is # kept. part = clip_geometry(polygon, part) if part is None: continue feature.setGeometry(part) # There are multiple parts and we want to show it in the # explode_attribute if part_index > 0 and explode_attribute is not None: has_multipart = True writer.addFeature(feature) count += 1 del writer # Flush to disk if count < 1: message = tr( 'No features fall within the clip extents. Try panning / zooming ' 'to an area containing data and then try to run your analysis ' 'again. If hazard and exposure data doesn\'t overlap at all, it ' 'is not possible to do an analysis. Another possibility is that ' 'the layers do overlap but because they may have different ' 'spatial references, they appear to be disjointed. If this is the ' 'case, try to turn on reproject on-the-fly in QGIS.') raise NoFeaturesInExtentError(message) keyword_io = KeywordIO() if extra_keywords is None: extra_keywords = {} extra_keywords['had multipart polygon'] = has_multipart keyword_io.copy_keywords(layer, file_name, extra_keywords=extra_keywords) base_name = '%s clipped' % layer.name() layer = QgsVectorLayer(file_name, base_name, 'ogr') return layer
def get_gaps_in_polygon_layer(self, layer, include_roads): """ Find gaps in a continuous layer in space. Ported/adapted to Python from: https://github.com/qgis/QGIS/blob/2c536307476e205b83d86863b903d7ea9d628f0d/src/plugins/topology/topolTest.cpp#L579-L726 """ request = QgsFeatureRequest().setSubsetOfAttributes([]) features = layer.getFeatures(request) featureCollection = list() for feature in features: if feature.geometry().isEmpty(): continue if not feature.geometry().isGeosValid(): continue if feature.geometry().isMultipart() and feature.geometry().type( ) == QgsWkbTypes.PolygonGeometry: for polygon in feature.geometry().asMultiPolygon(): featureCollection.append( QgsGeometry.fromPolygonXY(polygon)) continue featureCollection.append(feature.geometry()) union_geom = QgsGeometry.unaryUnion(featureCollection) aux_convex_hull = union_geom.convexHull() buffer_extent = QgsGeometry.fromRect(union_geom.boundingBox()).buffer( 2, 3) buffer_diff = buffer_extent.difference( QgsGeometry.fromRect(union_geom.boundingBox())) diff_geoms = buffer_extent.difference(union_geom).difference( buffer_diff) if not diff_geoms: return None feature_error = list() if not diff_geoms.isMultipart(): if include_roads and diff_geoms.touches( union_geom) and diff_geoms.intersects(buffer_diff): print("Unique value and no error") return None for geometry in diff_geoms.asMultiPolygon(): conflict_geom = QgsGeometry.fromPolygonXY(geometry) if not include_roads and conflict_geom.touches( union_geom) and conflict_geom.intersects(buffer_diff): continue if not union_geom.isMultipart() and conflict_geom.touches( union_geom) and conflict_geom.intersects(buffer_diff): continue feature_error.append(conflict_geom) unified_error = QgsGeometry.collectGeometry(feature_error) feature_error.clear() clean_errors = unified_error.intersection(aux_convex_hull) return self.extract_geoms_by_type(clean_errors, [QgsWkbTypes.PolygonGeometry])
def triggerResult(self, result: QgsLocatorResult): # this should be run in the main thread, i.e. mapCanvas should not be None # remove any map tip self.clearPreviousResults() if type(result.userData) == NoResult: pass # WMS elif type(result.userData) == WMSLayerResult: url_with_params = 'contextualWMSLegend=0' \ '&crs=EPSG:{crs}' \ '&dpiMode=7' \ '&featureCount=10' \ '&format=image/png' \ '&layers={layer}' \ '&styles=' \ '&url=http://wms.geo.admin.ch/?VERSION%3D2.0.0'\ .format(crs=self.crs, layer=result.userData.layer) wms_layer = QgsRasterLayer(url_with_params, result.displayString, 'wms') label = QLabel() label.setTextFormat(Qt.RichText) label.setTextInteractionFlags(Qt.TextBrowserInteraction) label.setOpenExternalLinks(True) if not wms_layer.isValid(): msg = self.tr('Cannot load WMS layer: {} ({})'.format(result.userData.title, result.userData.layer)) level = Qgis.Warning label.setText('<a href="https://map.geo.admin.ch/' '?lang=fr&bgLayer=ch.swisstopo.pixelkarte-farbe&layers={}">' 'Open layer in map.geo.admin.ch</a>'.format(result.userData.layer)) self.info(msg, level) else: msg = self.tr('WMS layer added to the map: {} ({})'.format(result.userData.title, result.userData.layer)) level = Qgis.Info label.setText('<a href="https://map.geo.admin.ch/' '?lang=fr&bgLayer=ch.swisstopo.pixelkarte-farbe&layers={}">' 'Open layer in map.geo.admin.ch</a>'.format(result.userData.layer)) QgsProject.instance().addMapLayer(wms_layer) self.message_emitted.emit(self.displayName(), msg, level, label) # Feature elif type(result.userData) == FeatureResult: point = QgsGeometry.fromPointXY(result.userData.point) point.transform(self.transform_4326) self.highlight(point) if self.settings.value('show_map_tip'): self.show_map_tip(result.userData.layer, result.userData.feature_id, point) # Location else: point = QgsGeometry.fromPointXY(result.userData.point) bbox = QgsGeometry.fromRect(result.userData.bbox) layer = result.userData.layer feature_id = result.userData.feature_id if not point or not bbox: return point.transform(self.transform_ch) bbox.transform(self.transform_ch) self.highlight(point, bbox) if layer and feature_id: self.fetch_feature(layer, feature_id) if self.settings.value('show_map_tip'): self.show_map_tip(layer, feature_id, point) else: self.current_timer = QTimer() self.current_timer.timeout.connect(self.clearPreviousResults) self.current_timer.setSingleShot(True) self.current_timer.start(5000)
def processAlgorithm(self, parameters, context, feedback): feedback.setProgress(1) extent = self.parameterAsExtent(parameters, self.EXTENT, context) min_zoom = self.parameterAsInt(parameters, self.ZOOM_MIN, context) max_zoom = self.parameterAsInt(parameters, self.ZOOM_MAX, context) dpi = self.parameterAsInt(parameters, self.DPI, context) tile_format = self.formats[self.parameterAsEnum( parameters, self.TILE_FORMAT, context)] output_format = self.outputs[self.parameterAsEnum( parameters, self.OUTPUT_FORMAT, context)] if output_format == 'Directory': output_dir = self.parameterAsString(parameters, self.OUTPUT_DIRECTORY, context) if not output_dir: raise QgsProcessingException( self.tr('You need to specify output directory.')) else: # MBTiles output_file = self.parameterAsString(parameters, self.OUTPUT_FILE, context) if not output_file: raise QgsProcessingException( self.tr('You need to specify output filename.')) tile_width = 256 tile_height = 256 wgs_crs = QgsCoordinateReferenceSystem('EPSG:4326') dest_crs = QgsCoordinateReferenceSystem('EPSG:3857') project = context.project() src_to_wgs = QgsCoordinateTransform(project.crs(), wgs_crs, context.transformContext()) wgs_to_dest = QgsCoordinateTransform(wgs_crs, dest_crs, context.transformContext()) settings = QgsMapSettings() settings.setOutputImageFormat(QImage.Format_ARGB32_Premultiplied) settings.setDestinationCrs(dest_crs) settings.setLayers(self.layers) settings.setOutputDpi(dpi) wgs_extent = src_to_wgs.transformBoundingBox(extent) wgs_extent = [ wgs_extent.xMinimum(), wgs_extent.yMinimum(), wgs_extent.xMaximum(), wgs_extent.yMaximum() ] metatiles_by_zoom = {} metatiles_count = 0 for zoom in range(min_zoom, max_zoom + 1): metatiles = get_metatiles(wgs_extent, zoom, 4) metatiles_by_zoom[zoom] = metatiles metatiles_count += len(metatiles) lab_buffer_px = 100 progress = 0 tile_params = { 'format': tile_format, 'quality': 75, 'width': tile_width, 'height': tile_height } if output_format == 'Directory': writer = DirectoryWriter(output_dir, tile_params) else: writer = MBTilesWriter(output_file, tile_params, wgs_extent, min_zoom, max_zoom) for zoom in range(min_zoom, max_zoom + 1): feedback.pushConsoleInfo('Generating tiles for zoom level: %s' % zoom) for i, metatile in enumerate(metatiles_by_zoom[zoom]): size = QSize(tile_width * metatile.rows(), tile_height * metatile.columns()) extent = QgsRectangle(*metatile.extent()) settings.setExtent(wgs_to_dest.transformBoundingBox(extent)) settings.setOutputSize(size) label_area = QgsRectangle(settings.extent()) lab_buffer = label_area.width() * (lab_buffer_px / size.width()) label_area.set(label_area.xMinimum() + lab_buffer, label_area.yMinimum() + lab_buffer, label_area.xMaximum() - lab_buffer, label_area.yMaximum() - lab_buffer) settings.setLabelBoundaryGeometry( QgsGeometry.fromRect(label_area)) image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) dpm = settings.outputDpi() / 25.4 * 1000 image.setDotsPerMeterX(dpm) image.setDotsPerMeterY(dpm) painter = QPainter(image) job = QgsMapRendererCustomPainterJob(settings, painter) job.renderSynchronously() painter.end() # For analysing metatiles (labels, etc.) # metatile_dir = os.path.join(output_dir, str(zoom)) # os.makedirs(metatile_dir, exist_ok=True) # image.save(os.path.join(metatile_dir, 'metatile_%s.png' % i)) for r, c, tile in metatile.tiles: tile_img = image.copy(tile_width * r, tile_height * c, tile_width, tile_height) writer.writeTile(tile, tile_img) progress += 1 feedback.setProgress(100 * (progress / metatiles_count)) writer.close() results = {} if output_format == 'Directory': results['OUTPUT_DIRECTORY'] = output_dir else: # MBTiles results['OUTPUT_FILE'] = output_file return results
def calculate_locally(self): if not self.groupBox_custom_SOC.isChecked(): QtWidgets.QMessageBox.critical(None, self.tr("Error"), self.tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom soil organic carbon dataset."), None) return if not self.lc_setup_tab.use_custom.isChecked(): QtWidgets.QMessageBox.critical(None, self.tr("Error"), self.tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom land cover dataset."), None) return if len(self.comboBox_custom_soc.layer_list) == 0: QtWidgets.QMessageBox.critical(None, self.tr("Error"), self.tr("You must add a soil organic carbon layer to your map before you can run the calculation."), None) return year_baseline = self.lc_setup_tab.get_initial_year() year_target = self.lc_setup_tab.get_final_year() if int(year_baseline) >= int(year_target): QtWidgets.QMessageBox.information(None, self.tr("Warning"), self.tr('The baseline year ({}) is greater than or equal to the target year ({}) - this analysis might generate strange results.'.format(year_baseline, year_target))) if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.lc_setup_tab.use_custom_initial.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical(None, self.tr("Error"), self.tr("Area of interest is not entirely within the initial land cover layer."), None) return if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.lc_setup_tab.use_custom_final.get_layer().extent())) < .99: QtWidgets.QMessageBox.critical(None, self.tr("Error"), self.tr("Area of interest is not entirely within the final land cover layer."), None) return out_f = self.get_save_raster() if not out_f: return self.close() # Select the initial and final bands from initial and final datasets # (in case there is more than one lc band per dataset) lc_initial_vrt = self.lc_setup_tab.use_custom_initial.get_vrt() lc_final_vrt = self.lc_setup_tab.use_custom_final.get_vrt() lc_files = [lc_initial_vrt, lc_final_vrt] lc_years = [self.lc_setup_tab.get_initial_year(), self.lc_setup_tab.get_final_year()] lc_vrts = [] for i in range(len(lc_files)): f = tempfile.NamedTemporaryFile(suffix='.vrt').name # Add once since band numbers don't start at zero gdal.BuildVRT(f, lc_files[i], bandList=[i + 1], outputBounds=self.aoi.get_aligned_output_bounds_deprecated(lc_initial_vrt), resolution='highest', resampleAlg=gdal.GRA_NearestNeighbour, separate=True) lc_vrts.append(f) soc_vrt = self.comboBox_custom_soc.get_vrt() climate_zones = os.path.join(os.path.dirname(__file__), 'data', 'IPCC_Climate_Zones.tif') in_files = [soc_vrt, climate_zones] in_files.extend(lc_vrts) in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name log(u'Saving SOC input files to {}'.format(in_vrt)) gdal.BuildVRT(in_vrt, in_files, resolution='highest', resampleAlg=gdal.GRA_NearestNeighbour, outputBounds=self.aoi.get_aligned_output_bounds_deprecated(lc_initial_vrt), separate=True) # Lc bands start on band 3 as band 1 is initial soc, and band 2 is # climate zones lc_band_nums = np.arange(len(lc_files)) + 3 log(u'Saving soil organic carbon to {}'.format(out_f)) soc_worker = StartWorker(SOCWorker, 'calculating change in soil organic carbon', in_vrt, out_f, lc_band_nums, lc_years, self.get_fl()) if not soc_worker.success: QtWidgets.QMessageBox.critical(None, self.tr("Error"), self.tr("Error calculating change in soil organic carbon."), None) return band_infos = [BandInfo("Soil organic carbon (degradation)", add_to_map=True, metadata={'year_start': lc_years[0], 'year_end': lc_years[-1]})] for year in lc_years: if (year == lc_years[0]) or (year == lc_years[-1]): # Add first and last years to map add_to_map = True else: add_to_map = False band_infos.append(BandInfo("Soil organic carbon", add_to_map=add_to_map, metadata={'year': year})) for year in lc_years: band_infos.append(BandInfo("Land cover (7 class)", metadata={'year': year})) out_json = os.path.splitext(out_f)[0] + '.json' create_local_json_metadata(out_json, out_f, band_infos) schema = BandInfoSchema() for band_number in range(len(band_infos)): b = schema.dump(band_infos[band_number]) if b['add_to_map']: # The +1 is because band numbers start at 1, not zero add_layer(out_f, band_number + 1, b)
def processAlgorithm(self, parameters, context, feedback): raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_DEM, context) target_crs = raster_layer.crs() rasterPath = raster_layer.source() source = self.parameterAsSource(parameters, self.BOUNDARY_LAYER, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.BOUNDARY_LAYER)) step = self.parameterAsDouble(parameters, self.STEP, context) percentage = self.parameterAsBool(parameters, self.USE_PERCENTAGE, context) outputPath = self.parameterAsString(parameters, self.OUTPUT_DIRECTORY, context) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterBand = rasterDS.GetRasterBand(1) noData = rasterBand.GetNoDataValue() cellXSize = abs(geoTransform[1]) cellYSize = abs(geoTransform[5]) rasterXSize = rasterDS.RasterXSize rasterYSize = rasterDS.RasterYSize rasterBBox = QgsRectangle(geoTransform[0], geoTransform[3] - cellYSize * rasterYSize, geoTransform[0] + cellXSize * rasterXSize, geoTransform[3]) rasterGeom = QgsGeometry.fromRect(rasterBBox) crs = osr.SpatialReference() crs.ImportFromProj4(str(target_crs.toProj4())) memVectorDriver = ogr.GetDriverByName('Memory') memRasterDriver = gdal.GetDriverByName('MEM') features = source.getFeatures(QgsFeatureRequest().setDestinationCrs(target_crs, context.transformContext())) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if not f.hasGeometry(): continue if feedback.isCanceled(): break geom = f.geometry() intersectedGeom = rasterGeom.intersection(geom) if intersectedGeom.isEmpty(): feedback.pushInfo( self.tr('Feature {0} does not intersect raster or ' 'entirely located in NODATA area').format(f.id())) continue fName = os.path.join( outputPath, 'hystogram_%s_%s.csv' % (source.sourceName(), f.id())) ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.asWkt()) bbox = intersectedGeom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startColumn, startRow) = raster.mapToPixel(xMin, yMax, geoTransform) (endColumn, endRow) = raster.mapToPixel(xMax, yMin, geoTransform) width = endColumn - startColumn height = endRow - startRow srcOffset = (startColumn, startRow, width, height) srcArray = rasterBand.ReadAsArray(*srcOffset) if srcOffset[2] == 0 or srcOffset[3] == 0: feedback.pushInfo( self.tr('Feature {0} is smaller than raster ' 'cell size').format(f.id())) continue newGeoTransform = ( geoTransform[0] + srcOffset[0] * geoTransform[1], geoTransform[1], 0.0, geoTransform[3] + srcOffset[1] * geoTransform[5], 0.0, geoTransform[5] ) memVDS = memVectorDriver.CreateDataSource('out') memLayer = memVDS.CreateLayer('poly', crs, ogr.wkbPolygon) ft = ogr.Feature(memLayer.GetLayerDefn()) ft.SetGeometry(ogrGeom) memLayer.CreateFeature(ft) ft.Destroy() rasterizedDS = memRasterDriver.Create('', srcOffset[2], srcOffset[3], 1, gdal.GDT_Byte) rasterizedDS.SetGeoTransform(newGeoTransform) gdal.RasterizeLayer(rasterizedDS, [1], memLayer, burn_values=[1]) rasterizedArray = rasterizedDS.ReadAsArray() srcArray = numpy.nan_to_num(srcArray) masked = numpy.ma.MaskedArray(srcArray, mask=numpy.logical_or(srcArray == noData, numpy.logical_not(rasterizedArray))) self.calculateHypsometry(f.id(), fName, feedback, masked, cellXSize, cellYSize, percentage, step) memVDS = None rasterizedDS = None feedback.setProgress(int(current * total)) rasterDS = None return {self.OUTPUT_DIRECTORY: outputPath}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) field_name = self.parameterAsString(parameters, self.FIELD, context) type = self.parameterAsEnum(parameters, self.TYPE, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) if use_field: # keep original field type, name and parameters field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append(source.fields()[field_index]) if type == 0: # envelope fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 1: # oriented rect fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('angle', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 2: # circle fields.append(QgsField('radius', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) elif type == 3: # convex hull fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if field_index >= 0: geometry_dict = {} bounds_dict = {} total = 50.0 / source.featureCount() if source.featureCount() else 1 features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index])) for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes - calculate on the fly for efficiency if not f.attributes()[field_index] in bounds_dict: bounds_dict[f.attributes()[field_index]] = f.geometry().boundingBox() else: bounds_dict[f.attributes()[field_index]].combineExtentWith(f.geometry().boundingBox()) else: if not f.attributes()[field_index] in geometry_dict: geometry_dict[f.attributes()[field_index]] = [f.geometry()] else: geometry_dict[f.attributes()[field_index]].append(f.geometry()) feedback.setProgress(int(current * total)) if type == 0: # bounding boxes current = 0 total = 50.0 / len(bounds_dict) if bounds_dict else 1 for group, rect in bounds_dict.items(): if feedback.isCanceled(): break # envelope feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(rect)) feature.setAttributes([current, group, rect.width(), rect.height(), rect.area(), rect.perimeter()]) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: current = 0 total = 50.0 / len(geometry_dict) if geometry_dict else 1 for group, geometries in geometry_dict.items(): if feedback.isCanceled(): break feature = self.createFeature(feedback, current, type, geometries, group) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: total = 80.0 / source.featureCount() if source.featureCount() else 1 features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) geometry_queue = [] bounds = QgsRectangle() for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes, calculate on the fly for efficiency bounds.combineExtentWith(f.geometry().boundingBox()) else: geometry_queue.append(f.geometry()) feedback.setProgress(int(current * total)) if not feedback.isCanceled(): if type == 0: feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(bounds)) feature.setAttributes([0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter()]) else: feature = self.createFeature(feedback, 0, type, geometry_queue) sink.addFeature(feature, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}