Ejemplo n.º 1
1
    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}
Ejemplo n.º 2
0
    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])
Ejemplo n.º 3
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)))
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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)
Ejemplo n.º 8
0
    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()
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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}
Ejemplo n.º 11
0
    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())
Ejemplo n.º 12
0
    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}
Ejemplo n.º 13
0
    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()
Ejemplo n.º 14
0
    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],
        )
Ejemplo n.º 15
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}
Ejemplo n.º 16
0
Archivo: grids.py Proyecto: gem/sidd
 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 )
Ejemplo n.º 18
0
    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
Ejemplo n.º 19
0
 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)
Ejemplo n.º 20
0
    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
Ejemplo n.º 21
0
    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
Ejemplo n.º 22
0
    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()
Ejemplo n.º 23
0
    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)
Ejemplo n.º 24
0
 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 ""
Ejemplo n.º 25
0
    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))
Ejemplo n.º 26
0
 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()
Ejemplo n.º 27
0
    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()
Ejemplo n.º 28
0
    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
Ejemplo n.º 29
0
    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)
Ejemplo n.º 30
0
    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)
Ejemplo n.º 31
0
    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))
Ejemplo n.º 32
0
    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)
Ejemplo n.º 33
0
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
Ejemplo n.º 34
0
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
Ejemplo n.º 35
0
    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
Ejemplo n.º 36
0
    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)
Ejemplo n.º 37
0
    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
Ejemplo n.º 38
0
    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}
Ejemplo n.º 39
0
    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
Ejemplo n.º 40
0
    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
Ejemplo n.º 42
0
    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)
Ejemplo n.º 43
0
    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))
Ejemplo n.º 44
0
    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())
Ejemplo n.º 45
0
    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
Ejemplo n.º 46
0
    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
Ejemplo n.º 47
0
    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 (&centre)
        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
Ejemplo n.º 48
0
    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))
Ejemplo n.º 49
0
 def bounding_box_geom(self):
     'Returns bounding box in chosen destination coordinate system'
     return QgsGeometry.fromRect(self.l.extent())
Ejemplo n.º 50
0
    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}
Ejemplo n.º 52
0
    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
Ejemplo n.º 53
0
 def geometry(self):
   geom = QgsGeometry.fromRect(self._unrotated_rect)
   if self._rotation:
     geom.rotate(-self._rotation, self._center)
   return geom
Ejemplo n.º 54
0
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
Ejemplo n.º 55
0
    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)
Ejemplo n.º 57
0
    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
Ejemplo n.º 58
0
    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)
Ejemplo n.º 59
0
    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}
Ejemplo n.º 60
0
    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}