Ejemplo n.º 1
1
def simpleMeasure(geom, method=0, ellips=None, crs=None):
    # Method defines calculation type:
    # 0 - layer CRS
    # 1 - project CRS
    # 2 - ellipsoidal

    if geom.type() == QgsWkbTypes.PointGeometry:
        if not geom.isMultipart():
            pt = geom.geometry()
            attr1 = pt.x()
            attr2 = pt.y()
        else:
            pt = geom.asMultiPoint()
            attr1 = pt[0].x()
            attr2 = pt[0].y()
    else:
        measure = QgsDistanceArea()

        if method == 2:
            measure.setSourceCrs(crs)
            measure.setEllipsoid(ellips)
            measure.setEllipsoidalMode(True)

        if geom.type() == QgsWkbTypes.PolygonGeometry:
            attr1 = measure.measureArea(geom)
            attr2 = measure.measurePerimeter(geom)
        else:
            attr1 = measure.measureLength(geom)
            attr2 = None

    return (attr1, attr2)
Ejemplo n.º 2
0
    def testMeasurePolygon(self):
        # +-+-+
        # |   |
        # + +-+
        # | |
        # +-+
        polygon = QgsGeometry.fromPolygon(
            [
                [
                    QgsPoint(0, 0),
                    QgsPoint(1, 0),
                    QgsPoint(1, 1),
                    QgsPoint(2, 1),
                    QgsPoint(2, 2),
                    QgsPoint(0, 2),
                    QgsPoint(0, 0),
                ]
            ]
        )

        da = QgsDistanceArea()
        area = da.measureArea(polygon)
        assert area == 3, "Expected:\n%f\nGot:\n%f\n" % (3, area)

        perimeter = da.measurePerimeter(polygon)
        assert perimeter == 8, "Expected:\n%f\nGot:\n%f\n" % (8, perimeter)
Ejemplo n.º 3
0
    def testMeasureMultiPolygon(self):
        # +-+-+ +-+-+
        # |   | |   |
        # + +-+ +-+ +
        # | |     | |
        # +-+     +-+
        polygon = QgsGeometry.fromMultiPolygon(
            [
                [[QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 2), QgsPoint(0, 2), QgsPoint(0, 0), ]],
                [[QgsPoint(4, 0), QgsPoint(5, 0), QgsPoint(5, 2), QgsPoint(3, 2), QgsPoint(3, 1), QgsPoint(4, 1), QgsPoint(4, 0), ]]
            ]
        )

        da = QgsDistanceArea()
        area = da.measureArea(polygon)
        assert area == 6, 'Expected:\n%f\nGot:\n%f\n' % (6, area)

        perimeter = da.measurePerimeter(polygon)
        assert perimeter == 16, "Expected:\n%f\nGot:\n%f\n" % (16, perimeter)
Ejemplo n.º 4
0
    def testMeasurePolygonWithHole(self):
        # +-+-+-+
        # |     |
        # + +-+ +
        # | | | |
        # + +-+ +
        # |     |
        # +-+-+-+
        polygon = QgsGeometry.fromPolygon(
            [
                [QgsPoint(0, 0), QgsPoint(3, 0), QgsPoint(3, 3), QgsPoint(0, 3), QgsPoint(0, 0)],
                [QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 2), QgsPoint(1, 2), QgsPoint(1, 1)],
            ]
        )
        da = QgsDistanceArea()
        area = da.measureArea(polygon)
        assert area == 8, "Expected:\n%f\nGot:\n%f\n" % (8, area)

# MH150729: Changed behaviour to consider inner rings for perimeter calculation. Therefore, expected result is 16.
        perimeter = da.measurePerimeter(polygon)
        assert perimeter == 16, "Expected:\n%f\nGot:\n%f\n" % (16, perimeter)
def evaluation(self=None, parameters={},feature=None):
    from PyQt4.QtCore import QVariant
    from qgis.core import QgsDistanceArea, QgsCoordinateReferenceSystem
    ar = NULL
    per = NULL
    id = NULL
    flr = NULL
    usage = NULL
    kind = NULL
    da_engine=QgsDistanceArea()
    da_engine.setSourceCrs(QgsCoordinateReferenceSystem(int(config.project_crs.split(':')[-1]), QgsCoordinateReferenceSystem.EpsgCrsId))
    da_engine.setEllipsoid(config.project_ellipsoid)
    da_engine.setEllipsoidalMode(True)
    if feature:
            geometry = feature.geometry()
            #print geometry
            ar = da_engine.measureArea(geometry)
            per =da_engine.measurePerimeter(geometry)
            id = feature[config.building_id_key] #necessary to safe dependency check
            flr = feature[u'FLRS_ALK']  # necessary to safe dependency check
            usage = feature[u'FUNC_ALK']  # necessary to safe dependency check
            kind = feature[u'KIND_ALK']  # necessary to safe dependency check

    #print ar
    #print per
    #print id

    return {config.building_id_key: {'type': QVariant.String,
                           'value': id},
            'AREA_ALK': {'type': QVariant.Double,
                           'value': ar},
            'PERI_ALK': {'type': QVariant.Double,
                           'value': per},
            'FLRS_ALK': {'type': QVariant.Double,
                           'value': flr},
            'FUNC_ALK': {'type': QVariant.Double,
                       'value': usage},
            'KIND_ALK': {'type': QVariant.Double,
                       'value': kind},
            }
Ejemplo n.º 6
0
    def processAlgorithm(self, progress):
        layer = dataobjects.getObjectFromUri(
            self.getParameterValue(self.VECTOR))
        value = float(self.getParameterValue(self.VALUE))
        minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
        strategy = self.getParameterValue(self.STRATEGY)

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))
        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fields, QgsWkbTypes.Point, layer.crs())

        da = QgsDistanceArea()

        features = vector.features(layer)
        for current, f in enumerate(features):
            fGeom = f.geometry()
            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(value)
            else:
                pointCount = int(round(value * da.measureArea(fGeom)))

            index = QgsSpatialIndex()
            points = dict()

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            total = 100.0 / pointCount

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                pnt = QgsPoint(rx, ry)
                geom = QgsGeometry.fromPoint(pnt)
                if geom.within(fGeom) and \
                        vector.checkMinDistance(pnt, index, minDistance, points):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', nPoints)
                    f.setGeometry(geom)
                    writer.addFeature(f)
                    index.insertFeature(f)
                    points[nPoints] = pnt
                    nPoints += 1
                    progress.setPercentage(int(nPoints * total))
                nIterations += 1

            if nPoints < pointCount:
                ProcessingLog.addToLog(ProcessingLog.LOG_INFO,
                                       self.tr('Can not generate requested number of random '
                                               'points. Maximum number of attempts exceeded.'))

            progress.setPercentage(0)

        del writer
Ejemplo n.º 7
0
    def testAreaMeasureAndUnits(self):
        """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the
           calculated areas and units are always consistent
        """

        da = QgsDistanceArea()
        da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext())
        da.setEllipsoid("NONE")

        polygon = QgsGeometry.fromPolygonXY(
            [[
                QgsPointXY(0, 0), QgsPointXY(1, 0), QgsPointXY(1, 1), QgsPointXY(2, 1), QgsPointXY(2, 2), QgsPointXY(0, 2), QgsPointXY(0, 0),
            ]]
        )

        # We check both the measured area AND the units, in case the logic regarding
        # ellipsoids and units changes in future
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or
                (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters))

        da.setEllipsoid("WGS84")
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        # should always be in Meters Squared
        self.assertAlmostEqual(area, 36918093794.121284, delta=0.1)
        self.assertEqual(units, QgsUnitTypes.AreaSquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareMiles)
        self.assertAlmostEqual(area, 14254.155703182701, delta=0.001)

        # now try with a source CRS which is in feet
        polygon = QgsGeometry.fromPolygonXY(
            [[
                QgsPointXY(1850000, 4423000), QgsPointXY(1851000, 4423000), QgsPointXY(1851000, 4424000), QgsPointXY(1852000, 4424000), QgsPointXY(1852000, 4425000), QgsPointXY(1851000, 4425000), QgsPointXY(1850000, 4423000)
            ]]
        )
        da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext())
        da.setEllipsoid("NONE")
        # measurement should be in square feet
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        self.assertAlmostEqual(area, 2000000, delta=0.001)
        self.assertEqual(units, QgsUnitTypes.AreaSquareFeet)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards)
        self.assertAlmostEqual(area, 222222.2222, delta=0.001)

        da.setEllipsoid("WGS84")
        # now should be in Square Meters again
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        self.assertAlmostEqual(area, 185818.59096575077, delta=1.0)
        self.assertEqual(units, QgsUnitTypes.AreaSquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards)
        self.assertAlmostEqual(area, 222237.18521272976, delta=1.0)
    def processAlgorithm(self, progress):
        layer = dataobjects.getObjectFromUri(
            self.getParameterValue(self.VECTOR))
        value = float(self.getParameterValue(self.VALUE))
        minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
        strategy = self.getParameterValue(self.STRATEGY)

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))
        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fields, QgsWkbTypes.Point, layer.crs())

        da = QgsDistanceArea()

        features = vector.features(layer)
        for current, f in enumerate(features):
            fGeom = f.geometry()
            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(value)
            else:
                pointCount = int(round(value * da.measureArea(fGeom)))

            index = QgsSpatialIndex()
            points = dict()

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            total = 100.0 / pointCount

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                pnt = QgsPoint(rx, ry)
                geom = QgsGeometry.fromPoint(pnt)
                if geom.within(fGeom) and \
                        vector.checkMinDistance(pnt, index, minDistance, points):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', nPoints)
                    f.setGeometry(geom)
                    writer.addFeature(f)
                    index.insertFeature(f)
                    points[nPoints] = pnt
                    nPoints += 1
                    progress.setPercentage(int(nPoints * total))
                nIterations += 1

            if nPoints < pointCount:
                ProcessingLog.addToLog(
                    ProcessingLog.LOG_INFO,
                    self.tr('Can not generate requested number of random '
                            'points. Maximum number of attempts exceeded.'))

            progress.setPercentage(0)

        del writer
Ejemplo n.º 9
0
class ExportGeometryInfo(QgisAlgorithm):

    INPUT = 'INPUT'
    METHOD = 'CALC_METHOD'
    OUTPUT = 'OUTPUT'

    def icon(self):
        return QIcon(
            os.path.join(pluginPath, 'images', 'ftools',
                         'export_geometry.png'))

    def tags(self):
        return self.tr(
            'export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons'
        ).split(',')

    def group(self):
        return self.tr('Vector geometry')

    def groupId(self):
        return 'vectorgeometry'

    def __init__(self):
        super().__init__()
        self.export_z = False
        self.export_m = False
        self.distance_area = None
        self.calc_methods = [
            self.tr('Layer CRS'),
            self.tr('Project CRS'),
            self.tr('Ellipsoidal')
        ]

    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFeatureSource(self.INPUT,
                                                self.tr('Input layer')))
        self.addParameter(
            QgsProcessingParameterEnum(self.METHOD,
                                       self.tr('Calculate using'),
                                       options=self.calc_methods,
                                       defaultValue=0))
        self.addParameter(
            QgsProcessingParameterFeatureSink(self.OUTPUT,
                                              self.tr('Added geom info')))

    def name(self):
        return 'exportaddgeometrycolumns'

    def displayName(self):
        return self.tr('Export geometry columns')

    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
        else:
            new_fields.append(QgsField('xcoord', QVariant.Double))
            new_fields.append(QgsField('ycoord', QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                new_fields.append(QgsField('zcoord', QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                new_fields.append(QgsField('mvalue', QVariant.Double))

        fields = QgsProcessingUtils.combineFields(fields, new_fields)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields, wkb_type,
                                               source.sourceCrs())

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(),
                                            context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(),
                                                    context.project().crs(),
                                                    context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}

    def point_attributes(self, geometry):
        pt = None
        if not geometry.isMultipart():
            pt = geometry.constGet()
        else:
            if geometry.numGeometries() > 0:
                pt = geometry.geometryN(0)
        attrs = []
        if pt:
            attrs.append(pt.x())
            attrs.append(pt.y())
            # add point z/m
            if self.export_z:
                attrs.append(pt.z())
            if self.export_m:
                attrs.append(pt.m())
        return attrs

    def line_attributes(self, geometry):
        return [self.distance_area.measureLength(geometry)]

    def polygon_attributes(self, geometry):
        area = self.distance_area.measureArea(geometry)
        perimeter = self.distance_area.measurePerimeter(geometry)
        return [area, perimeter]
class SplitMapTool(QgsMapToolEdit):
    def __init__(self, canvas, layer, actionMoveVertices, actionAddVertices,
                 actionRemoveVertices, actionMoveSegment, actionLineClose,
                 actionLineOpen, actionMoveLine):
        super(SplitMapTool, self).__init__(canvas)
        self.canvas = canvas
        self.scene = canvas.scene()
        self.layer = layer
        self.actionMoveVertices = actionMoveVertices
        self.actionAddVertices = actionAddVertices
        self.actionRemoveVertices = actionRemoveVertices
        self.actionMoveSegment = actionMoveSegment
        self.actionLineClose = actionLineClose
        self.actionLineOpen = actionLineOpen
        self.actionMoveLine = actionMoveLine
        self.initialize()

    def initialize(self):
        try:
            self.canvas.renderStarting.disconnect(self.mapCanvasChanged)
        except:
            pass
        self.canvas.renderStarting.connect(self.mapCanvasChanged)
        try:
            self.layer.editingStopped.disconnect(self.stopCapturing)
        except:
            pass
        self.layer.editingStopped.connect(self.stopCapturing)

        self.selectedFeatures = self.layer.selectedFeatures()
        self.rubberBand = None
        self.tempRubberBand = None
        self.capturedPoints = []
        self.capturing = False
        self.setCursor(Qt.CrossCursor)
        self.proj = QgsProject.instance()
        self.labels = []
        self.vertices = []
        self.calculator = QgsDistanceArea()
        self.calculator.setSourceCrs(self.layer.dataProvider().crs(),
                                     QgsProject.instance().transformContext())
        self.calculator.setEllipsoid(
            self.layer.dataProvider().crs().ellipsoidAcronym())
        self.drawingLine = False
        self.movingVertices = False
        self.addingVertices = False
        self.removingVertices = False
        self.movingSegment = False
        self.movingLine = False
        self.showingVertices = False
        self.movingVertex = -1
        self.movingSegm = -1
        self.movingLineInitialPoint = None
        self.lineClosed = False

    def restoreAction(self):
        self.addingVertices = False
        self.removingVertices = False
        self.movingVertices = False
        self.movingSegment = False
        self.movingLine = False
        self.showingVertices = False
        self.drawingLine = True
        self.movingVertex = -1
        self.movingLineInitialPoint = None
        self.deleteVertices()
        self.redrawRubberBand()
        self.redrawTempRubberBand()
        self.canvas.scene().addItem(self.tempRubberBand)
        self.redrawActions()

    def mapCanvasChanged(self):
        self.redrawAreas()
        if self.showingVertices:
            self.redrawVertices()

    def canvasMoveEvent(self, event):
        if self.drawingLine and not self.lineClosed:
            if self.tempRubberBand != None and self.capturing:
                mapPoint = self.toMapCoordinates(event.pos())
                self.tempRubberBand.movePoint(mapPoint)
                self.redrawAreas(event.pos())

        if self.movingVertices and self.movingVertex >= 0:
            layerPoint = self.toLayerCoordinates(self.layer, event.pos())
            self.capturedPoints[self.movingVertex] = layerPoint

            if self.lineClosed and self.movingVertex == 0:
                self.capturedPoints[len(self.capturedPoints) - 1] = layerPoint

            self.redrawRubberBand()
            self.redrawVertices()
            self.redrawAreas()

        if self.movingSegment and self.movingSegm >= 0:
            currentPoint = self.toLayerCoordinates(self.layer, event.pos())
            distance = self.distancePoint(currentPoint,
                                          self.movingLineInitialPoint)
            bearing = self.movingLineInitialPoint.azimuth(currentPoint)

            self.capturedPoints[self.movingSegm] = self.projectPoint(
                self.capturedPoints[self.movingSegm], distance, bearing)
            self.capturedPoints[self.movingSegm + 1] = self.projectPoint(
                self.capturedPoints[self.movingSegm + 1], distance, bearing)

            if self.lineClosed:
                if self.movingSegm == 0:
                    self.capturedPoints[
                        len(self.capturedPoints) - 1] = self.projectPoint(
                            self.capturedPoints[len(self.capturedPoints) - 1],
                            distance, bearing)
                elif self.movingSegm == len(self.capturedPoints) - 2:
                    self.capturedPoints[0] = self.projectPoint(
                        self.capturedPoints[0], distance, bearing)

            self.redrawRubberBand()
            self.redrawVertices()
            self.redrawAreas()
            self.movingLineInitialPoint = currentPoint

        if self.movingLine and self.movingLineInitialPoint != None:
            currentPoint = self.toLayerCoordinates(self.layer, event.pos())
            distance = self.distancePoint(currentPoint,
                                          self.movingLineInitialPoint)
            bearing = self.movingLineInitialPoint.azimuth(currentPoint)
            for i in range(len(self.capturedPoints)):
                self.capturedPoints[i] = self.projectPoint(
                    self.capturedPoints[i], distance, bearing)
            self.redrawRubberBand()
            self.redrawAreas()
            self.movingLineInitialPoint = currentPoint

    def projectPoint(self, point, distance, bearing):
        rads = bearing * pi / 180.0
        dx = distance * sin(rads)
        dy = distance * cos(rads)
        return QgsPointXY(point.x() + dx, point.y() + dy)

    def redrawAreas(self, mousePos=None):
        self.deleteLabels()

        if self.capturing and len(self.capturedPoints) > 0:
            for i in range(len(self.selectedFeatures)):
                geometry = QgsGeometry(self.selectedFeatures[i].geometry())
                movingPoints = list(self.capturedPoints)

                if mousePos != None:
                    movingPoints.append(
                        self.toLayerCoordinates(self.layer, mousePos))

                result, newGeometries, topoTestPoints = geometry.splitGeometry(
                    movingPoints, self.proj.topologicalEditing())

                self.addLabel(geometry)
                if newGeometries != None and len(newGeometries) > 0:
                    for i in range(len(newGeometries)):
                        self.addLabel(newGeometries[i])

    def addLabel(self, geometry):
        area = self.calculator.measureArea(geometry)
        labelPoint = geometry.pointOnSurface().vertexAt(0)
        label = QGraphicsTextItem("%.2f" % round(area, 2))
        label.setHtml(
            "<div style=\"color:#ffffff;background:#111111;padding:5px\">" +
            "%.2f" % round(area, 2) + " " +
            areaUnits[self.calculator.areaUnits()] + "</div>")
        point = self.toMapCoordinatesV2(self.layer, labelPoint)
        label.setPos(self.toCanvasCoordinates(QgsPointXY(point.x(),
                                                         point.y())))

        self.scene.addItem(label)
        self.labels.append(label)

    def deleteLabels(self):
        for i in range(len(self.labels)):
            self.scene.removeItem(self.labels[i])
        self.labels = []

    def canvasPressEvent(self, event):
        if self.movingVertices:
            for i in range(len(self.capturedPoints)):
                point = self.toMapCoordinates(self.layer,
                                              self.capturedPoints[i])
                currentVertex = self.toCanvasCoordinates(
                    QgsPointXY(point.x(), point.y()))
                if self.distancePoint(event.pos(),
                                      currentVertex) <= maxDistanceHitTest:
                    self.movingVertex = i
                    break

        if self.movingSegment:
            for i in range(len(self.capturedPoints) - 1):
                vertex1 = self.toMapCoordinates(self.layer,
                                                self.capturedPoints[i])
                currentVertex1 = self.toCanvasCoordinates(
                    QgsPointXY(vertex1.x(), vertex1.y()))
                vertex2 = self.toMapCoordinates(self.layer,
                                                self.capturedPoints[i + 1])
                currentVertex2 = self.toCanvasCoordinates(
                    QgsPointXY(vertex2.x(), vertex2.y()))
                if self.distancePointLine(
                        event.pos().x(),
                        event.pos().y(), currentVertex1.x(),
                        currentVertex1.y(), currentVertex2.x(),
                        currentVertex2.y()) <= maxDistanceHitTest:
                    self.movingSegm = i
                    break

        self.movingLineInitialPoint = self.toLayerCoordinates(
            self.layer, event.pos())

    def distancePoint(self, eventPos, vertexPos):
        return sqrt((eventPos.x() - vertexPos.x())**2 +
                    (eventPos.y() - vertexPos.y())**2)

    def canvasReleaseEvent(self, event):
        if self.movingVertices or self.movingSegment or self.movingLine:
            if event.button() == Qt.RightButton:
                self.finishOperation()
        elif self.addingVertices:
            if event.button() == Qt.LeftButton:
                self.addVertex(event.pos())
            elif event.button() == Qt.RightButton:
                self.finishOperation()
        elif self.removingVertices:
            if event.button() == Qt.LeftButton:
                self.removeVertex(event.pos())
            elif event.button() == Qt.RightButton:
                self.finishOperation()
        else:
            if event.button() == Qt.LeftButton:
                if not self.lineClosed:
                    if not self.capturing:
                        self.startCapturing()
                    self.addEndingVertex(event.pos())
            elif event.button() == Qt.RightButton:
                self.finishOperation()

        self.movingVertex = -1
        self.movingSegm = -1
        self.movingLineInitialPoint = None
        self.redrawActions()

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.stopCapturing()
        if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete:
            self.removeLastVertex()
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            self.finishOperation()

        event.accept()
        self.redrawActions()

    def finishOperation(self):
        self.doSplit()
        self.stopCapturing()
        self.initialize()
        self.startCapturing()

    def doSplit(self):
        if self.capturedPoints != None:
            self.layer.splitFeatures(self.capturedPoints,
                                     self.proj.topologicalEditing())

    def startCapturing(self):
        self.prepareRubberBand()
        self.prepareTempRubberBand()

        self.drawingLine = True
        self.capturing = True

        self.redrawActions()

    def prepareRubberBand(self):
        color = QColor("red")
        color.setAlphaF(0.78)

        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBand.setWidth(1)
        self.rubberBand.setColor(color)
        self.rubberBand.show()

    def prepareTempRubberBand(self):
        color = QColor("red")
        color.setAlphaF(0.78)

        self.tempRubberBand = QgsRubberBand(self.canvas,
                                            QgsWkbTypes.LineGeometry)
        self.tempRubberBand.setWidth(1)
        self.tempRubberBand.setColor(color)
        self.tempRubberBand.setLineStyle(Qt.DotLine)
        self.tempRubberBand.show()

    def redrawRubberBand(self):
        self.canvas.scene().removeItem(self.rubberBand)
        self.prepareRubberBand()
        for i in range(len(self.capturedPoints)):
            point = self.capturedPoints[i]
            if point.__class__ == QgsPoint:
                vertexCoord = self.toMapCoordinatesV2(self.layer,
                                                      self.capturedPoints[i])
                vertexCoord = QgsPointXY(vertexCoord.x(), vertexCoord.y())
            else:
                vertexCoord = self.toMapCoordinates(self.layer,
                                                    self.capturedPoints[i])

            self.rubberBand.addPoint(vertexCoord)

    def redrawTempRubberBand(self):
        if self.tempRubberBand != None:
            self.tempRubberBand.reset(QgsWkbTypes.LineGeometry)
            self.tempRubberBand.addPoint(
                self.toMapCoordinates(
                    self.layer,
                    self.capturedPoints[len(self.capturedPoints) - 1]))

    def stopCapturing(self):
        self.deleteLabels()
        self.deleteVertices()
        if self.rubberBand:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None
        self.drawingLine = False
        self.movingVertices = False
        self.showingVertices = False
        self.capturing = False
        self.capturedPoints = []
        self.canvas.refresh()

        self.redrawActions()

    def addEndingVertex(self, canvasPoint):
        mapPoint = self.toMapCoordinates(canvasPoint)
        layerPoint = self.toLayerCoordinates(self.layer, canvasPoint)

        self.rubberBand.addPoint(mapPoint)
        self.capturedPoints.append(layerPoint)

        self.tempRubberBand.reset(QgsWkbTypes.LineGeometry)
        self.tempRubberBand.addPoint(mapPoint)

    def removeLastVertex(self):
        if not self.capturing: return

        rubberBandSize = self.rubberBand.numberOfVertices()
        tempRubberBandSize = self.tempRubberBand.numberOfVertices()
        numPoints = len(self.capturedPoints)

        if rubberBandSize < 1 or numPoints < 1:
            return

        self.rubberBand.removePoint(-1)

        if rubberBandSize > 1:
            if tempRubberBandSize > 1:
                point = self.rubberBand.getPoint(0, rubberBandSize - 2)
                self.tempRubberBand.movePoint(tempRubberBandSize - 2, point)
        else:
            self.tempRubberBand.reset(self.bandType())

        del self.capturedPoints[-1]

    def addVertex(self, pos):
        newCapturedPoints = []
        for i in range(len(self.capturedPoints) - 1):
            newCapturedPoints.append(self.capturedPoints[i])
            vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i])
            currentVertex1 = self.toCanvasCoordinates(
                QgsPointXY(vertex1.x(), vertex1.y()))
            vertex2 = self.toMapCoordinates(self.layer,
                                            self.capturedPoints[i + 1])
            currentVertex2 = self.toCanvasCoordinates(
                QgsPointXY(vertex2.x(), vertex2.y()))

            distance = self.distancePointLine(pos.x(), pos.y(),
                                              currentVertex1.x(),
                                              currentVertex1.y(),
                                              currentVertex2.x(),
                                              currentVertex2.y())
            if distance <= maxDistanceHitTest:
                layerPoint = self.toLayerCoordinates(self.layer, pos)
                newCapturedPoints.append(layerPoint)

        newCapturedPoints.append(self.capturedPoints[len(self.capturedPoints) -
                                                     1])
        self.capturedPoints = newCapturedPoints

        self.redrawRubberBand()
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def removeVertex(self, pos):
        deletedFirst = False
        deletedLast = False
        newCapturedPoints = []
        for i in range(len(self.capturedPoints)):
            vertex = self.toMapCoordinates(self.layer, self.capturedPoints[i])
            currentVertex = self.toCanvasCoordinates(
                QgsPointXY(vertex.x(), vertex.y()))
            if not self.distancePoint(pos,
                                      currentVertex) <= maxDistanceHitTest:
                newCapturedPoints.append(self.capturedPoints[i])
            elif i == 0:
                deletedFirst = True
            elif i == len(self.capturedPoints) - 1:
                deletedLast = True

        self.capturedPoints = newCapturedPoints

        if deletedFirst and deletedLast:
            self.lineClosed = False

        self.redrawRubberBand()
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

        if len(self.capturedPoints) <= 2:
            self.stopRemovingVertices()

    def startMovingVertices(self):
        self.stopMovingLine()
        self.stopAddingVertices()
        self.stopRemovingVertices()
        self.stopMovingSegment()

        self.actionMoveVertices.setChecked(True)
        self.movingVertices = True
        self.showingVertices = True
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopMovingVertices(self):
        self.movingVertices = False
        self.actionMoveVertices.setChecked(False)
        self.restoreAction()

    def startAddingVertices(self):
        self.stopMovingVertices()
        self.stopRemovingVertices()
        self.stopMovingLine()
        self.stopMovingSegment()

        self.actionAddVertices.setChecked(True)
        self.addingVertices = True
        self.showingVertices = True
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopAddingVertices(self):
        self.addVertices = False
        self.actionAddVertices.setChecked(False)
        self.restoreAction()

    def startRemovingVertices(self):
        self.stopMovingVertices()
        self.stopAddingVertices()
        self.stopMovingLine()
        self.stopMovingSegment()

        self.actionRemoveVertices.setChecked(True)
        self.removingVertices = True
        self.showingVertices = True
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopRemovingVertices(self):
        self.removingVertices = False
        self.actionRemoveVertices.setChecked(False)
        self.restoreAction()

    def startMovingSegment(self):
        self.stopMovingVertices()
        self.stopMovingLine()
        self.stopAddingVertices()
        self.stopRemovingVertices()

        self.actionMoveSegment.setChecked(True)
        self.movingSegment = True
        self.showingVertices = False
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawVertices()
        self.redrawAreas()
        self.redrawActions()

    def stopMovingSegment(self):
        self.movingSegment = False
        self.actionMoveSegment.setChecked(False)
        self.restoreAction()

    def startMovingLine(self):
        self.stopMovingVertices()
        self.stopAddingVertices()
        self.stopRemovingVertices()
        self.stopMovingSegment()

        self.actionMoveLine.setChecked(True)
        self.movingLine = True
        self.showingVertices = False
        self.drawingLine = False
        self.canvas.scene().removeItem(self.tempRubberBand)
        self.redrawAreas()
        self.redrawActions()

    def stopMovingLine(self):
        self.actionMoveLine.setChecked(False)
        self.restoreAction()

    def lineClose(self):
        self.lineClosed = True
        self.capturedPoints.append(self.capturedPoints[0])
        self.redrawRubberBand()
        self.redrawTempRubberBand()
        self.redrawAreas()
        self.redrawActions()

    def lineOpen(self):
        self.lineClosed = False
        del self.capturedPoints[-1]
        self.redrawRubberBand()
        self.redrawTempRubberBand()
        self.redrawAreas()
        self.redrawActions()

    def showVertices(self):
        for i in range(len(self.capturedPoints)):
            vertexc = self.toMapCoordinates(self.layer, self.capturedPoints[i])
            vertexCoords = self.toCanvasCoordinates(
                QgsPointXY(vertexc.x(), vertexc.y()))
            if i == self.movingVertex:
                vertex = self.scene.addRect(vertexCoords.x() - 5,
                                            vertexCoords.y() - 5, 10, 10,
                                            QPen(QColor("green")),
                                            QBrush(QColor("green")))
                self.vertices.append(vertex)
            elif i == len(self.capturedPoints
                          ) - 1 and self.movingVertex == 0 and self.lineClosed:
                vertex = self.scene.addRect(vertexCoords.x() - 5,
                                            vertexCoords.y() - 5, 10, 10,
                                            QPen(QColor("green")),
                                            QBrush(QColor("green")))
                self.vertices.append(vertex)
            else:
                vertex = self.scene.addRect(vertexCoords.x() - 4,
                                            vertexCoords.y() - 4, 8, 8,
                                            QPen(QColor("red")),
                                            QBrush(QColor("red")))
                self.vertices.append(vertex)

    def deleteVertices(self):
        for i in range(len(self.vertices)):
            self.scene.removeItem(self.vertices[i])
        self.vertices = []

    def lineMagnitude(self, x1, y1, x2, y2):
        return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2))

    def distancePointLine(self, px, py, x1, y1, x2, y2):
        magnitude = self.lineMagnitude(x1, y1, x2, y2)

        if magnitude < 0.00000001:
            distance = 9999
            return distance

        u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1)))
        u = u1 / (magnitude * magnitude)

        if (u < 0.00001) or (u > 1):
            ix = self.lineMagnitude(px, py, x1, y1)
            iy = self.lineMagnitude(px, py, x2, y2)
            if ix > iy:
                distance = iy
            else:
                distance = ix
        else:
            ix = x1 + u * (x2 - x1)
            iy = y1 + u * (y2 - y1)
            distance = self.lineMagnitude(px, py, ix, iy)

        return distance

    def redrawVertices(self):
        self.deleteVertices()
        self.showVertices()

    def redrawActions(self):
        self.redrawActionMoveVertices()
        self.redrawActionAddVertices()
        self.redrawActionRemoveVertices()
        self.redrawActionMoveSegment()
        self.redrawActionLineClose()
        self.redrawActionLineOpen()
        self.redrawActionMoveLine()

    def redrawActionMoveVertices(self):
        self.actionMoveVertices.setEnabled(False)
        if len(self.capturedPoints) > 0:
            self.actionMoveVertices.setEnabled(True)

    def redrawActionAddVertices(self):
        self.actionAddVertices.setEnabled(False)
        if len(self.capturedPoints) >= 2:
            self.actionAddVertices.setEnabled(True)

    def redrawActionRemoveVertices(self):
        self.actionRemoveVertices.setEnabled(False)
        if len(self.capturedPoints) > 2:
            self.actionRemoveVertices.setEnabled(True)

    def redrawActionMoveSegment(self):
        self.actionMoveSegment.setEnabled(False)
        if len(self.capturedPoints) > 2:
            self.actionMoveSegment.setEnabled(True)

    def redrawActionLineClose(self):
        self.actionLineClose.setEnabled(False)
        if not self.lineClosed and len(self.capturedPoints) >= 3:
            self.actionLineClose.setEnabled(True)

    def redrawActionLineOpen(self):
        self.actionLineOpen.setEnabled(False)
        if self.lineClosed:
            self.actionLineOpen.setEnabled(True)

    def redrawActionMoveLine(self):
        self.actionMoveLine.setEnabled(False)
        if len(self.capturedPoints) > 0:
            self.actionMoveLine.setEnabled(True)
Ejemplo n.º 11
0
class MeasureSelectedFeatures:
    def __init__(self, iface):
        self.iface = iface
        self.window = self.iface.mainWindow()
        self.proj_close_action = [
            a for a in self.iface.projectMenu().actions()
            if a.objectName() == 'mActionCloseProject'
        ][0]
        self.dlg = MeasureSelectedFeaturesDialog()
        self.toolbar = self.iface.pluginToolBar()
        self.folder_name = os.path.dirname(os.path.abspath(__file__))
        self.icon_path = os.path.join(self.folder_name, 'msf_icon.png')
        self.action = QAction(QIcon(self.icon_path),
                              'Sum selected feature size', self.window)
        self.action.setToolTip('Display total dimensions of selected features')
        self.da = QgsDistanceArea()
        #        self.da.setEllipsoid('WGS84')
        self.Distance_Units = {
            0: 'm',
            1: 'km',
            2: 'Feet',
            3: 'NM',
            4: 'Yards',
            5: 'Miles',
            6: 'Degrees',
            7: 'cm',
            8: 'mm',
            9: 'Unknown units'
        }
        self.Area_Units = {
            0: 'm2',
            1: 'km2',
            2: 'Square feet',
            3: 'Square yards',
            4: 'Square miles',
            5: 'Hectares',
            6: 'Acres',
            7: 'NM2',
            8: 'Square degrees',
            9: 'cm2',
            10: 'mm2',
            11: 'Unknown units'
        }

        self.cb_linear_items = [
            'meters', 'kilometers', 'feet', 'nautical miles', 'yards', 'miles',
            'degrees', 'centimeters', 'millimeters'
        ]

        self.cb_area_items = [
            'square meters', 'square kilometers', 'square feet',
            'square yards', 'square miles', 'hectares', 'acres',
            'square nautical miles', 'square degrees', 'square centimeters',
            'square millimeters'
        ]

        self.project = None
        self.layer = None

    def initGui(self):
        """This method is where we add the plugin action to the plugin toolbar."""
        self.action.setObjectName('btnMSF')
        self.toolbar.addAction(self.action)
        if self.iface.activeLayer():
            self.layer = self.iface.activeLayer()
        else:
            self.action.setEnabled(False)
        self.action.triggered.connect(self.action_triggered)
        self.iface.projectRead.connect(self.project_opened)
        self.iface.newProjectCreated.connect(self.project_opened)
        self.dlg.was_closed.connect(self.dockwidget_closed)
        self.dlg.topLevelChanged.connect(self.widget_moved)
        self.iface.projectMenu().aboutToShow.connect(self.project_menu_shown)
        self.proj_close_action.triggered.connect(
            self.project_closed_via_menu_action)
        self.dlg.rad_1.setChecked(True)  # 03-06-21
        #####31-05-21
        self.dlg.rad_1.toggled.connect(
            self.radios_toggled)  #############25-06-21
        self.dlg.cb_units.currentIndexChanged.connect(self.total_length)

    def project_menu_shown(self):
        if self.dlg.isVisible():
            self.dlg.close()

    def project_opened(self):
        if self.project is not None:
            self.project.layerWasAdded.disconnect(self.layer_added)
            self.project.layersRemoved.disconnect(self.layers_removed)
        self.project = QgsProject.instance()
        a = [a for a in self.toolbar.actions()
             if a.objectName() == 'btnMSF'][0]
        if self.iface.activeLayer():
            self.layer = self.iface.activeLayer()
            if not a.isEnabled():
                a.setEnabled(True)
            self.set_title()
        else:
            if a.isEnabled():
                a.setEnabled(False)
        self.project.layerWasAdded.connect(self.layer_added)
        self.project.layersRemoved.connect(self.layers_removed)

    def layer_added(self, l):
        if self.layer is None:
            if isinstance(l, QgsVectorLayer):
                self.layer = l
        if len(self.project.mapLayers()) == 1:
            a = [
                a for a in self.toolbar.actions() if a.objectName() == 'btnMSF'
            ][0]
            if not a.isEnabled():
                a.setEnabled(True)

    def layers_removed(self, lyr_ids):
        if len(self.project.mapLayers()) == 0:
            self.layer = None
            if self.dlg.isVisible():
                self.dlg.close()
            a = [
                a for a in self.toolbar.actions() if a.objectName() == 'btnMSF'
            ][0]
            if a.isEnabled():
                a.setEnabled(False)

    def project_closed_via_menu_action(self):
        a = [a for a in self.toolbar.actions()
             if a.objectName() == 'btnMSF'][0]
        a.setEnabled(False)
        QgsProject.instance().layerWasAdded.disconnect(self.layer_added)

    def widget_moved(self, top_level):
        if top_level is True:
            self.set_gui_geometry()

    def set_gui_geometry(self):
        self.dlg.setGeometry(750, 300, 750, 50)

    def action_triggered(self):
        self.window.addDockWidget(Qt.TopDockWidgetArea, self.dlg)
        self.dlg.setAllowedAreas(Qt.TopDockWidgetArea)
        self.dlg.show()
        if self.layer is not None:
            if isinstance(self.iface.activeLayer(), QgsVectorLayer):
                self.layer = self.iface.activeLayer()
            if isinstance(self.layer, QgsVectorLayer):
                self.layer.selectionChanged.connect(self.total_length)
        self.iface.currentLayerChanged.connect(self.active_changed)
        self.set_title()  # V2 change
        self.total_length()  # V2 change
        #####25-05-21
        self.action.setEnabled(False)
        #####

    def active_changed(self, new_layer):
        self.tool_reset(self.layer)
        self.set_title()  # V3 change
        if isinstance(new_layer, QgsVectorLayer):
            if self.layer is not None:
                #                print(self.layer.name())
                if len(QgsProject.instance().mapLayers()) > 1:
                    self.layer.selectionChanged.disconnect(self.total_length)
            self.layer = new_layer
            self.layer.selectionChanged.connect(self.total_length)
            self.total_length()  # V2 change

    def tool_reset(self, layer):
        if layer is not None:
            if isinstance(
                    layer,
                    QgsVectorLayer) and layer.geometryType() == 0:  # V2 change
                layer.selectByIds([])
            for le in self.dlg.findChildren(QLineEdit):
                le.clear()
            for cb in self.dlg.findChildren(QComboBox):
                cb.clear()
                cb.setEnabled(False)

    def set_title(self):
        self.dlg.lbl_1.setText('Total')
        for le in self.dlg.findChildren(QLineEdit):
            le.setEnabled(False)
        active_layer = self.iface.activeLayer()
        if isinstance(active_layer, QgsVectorLayer):
            if active_layer.isSpatial():
                #####25-05-21
                if active_layer.geometryType() == 0:  # points
                    self.dlg.setWindowTitle('Point layer selected')
                    for le in self.dlg.findChildren(QLineEdit):
                        le.setEnabled(False)
                    for rb in self.dlg.findChildren(QRadioButton):
                        rb.setEnabled(False)
                    for cb in self.dlg.findChildren(QComboBox):
                        cb.clear()
                        cb.setEnabled(False)
                elif active_layer.geometryType() in [1, 2]:
                    #                    self.dlg.setWindowTitle('Measuring {} selected features from layer: {}'.format(active_layer.selectedFeatureCount(), active_layer.name())) # V2 change
                    for le in self.dlg.findChildren(QLineEdit):
                        le.setEnabled(True)
                    self.dlg.cb_units.setEnabled(True)
                    if active_layer.crs().isGeographic():
                        self.dlg.rad_1.setChecked(True)
                        self.dlg.rad_1.setEnabled(True)
                        self.dlg.rad_2.setEnabled(False)
                        ###25-06-21
                        self.dlg.cb_units.clear()
                        if active_layer.geometryType() == 1:  # lines
                            self.dlg.cb_units.addItems(self.cb_linear_items)
                        elif active_layer.geometryType() == 2:  # polygons
                            self.dlg.cb_units.addItems(self.cb_area_items)
                    else:  # projected CRS
                        for rb in self.dlg.findChildren(QRadioButton):
                            rb.setEnabled(True)
                        ###25-06-21
                        if active_layer.geometryType() == 1:  # lines
                            self.dlg.cb_units.clear()
                            self.dlg.cb_units.addItems(self.cb_linear_items)
                            if self.dlg.rad_2.isChecked():
                                self.dlg.cb_units.removeItem(
                                    self.cb_linear_items.index('degrees'))
                        elif active_layer.geometryType() == 2:  # polygons
                            self.dlg.cb_units.clear()
                            self.dlg.cb_units.addItems(self.cb_area_items)
                            if self.dlg.rad_2.isChecked():
                                self.dlg.cb_units.removeItem(
                                    self.cb_area_items.index('square degrees'))

                #####
            elif not active_layer.isSpatial():
                self.dlg.setWindowTitle(
                    'Raster or non-spatial vector layer selected')
                for le in self.dlg.findChildren(QLineEdit):
                    le.setEnabled(False)
                for rb in self.dlg.findChildren(QRadioButton):
                    rb.setEnabled(False)
                for cb in self.dlg.findChildren(QComboBox):
                    cb.clear()
                    cb.setEnabled(False)
        elif isinstance(active_layer, QgsRasterLayer):
            self.dlg.setWindowTitle(
                'Raster or non-spatial vector layer selected')
            for le in self.dlg.findChildren(QLineEdit):
                le.setEnabled(False)
            for rb in self.dlg.findChildren(QRadioButton):
                rb.setEnabled(False)
            for cb in self.dlg.findChildren(QComboBox):
                cb.clear()
                cb.setEnabled(False)
        elif active_layer is None:
            self.dlg.setWindowTitle('No layer selected')

    def radios_toggled(self):
        if self.iface.activeLayer().geometryType() == 1:  # lines
            if self.dlg.rad_2.isChecked():  # planimetric
                if self.dlg.cb_units.currentText() == 'degrees':
                    # reload combobox items without degree option
                    self.dlg.cb_units.clear()
                    self.dlg.cb_units.addItems(self.cb_linear_items)
                    self.dlg.cb_units.removeItem(
                        self.cb_linear_items.index('degrees'))
                else:
                    # just remove the degree option
                    self.dlg.cb_units.removeItem(
                        self.cb_linear_items.index('degrees'))
            elif self.dlg.rad_1.isChecked():  # ellipsoidal
                if self.dlg.cb_units.count() == 0:
                    self.dlg.cb_units.addItems(self.cb_linear_items)
                    if not self.dlg.cb_units.isEnabled():
                        self.dlg.cb_units.setEnabled(True)
                    if self.layer.crs().mapUnits(
                    ) != QgsUnitTypes.DistanceUnknownUnit:
                        self.dlg.cb_units.setCurrentText(
                            QgsUnitTypes.encodeUnit(
                                self.layer.crs().mapUnits()))
                else:
                    self.dlg.cb_units.insertItem(6, 'degrees')
        elif self.iface.activeLayer().geometryType() == 2:  # polygons
            if self.dlg.rad_2.isChecked():
                if self.dlg.cb_units.currentText() == 'square degrees':
                    self.dlg.cb_units.clear()
                    self.dlg.cb_units.addItems(self.cb_area_items)
                    self.dlg.cb_units.removeItem(
                        self.cb_area_items.index('square degrees'))
                else:
                    self.dlg.cb_units.removeItem(
                        self.cb_area_items.index('square degrees'))
            elif self.dlg.rad_1.isChecked():
                if self.dlg.cb_units.count() == 0:
                    self.dlg.cb_units.addItems(self.cb_area_items)
                    if not self.dlg.cb_units.isEnabled():
                        self.dlg.cb_units.setEnabled(True)
                    if self.layer.crs().mapUnits(
                    ) != QgsUnitTypes.DistanceUnknownUnit:
                        self.dlg.cb_units.setCurrentText('square {}'.format(
                            QgsUnitTypes.encodeUnit(
                                self.layer.crs().mapUnits())))
                else:
                    self.dlg.cb_units.insertItem(8, 'square degrees')
        self.total_length()

    def geodetic_length(self, feat):
        geo_m = self.da.measureLength(feat.geometry())
        return geo_m

    def geodetic_area(self, feat):
        geo_m2 = self.da.measureArea(feat.geometry())
        return geo_m2

    def planar_length(self, feat):
        proj_m = feat.geometry().length()
        return proj_m

    def planar_area(self, feat):
        proj_m2 = feat.geometry().area()
        return proj_m2

    def total_length(self):
        #        print('func called')
        layer = self.layer
        #        self.set_title()
        if isinstance(layer, QgsVectorLayer) and layer.isSpatial():
            #####04-06-21
            self.da.setSourceCrs(layer.crs(),
                                 QgsProject.instance().transformContext())
            self.da.setEllipsoid(layer.crs().ellipsoidAcronym())
            #####04-06-21
            select_fts = [f for f in layer.selectedFeatures()]

            epsg_code = layer.crs().authid()
            if layer.crs().isGeographic():
                crs_type = 'Geographic'
            else:
                crs_type = 'Projected'

            l_units = layer.crs().mapUnits()
            if layer.geometryType() == 1:  # Lines
                self.dlg.setWindowTitle(
                    'Measuring {} selected features from layer: {} - {} ({})'.
                    format(layer.selectedFeatureCount(), layer.name(),
                           epsg_code, crs_type))
                self.dlg.lbl_1.setText('Total length of selected features: ')
                if layer.crs().isGeographic() or (
                        not layer.crs().isGeographic()
                        and self.dlg.rad_1.isChecked()):
                    total_geo_m = sum(
                        [self.geodetic_length(f) for f in select_fts])
                    if self.dlg.cb_units.currentText() == 'meters':
                        self.dlg.le_total.setText(
                            str('{:.3f}m'.format(total_geo_m)))
                    elif self.dlg.cb_units.currentText() == 'kilometers':
                        total_geo_km = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceKilometers)
                        self.dlg.le_total.setText(
                            str('{:.3f}km'.format(total_geo_km)))
                    elif self.dlg.cb_units.currentText() == 'feet':
                        total_geo_ft = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceFeet)
                        self.dlg.le_total.setText(
                            str('{:.3f}ft'.format(total_geo_ft)))
                    elif self.dlg.cb_units.currentText() == 'nautical miles':
                        total_geo_nm = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceNauticalMiles)
                        self.dlg.le_total.setText(
                            str('{:.3f}NM'.format(total_geo_nm)))
                    elif self.dlg.cb_units.currentText() == 'yards':
                        total_geo_yds = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceYards)
                        self.dlg.le_total.setText(
                            str('{:.3f}yds'.format(total_geo_yds)))
                    elif self.dlg.cb_units.currentText() == 'miles':
                        total_geo_mi = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceMiles)
                        self.dlg.le_total.setText(
                            str('{:.3f}mi'.format(total_geo_mi)))
                    elif self.dlg.cb_units.currentText() == 'degrees':
                        total_geo_deg = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceDegrees)
                        self.dlg.le_total.setText(
                            str('{:.3f}deg'.format(total_geo_deg)))
                    elif self.dlg.cb_units.currentText() == 'centimeters':
                        total_geo_cm = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceCentimeters)
                        self.dlg.le_total.setText(
                            str('{:.3f}cm'.format(total_geo_cm)))
                    elif self.dlg.cb_units.currentText() == 'millimeters':
                        total_geo_mm = self.da.convertLengthMeasurement(
                            total_geo_m, QgsUnitTypes.DistanceMillimeters)
                        self.dlg.le_total.setText(
                            str('{:.3f}mm'.format(total_geo_mm)))

                else:  # projected CRS
                    total_length_proj = sum(
                        [self.planar_length(f) for f in select_fts])
                    if l_units != 6:  # Units are NOT degrees
                        if self.dlg.cb_units.currentText() == 'meters':
                            self.dlg.le_total.setText(
                                str('{:.3f}m'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 0))))
                        elif self.dlg.cb_units.currentText() == 'kilometers':
                            self.dlg.le_total.setText(
                                str('{:.3f}km'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 1))))
                        elif self.dlg.cb_units.currentText() == 'feet':
                            self.dlg.le_total.setText(
                                str('{:.3f}ft'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 2))))
                        elif self.dlg.cb_units.currentText(
                        ) == 'nautical miles':
                            self.dlg.le_total.setText(
                                str('{:.3f}NM'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 3))))
                        elif self.dlg.cb_units.currentText() == 'yards':
                            self.dlg.le_total.setText(
                                str('{:.3f}yd'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 4))))
                        elif self.dlg.cb_units.currentText() == 'miles':
                            self.dlg.le_total.setText(
                                str('{:.3f}mi'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 5))))
                        elif self.dlg.cb_units.currentText() == 'centimeters':
                            self.dlg.le_total.setText(
                                str('{:.3f}cm'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 7))))
                        elif self.dlg.cb_units.currentText() == 'millimeters':
                            self.dlg.le_total.setText(
                                str('{:.3f}mm'.format(
                                    self.convert_planar_length(
                                        total_length_proj, l_units, 8))))
                    else:  # degree units
                        self.dlg.cb_units.clear()
                        self.dlg.cb_units.setEnabled(False)
                        self.dlg.le_total.setText(
                            str('{:.3f}{}'.format(
                                total_length_proj,
                                self.Distance_Units[l_units])))

            elif layer.geometryType() == 2:  # Polygons
                self.dlg.setWindowTitle(
                    'Measuring {} selected features from layer: {} - {} ({})'.
                    format(layer.selectedFeatureCount(), layer.name(),
                           epsg_code, crs_type))
                self.dlg.lbl_1.setText('Total area of selected features: ')
                if layer.crs().isGeographic() or (
                        not layer.crs().isGeographic()
                        and self.dlg.rad_1.isChecked()):
                    total_geo_m = sum(
                        [self.geodetic_area(f) for f in select_fts])
                    if self.dlg.cb_units.currentText() == 'square meters':
                        self.dlg.le_total.setText(
                            str('{:.3f}m2'.format(total_geo_m)))
                    elif self.dlg.cb_units.currentText(
                    ) == 'square kilometers':
                        total_geo_km = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareKilometers)
                        self.dlg.le_total.setText(
                            str('{:.3f}km2'.format(total_geo_km)))
                    elif self.dlg.cb_units.currentText() == 'square feet':
                        total_geo_ft = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareFeet)
                        self.dlg.le_total.setText(
                            str('{:.3f}ft2'.format(total_geo_ft)))
                    elif self.dlg.cb_units.currentText() == 'square yards':
                        total_geo_yds = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareYards)
                        self.dlg.le_total.setText(
                            str('{:.3f}yd2'.format(total_geo_yds)))
                    elif self.dlg.cb_units.currentText() == 'square miles':
                        total_geo_mi = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareMiles)
                        self.dlg.le_total.setText(
                            str('{:.3f}mi2'.format(total_geo_mi)))
                    elif self.dlg.cb_units.currentText() == 'hectares':
                        total_geo_ha = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaHectares)
                        self.dlg.le_total.setText(
                            str('{:.3f}ha'.format(total_geo_ha)))
                    elif self.dlg.cb_units.currentText() == 'acres':
                        total_geo_ac = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaAcres)
                        self.dlg.le_total.setText(
                            str('{:.3f}ac'.format(total_geo_ac)))
                    elif self.dlg.cb_units.currentText(
                    ) == 'square nautical miles':
                        total_geo_nm = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareNauticalMiles)
                        self.dlg.le_total.setText(
                            str('{:.3f}NM2'.format(total_geo_nm)))
                    elif self.dlg.cb_units.currentText() == 'square degrees':
                        total_geo_deg = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareDegrees)
                        self.dlg.le_total.setText(
                            str('{:.3f}deg2'.format(total_geo_deg)))
                    elif self.dlg.cb_units.currentText(
                    ) == 'square centimeters':
                        total_geo_cm = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareCentimeters)
                        self.dlg.le_total.setText(
                            str('{:.3f}cm2'.format(total_geo_cm)))
                    elif self.dlg.cb_units.currentText(
                    ) == 'square millimeters':
                        total_geo_mm = self.da.convertAreaMeasurement(
                            total_geo_m, QgsUnitTypes.AreaSquareMillimeters)
                        self.dlg.le_total.setText(
                            str('{:.3f}mm2'.format(total_geo_mm)))

                else:  # projected CRS
                    total_area_proj = sum(
                        [self.planar_area(f) for f in select_fts])
                    if l_units != 6:  # Units are NOT degrees
                        if self.dlg.cb_units.currentText() == 'square meters':
                            self.dlg.le_total.setText(
                                str('{:.3f}m2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square meters'))))
                        elif self.dlg.cb_units.currentText(
                        ) == 'square kilometers':
                            self.dlg.le_total.setText(
                                str('{:.3f}km2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square kilometers'))))
                        elif self.dlg.cb_units.currentText() == 'square feet':
                            self.dlg.le_total.setText(
                                str('{:.3f}ft2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square feet'))))
                        elif self.dlg.cb_units.currentText() == 'square yards':
                            self.dlg.le_total.setText(
                                str('{:.3f}yd2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square yards'))))
                        elif self.dlg.cb_units.currentText() == 'square miles':
                            self.dlg.le_total.setText(
                                str('{:.3f}mi2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square miles'))))
                        elif self.dlg.cb_units.currentText() == 'hectares':
                            self.dlg.le_total.setText(
                                str('{:.3f}ha'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'hectares'))))
                        elif self.dlg.cb_units.currentText() == 'acres':
                            self.dlg.le_total.setText(
                                str('{:.3f}ac'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units, 'acres'))))
                        elif self.dlg.cb_units.currentText(
                        ) == 'square nautical miles':
                            self.dlg.le_total.setText(
                                str('{:.3f}NM2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square nautical miles'))))
                        elif self.dlg.cb_units.currentText(
                        ) == 'square centimeters':
                            self.dlg.le_total.setText(
                                str('{:.3f}cm2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square centimeters'))))
                        elif self.dlg.cb_units.currentText(
                        ) == 'square millimeters':
                            self.dlg.le_total.setText(
                                str('{:.3f}mm2'.format(
                                    self.convert_planar_area(
                                        total_area_proj, l_units,
                                        'square millimeters'))))
                    else:  # Degree units
                        self.dlg.cb_units.clear()
                        if self.dlg.cb_units.isEnabled():
                            self.dlg.cb_units.setEnabled(False)
                        self.dlg.le_total.setText(
                            str('{:.3f}{}2'.format(
                                total_area_proj,
                                self.Distance_Units[l_units])))

            if layer.geometryType() in [3, 4]:
                self.iface.messageBar().pushMessage(
                    'Layer has unknown or Null geometry type', duration=2)

###########################UNIT CONVERSIONS FOR PROJECTED CRS'S#####################################

    def convert_planar_length(self, length, input_units, output_units):
        if input_units == 0:  # Meters
            if output_units == 0:  # Meters
                result = length
            elif output_units == 1:  # Kilometers
                result = length / 1000
            elif output_units == 2:  # Imperial feet
                result = length * 3.28084
            elif output_units == 3:  # Nautical miles
                result = length / 1852
            elif output_units == 4:  # Imperial yards
                result = length * 1.09361
            elif output_units == 5:  # Terrestrial miles
                result = length / 1609.344
            elif output_units == 7:  # Centimeters
                result = length * 100
            elif output_units == 8:  # Millimeters
                result = length * 1000
        elif input_units == 1:  # Kilometers
            if output_units == 0:  # Meters
                result = length * 1000
            elif output_units == 1:  # Kilometers
                result = length
            elif output_units == 2:  # Imperial feet
                result = length * 3280.84
            elif output_units == 3:  # Nautical miles
                result = length / 1.852
            elif output_units == 4:  # Imperial yards
                result = length * 1093.61
            elif output_units == 5:  # Terrestrial miles
                result = length / 1.609
            elif output_units == 7:  # Centimeters
                result = length * 100000
            elif output_units == 8:  # Millimeters
                result = length * 1000000
        elif input_units == 2:  # Imperial feet
            if output_units == 0:  # Meters
                result = length / 3.281
            elif output_units == 1:  # Kilometers
                result = length / 3281
            elif output_units == 2:  # Imperial feet
                result = length
            elif output_units == 3:  # Nautical Miles
                result = length / 6076
            elif output_units == 4:  # Imperial yards
                result = length / 3
            elif output_units == 5:  # Terrestrial miles
                result = length / 5280
            elif output_units == 7:  # Centimeters
                result = length * 30.48
            elif output_units == 8:  # Millimeters
                result = length * 304.8
        elif input_units == 3:  # Nautical miles
            if output_units == 0:  # Meters
                result = length * 1852
            if output_units == 1:  # Kilometers
                result = length * 1.852
            elif output_units == 2:  # Imperial feet
                result = length * 6076
            elif output_units == 3:  # Nautical miles
                result = length
            elif output_units == 4:  # Imperial yards
                result = length * 2025.37
            elif output_units == 5:  # Terrestrial miles
                result = length * 1.15078
            elif output_units == 7:  # Centimeters
                result = length * 185200
            elif output_units == 8:  # Millimeters
                result = length * 1852000
        elif input_units == 4:  # Imperial yards
            if output_units == 0:  # Meters
                result = length / 1.094
            elif output_units == 1:  # Kilometers
                result = length / 1094
            elif output_units == 2:  # Imperial feet
                result = length * 3
            elif output_units == 3:  # Nautical miles
                result = length / 2025
            elif output_units == 4:  # Imperial yards
                result = length
            elif output_units == 5:  # Terrestrial miles
                result = length / 1760
            elif output_units == 7:  # Centimeters
                result = length * 91.44
            elif output_units == 8:  # Millimeters
                result = length * 914.4
        elif input_units == 5:  # Terrestrial miles
            if output_units == 0:  # Meters
                result = length * 1609.34
            elif output_units == 1:  # Kilometers
                result = length * 1.609
            elif output_units == 2:  # Imperial feet
                result = length * 5280
            elif output_units == 3:  # Nautical miles
                result = length / 1.151
            elif output_units == 4:  # Imperial yards
                result = length * 1760
            elif output_units == 5:  # Terrestrial miles
                result = length
            elif output_units == 7:  # Centimeters
                result = length * 160934
            elif output_units == 8:  # Millimeters
                result = length * 1609340
        elif input_units == 7:  # Centimeters
            if output_units == 0:  # Meters
                result = length / 100
            elif output_units == 1:  # Kilometers
                result = length / 100000
            elif output_units == 2:  # Imperial feet
                result = length / 30.48
            elif output_units == 3:  # Nautical miles
                result = length / 185200
            elif output_units == 4:  # Imperial yards
                result = length / 91.44
            elif output_units == 5:  # Terrestrial miles
                result = length / 160934
            elif output_units == 7:  # Centimeters
                result = length
            elif output_units == 8:  # Millimeters
                result = length * 10
        elif input_units == 8:  # Millimeters
            if output_units == 0:  # Meters
                result = length / 1000
            elif output_units == 1:  # Kilometers
                result = length / 1000000
            elif output_units == 2:  # Imperial feet
                result = length / 305
            elif output_units == 3:  # Nautical miles
                result = length / 1852000
            elif output_units == 4:  # Imperial yards
                result = length / 914
            elif output_units == 5:  # Terrestrial miles
                result = length / 1609000
            elif output_units == 7:  # Centimeters
                result = length / 10
            elif output_units == 8:  # Millimeters
                result = length

        return result

#####################################AREA UNITS#####################################################

    def convert_planar_area(self, area, input_units, output_units):
        if input_units == 0:  # Meters
            if output_units == 'square meters':  # Square meters
                result = area
            elif output_units == 'square kilometers':  # Square kilometers
                result = area / 1000000
            elif output_units == 'square feet':  # Square feet
                result = area * 10.764
            elif output_units == 'square yards':  # Square yards
                result = area * 1.196
            elif output_units == 'square miles':  # Square miles
                result = area / 2589988.1
            elif output_units == 'hectares':  # Hectares
                result = area / 10000
            elif output_units == 'acres':  # Acres
                result = area / 4047
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 3429904
            elif output_units == 'square centimeters':  # Square centimeters
                result = area * 10000
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 1000000

    #--------------------------------------------------------------------

        elif input_units == 1:  # Kilometers
            if output_units == 'square meters':  # Square meters
                result = area * 10000
            elif output_units == 'square kilometers':  # Square kilometers
                result = area
            elif output_units == 'square feet':  # Square feet
                result = area * 10763910.417
            elif output_units == 'square yards':  # Square yards
                result = area * 1195990.05
            elif output_units == 'square miles':  # Square miles
                result = area / 2.59
            elif output_units == 'hectares':  # Hectares
                result = area * 100
            elif output_units == 'acres':  # Acres
                result = area * 247.105
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 3.43
            elif output_units == 'square centimeters':  # Square centimeters
                result = area * 10000000000
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 1000000000000

    #--------------------------------------------------------------------

        elif input_units == 2:  # Imperial feet
            if output_units == 'square meters':  # Square meters
                result = area / 10.764
            elif output_units == 'square kilometers':  # Square kilometers
                result = area / 10763910.417
            elif output_units == 'square feet':  # Square feet
                result = area
            elif output_units == 'square yards':  # Square yards
                result = area / 9
            elif output_units == 'square miles':  # Square miles
                result = area / 27878400
            elif output_units == 'hectares':  # Hectares
                result = area / 107639
            elif output_units == 'acres':  # Acres
                result = area / 43560
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 36920000
            elif output_units == 'square centimeters':  # Square centimeters
                result = area * 929
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 92903

    #--------------------------------------------------------------------

        elif input_units == 3:  # Nautical miles
            if output_units == 'square meters':  # Square meters
                result = area * 3430000
            elif output_units == 'square kilometers':  # Square kilometers
                result = area * 3.43
            elif output_units == 'square feet':  # Square feet
                result = area * 36920000
            elif output_units == 'square yards':  # Square yards
                result = area * 4102000
            elif output_units == 'square miles':  # Square miles
                result = area * 1.324
            elif output_units == 'hectares':  # Hectares
                result = area * 343
            elif output_units == 'acres':  # Acres
                result = area * 847.548
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area
            elif output_units == 'square centimeters':  # Square centimeters
                result = area * 34300000000
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 3430000000000

    #--------------------------------------------------------------------

        elif input_units == 4:  # Imperial yards
            if output_units == 'square meters':  # Square meters
                result = area / 1.196
            elif output_units == 'square kilometers':  # Square kilometers
                result = area / 1196000
            elif output_units == 'square feet':  # Square feet
                result = area * 9
            elif output_units == 'square yards':  # Square yards
                result = area
            elif output_units == 'square miles':  # Square miles
                result = area / 3098000
            elif output_units == 'hectares':  # Hectares
                result = area / 11960
            elif output_units == 'acres':  # Acres
                result = area / 4840
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 4102000
            elif output_units == 'square centimeters':  # Square centimeters
                result = area * 8361
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 836127

    #--------------------------------------------------------------------

        elif input_units == 5:  # Terrestrial miles
            if output_units == 'square meters':  # Square meters
                result = area * 2590000
            elif output_units == 'square kilometers':  # Square kilometers
                result = area * 2.59
            elif output_units == 'square feet':  # Square feet
                result = area * 27880000
            elif output_units == 'square yards':  # Square yards
                result = area * 3098000
            elif output_units == 'square miles':  # Square miles
                result = area
            elif output_units == 'hectares':  # Hectares
                result = area * 259
            elif output_units == 'acres':  # Acres
                result = length * 640
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 1.324
            elif output_units == 'square centimeters':  # Square centimeters
                result = area * 25900000000
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 2590000000000

    #--------------------------------------------------------------------

        elif input_units == 7:  # Centimeters
            if output_units == 'square meters':  # Square meters
                result = area / 10000
            elif output_units == 'square kilometers':  # Square kilometers
                result = area / 10000000000
            elif output_units == 'square feet':  # Square feet
                result = area / 929.03
            elif output_units == 'square yards':  # Square yards
                result = area / 8361.27
            elif output_units == 'square miles':  # Square miles
                result = area / 25899881103.36
            elif output_units == 'hectares':  # Hectares
                result = area / 100000000
            elif output_units == 'acres':  # Acres
                result = area / 40468564.224
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 34299040000
            elif output_units == 'square centimeters':  # Square centimeters
                result = area
            elif output_units == 'square millimeters':  # Square millimeters
                result = area * 100

    #--------------------------------------------------------------------

        elif input_units == 8:  # Millimeters
            if output_units == 'square meters':  # Square meters
                result = area / 1000000
            elif output_units == 'square kilometers':  # Square kilometers
                result = area / 1000000000000
            elif output_units == 'square feet':  # Square feet
                result = area / 92903
            elif output_units == 'square yards':  # Square yards
                result = area / 836127
            elif output_units == 'square miles':  # Square miles
                result = area / 2589988110336
            elif output_units == 'hectares':  # Hectares
                result = area / 10000000000
            elif output_units == 'acres':  # Acres
                result = area / 4046856422
            elif output_units == 'square nautical miles':  # Square Nautical miles
                result = area / 3429904000000
            elif output_units == 'square centimeters':  # Square centimeters
                result = area / 100
            elif output_units == 'square millimeters':  # Square millimeters
                result = area

        return result

####################################################################################################

    def dockwidget_closed(self):
        #        print('dockwidget closed!!')
        self.dlg.setFloating(False)
        if self.layer is not None:
            self.tool_reset(self.layer)
            if isinstance(self.layer, QgsVectorLayer):
                self.layer.selectionChanged.disconnect(self.total_length)
        self.iface.currentLayerChanged.disconnect(self.active_changed)
        #####25-05-21
        self.action.setEnabled(True)

    def unload(self):
        self.toolbar.removeAction(self.action)
        del self.action
class MorphALPolygonPerimeterArea(PTM4QgisAlgorithm):
    INPUT = 'INPUT'
    METHOD = 'CALC_METHOD'
    OUTPUT = 'OUTPUT'

    def help(self):
        # TODO improve help text
        return self.tr('Compute the perimeters and areas of a layer of polygons')

    def group(self):
        return self.tr('MorphAL')

    def groupId(self):
        return 'morphal'

    def __init__(self):
        super().__init__()
        self.export_z = False
        self.export_m = False
        self.distance_area = None
        self.calc_methods = [self.tr('Layer CRS'),
                             self.tr('Project CRS'),
                             self.tr('Ellipsoidal')]

    def initAlgorithm(self, config):
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Input layer'),
                types=[QgsProcessing.TypeVectorPolygon]
            )
        )
        self.addParameter(
            QgsProcessingParameterEnum(
                self.METHOD,
                self.tr('Calculate using'),
                options=self.calc_methods,
                defaultValue=0
            )
        )
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Layer with added perimeters and areas')
            )
        )

    def name(self):
        return 'morphalpolygonperimeterarea'

    def displayName(self):
        # TODO IMPROVE TEXT
        return self.tr('Compute the perimeters and areas of a layer of polygons')

    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()

        if QgsWkbTypes.geometryType(wkb_type) != QgsWkbTypes.PolygonGeometry:
            # TODO IMPROVE FEEDBACK
            feedback.reportError('The layer geometry type is different from a polygon')
            return {}

        fields = source.fields()

        new_fields = QgsFields()
        new_fields.append(QgsField('perimeter', QVariant.Double))
        new_fields.append(QgsField('area', QVariant.Double))

        fields = QgsProcessingUtils.combineFields(fields, new_fields)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, wkb_type, source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext())
            self.distance_area.setEllipsoid(context.ellipsoid())
        elif method == 1:
            if not context.project():
                raise QgsProcessingException(self.tr('No project is available in this context'))
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                attrs.extend(self.polygon_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}

    def polygon_attributes(self, geometry):
        perimeter = self.distance_area.measurePerimeter(geometry)
        area = self.distance_area.measureArea(geometry)
        return [perimeter, area]
Ejemplo n.º 13
0
    def processAlgorithm(self, parameters, context, feedback):
        # get input variables
        raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT,
                                                   context)
        band_number = self.parameterAsInt(parameters, self.BAND, context)
        output = self.parameterAsFileOutput(parameters, self.OUTPUT, context)

        # layer name
        name = raster_layer.name()

        # layer provider
        provider = raster_layer.dataProvider()

        # get CRS
        crs_raster = raster_layer.crs()

        # set project ellipsoid (for measurements) to CRS ellipsoid
        ellipsoid = context.project().crs().ellipsoidAcronym()

        # get transform context from project
        trans_context = context.project().transformContext()

        # 5% done
        feedback.setProgress(5)

        # Initialize Area calculator class with ellipsoid
        da = QgsDistanceArea()
        da.setSourceCrs(crs_raster, trans_context)
        da.setEllipsoid(ellipsoid)

        # get raster extent
        extent = raster_layer.extent()
        extent = QgsGeometry().fromRect(extent)

        # 20% done
        feedback.setProgress(20)

        # get area of extent
        feedback.pushConsoleInfo(
            self.tr(f'Measuring area of raster rectangle...'))
        area = da.measureArea(extent)

        # convert area from
        area_m2 = da.convertAreaMeasurement(area,
                                            QgsUnitTypes.AreaSquareMeters)

        # 30% done
        feedback.setProgress(30)

        # check if NoData value is set
        if provider.sourceHasNoDataValue(band_number):
            feedback.pushConsoleInfo(
                self.tr(f'Calculating NoData percentage...'))
            # unique values parameters
            rastervalue_params = {'INPUT': raster_layer, 'BAND': band_number}

            # run unique values
            result = processing.run('native:rasterlayeruniquevaluesreport',
                                    rastervalue_params)

            # get total pixel and nodata count
            cells = result['TOTAL_PIXEL_COUNT']
            nodata_cells = result['NODATA_PIXEL_COUNT']

            # calculate nodata percentage
            nodata_percentage = nodata_cells / cells

            # calclate data coverage
            feedback.pushConsoleInfo(
                self.tr(f'Calculating data coverage...\n'))
            coverage_m2 = area_m2 * (1 - nodata_percentage)
            coverage_percentage = (1 - nodata_percentage)
        else:
            feedback.reportError(self.tr(
                'Missing NoData value(s) detected. Check settings of the raster layer!'
            ),
                                 fatalError=False)
            coverage_m2 = area_m2
            coverage_percentage = 1.0

        # 80% done
        feedback.setProgress(80)

        # calculate area units
        area_km2 = area_m2 / (1000 * 1000)
        coverage_km2 = coverage_m2 / (1000 * 1000)

        feedback.pushConsoleInfo(
            self.tr(f'------------------------------------------\n'))

        feedback.pushConsoleInfo(
            self.tr(f'Raster Coverage of Layer [ {name} ]:\n'))

        feedback.pushConsoleInfo(
            self.tr(f'Raster Area [km2] ....... : {round(area_km2,3)}'))
        feedback.pushConsoleInfo(
            self.tr(f'Data Coverage [km2] ..... : {round(coverage_km2,3)}'))
        feedback.pushConsoleInfo(
            self.tr(f'Data Coverage [m2] ...... : {round(coverage_m2,2)}'))
        feedback.pushConsoleInfo(
            self.
            tr(f'Data Coverage [%] ....... : {round(coverage_percentage * 100,2)}\n'
               ))

        feedback.pushConsoleInfo(
            self.
            tr(f'This is {round(coverage_km2 / self.bremen_area,2)} times the area of Bremen\n'
               ))

        feedback.pushConsoleInfo(
            self.tr(f'------------------------------------------\n'))

        # 100% done
        feedback.setProgress(100)
        feedback.pushInfo(
            self.tr(
                f'{utils.return_success()}! Raster area has been calculated!\n'
            ))

        result = {
            self.RASTER_AREA_KM2: area_km2,
            self.DATA_COVERAGE_KM2: coverage_km2,
            self.DATA_COVERAGE_M2: coverage_m2,
            self.DATA_COVERAGE_PERCENT: coverage_percentage,
            self.OUTPUT: output
        }

        if output != '':
            self.write_output(name, result, output)

        return result
Ejemplo n.º 14
0
    def generateFootprintsForFilmOblique(self):
        self.reloadFpLayer()
        self.reloadCpLayer()

        caps = self.fpLayer.dataProvider().capabilities()
        if caps & QgsVectorDataProvider.AddFeatures:
            if self.cpLayer.dataProvider().featureCount() > 0:
                iter = self.cpLayer.getFeatures()
                existingFootpints = QgsVectorLayerUtils.getValues(
                    self.fpLayer, "bildnummer")[0]
                cpFt = QgsFeature()
                fpFts = []
                #iterate over points from CP Layer > LON, LAT
                while iter.nextFeature(cpFt):
                    if cpFt['bildnummer'] in existingFootpints:
                        #QMessageBox.warning(None, u"Bild Nummern", u"Footprint für das Bild mit der Nummer {0} wurde bereits erstellt.".format(ft['BILD']))
                        continue
                    cp = cpFt.geometry()
                    cpMetric = QgsGeometry(cp)
                    destCrs = QgsCoordinateReferenceSystem()
                    destCrs.createFromProj4(self.Proj4Utm(cp.asPoint()))
                    coordTransformF = QgsCoordinateTransform(
                        self.cpLayer.crs(), destCrs, QgsProject.instance())
                    coordTransformB = QgsCoordinateTransform(
                        destCrs, self.cpLayer.crs(), QgsProject.instance())
                    cpMetric.transform(coordTransformF)
                    if cpFt['radius'] == '':
                        r = 175
                    else:
                        r = float(cpFt['radius'])
                    fpMetric = QgsGeometry(cpMetric.buffer(r, 18))
                    fp = QgsGeometry(fpMetric)
                    fp.transform(coordTransformB)

                    fpFt = QgsFeature(self.fpLayer.fields())
                    fpFt.setGeometry(fp)
                    fpFt.setAttribute("bildnummer", cpFt["bildnummer"])
                    fpFt.setAttribute("filmnummer", cpFt["filmnummer"])
                    da = QgsDistanceArea()
                    da.setEllipsoid(self.fpLayer.crs().ellipsoidAcronym())
                    fpFt.setAttribute('shape_length', da.measurePerimeter(fp))
                    fpFt.setAttribute('shape_area', da.measureArea(fp))
                    fpFts.append(fpFt)

                (res,
                 outFeats) = self.fpLayer.dataProvider().addFeatures(fpFts)
                self.fpLayer.updateExtents()
                if self.canvas.isCachingEnabled():
                    self.fpLayer.triggerRepaint()
                else:
                    self.canvas.refresh()
            else:
                QMessageBox.warning(
                    None, "Keine Bildmittelpunkte",
                    "Keine Bildmittelpunkte für den Film {0} vorhanden.".
                    format(self.currentFilmNumber))
        else:
            QMessageBox.warning(
                None, "Layer Capabilities",
                "AddFeature is not enabled ({0})".format(
                    self.fpLayer.dataProvider().capabilitiesString()))
Ejemplo n.º 15
0
    def generateFootprintsForFilmVertical(self):
        self.reloadFpLayer()
        self.reloadCpLayer()

        # Error wenn nur ein punkt vorhanden
        if self.cpLayer.featureCount() > 1:
            caps = self.fpLayer.dataProvider().capabilities()
            if caps & QgsVectorDataProvider.AddFeatures:
                #Get FORM1 from FilmInfoDict
                f1 = self.currentFilmInfoDict["form1"]  # Image height
                f2 = self.currentFilmInfoDict["form2"]  # Image width

                iterFeatures = self.cpLayer.getFeatures()
                iterNext = self.cpLayer.getFeatures()
                existingFootpints = QgsVectorLayerUtils.getValues(
                    self.fpLayer, "bildnummer")[0]
                ft = QgsFeature()
                ftNext = QgsFeature()
                iterNext.nextFeature(ftNext)
                fpFeats = []
                kappasToUpdate = {}
                # iterate over points from CP Layer > LON, LAT
                i = 0
                while iterFeatures.nextFeature(ft):
                    i += 1
                    iterNext.nextFeature(ftNext)
                    p = QgsPointXY(ft.geometry().asPoint())
                    if ft['bildnummer'] in existingFootpints:
                        pPrevGeom = QgsGeometry(ft.geometry())
                        #QMessageBox.warning(None, u"Bild Nummern", u"Footprint für das Bild mit der Nummer {0} wurde bereits erstellt.".format(ft['BILD']))
                        continue
                    if i == 1:
                        pPrevGeom = QgsGeometry(ftNext.geometry())
                    #if iterNext.isClosed():
                    #    #use pPrev as pNext
                    #    pNext = QgsPoint(pPrev)
                    #else:
                    #    pNext = QgsPoint(ftNext.geometry().asPoint())

                    #kappa = p.azimuth(pPrev)

                    #kappa = p.azimuth(pNext)

                    # d = math.sqrt(2*((f1/2 * ft['MASS']/1000)**2))
                    d1 = f1 / 2 * ft['massstab'] / 1000
                    d2 = f2 / 2 * ft['massstab'] / 1000
                    #QMessageBox.warning(None, u"Bild Nummern", "{0}".format(d))

                    calcCrs = QgsCoordinateReferenceSystem()
                    calcCrs.createFromProj4(self.Proj4Utm(p))
                    ctF = QgsCoordinateTransform(self.cpLayer.crs(), calcCrs,
                                                 QgsProject.instance())

                    cpMetric = QgsGeometry(ft.geometry())
                    cpMetric.transform(ctF)
                    pPrevGeom.transform(ctF)
                    pMetric = QgsPointXY(cpMetric.asPoint())
                    pPrevMetric = QgsPointXY(pPrevGeom.asPoint())
                    kappaMetric = pMetric.azimuth(pPrevMetric)
                    pPrevGeom = QgsGeometry(ft.geometry())
                    left = pMetric.x() - d2
                    bottom = pMetric.y() - d1
                    right = pMetric.x() + d2
                    top = pMetric.y() + d1

                    #R = 6371
                    #D = (d/1000)
                    #cpLat = math.radians(p.y())
                    #cpLon = math.radians(p.x())
                    #urLat = math.asin( math.sin(cpLat)*math.cos(D/R) + math.cos(cpLat)*math.sin(D/R)*math.cos(urBrng) )
                    #urLon = cpLon + math.atan2(math.sin(urBrng)*math.sin(D/R)*math.cos(cpLat), math.cos(D/R)-math.sin(cpLat)*math.sin(urLat))

                    #top = math.asin( math.sin(cpLat)*math.cos(D/R) + math.cos(cpLat)*math.sin(D/R) )
                    #bottom = math.asin( math.sin(cpLat)*math.cos(D/R) + math.cos(cpLat)*math.sin(D/R)*-1 )

                    #lat = math.asin( math.sin(cpLat)*math.cos(D/R) )
                    #right = cpLon + math.atan2(math.sin(D/R)*math.cos(cpLat), math.cos(D/R)-math.sin(cpLat)*math.sin(lat))
                    #left = cpLon + math.atan2(-1*math.sin(D/R)*math.cos(cpLat), math.cos(D/R)-math.sin(cpLat)*math.sin(lat))

                    #QMessageBox.warning(None, u"Bild Nummern", "{0}, {1}, {2}, {3}".format(math.degrees(top), math.degrees(bottom), math.degrees(left), math.degrees(right)))

                    #rect = QgsRectangle(math.degrees(left), math.degrees(bottom), math.degrees(right), math.degrees(top))
                    #l = math.degrees(left)
                    #b = math.degrees(bottom)
                    #r = math.degrees(right)
                    #t = math.degrees(top)
                    p1 = QgsGeometry.fromPointXY(QgsPointXY(left, bottom))
                    p2 = QgsGeometry.fromPointXY(QgsPointXY(right, bottom))
                    p3 = QgsGeometry.fromPointXY(QgsPointXY(right, top))
                    p4 = QgsGeometry.fromPointXY(QgsPointXY(left, top))
                    #p1.rotate(kappa+90, p)
                    #p2.rotate(kappa+90, p)
                    #p3.rotate(kappa+90, p)
                    #p4.rotate(kappa+90, p)
                    pol = [[
                        p1.asPoint(),
                        p2.asPoint(),
                        p3.asPoint(),
                        p4.asPoint()
                    ]]
                    geom = QgsGeometry.fromPolygonXY(pol)
                    geom.rotate(kappaMetric, pMetric)
                    #Transform to DestinationCRS
                    ctB = QgsCoordinateTransform(calcCrs, self.fpLayer.crs(),
                                                 QgsProject.instance())
                    geom.transform(ctB)

                    feat = QgsFeature(self.fpLayer.fields())
                    feat.setGeometry(geom)
                    feat.setAttribute('filmnummer', self.currentFilmNumber)
                    feat.setAttribute('bildnummer', ft['bildnummer'])
                    da = QgsDistanceArea()
                    da.setEllipsoid(self.fpLayer.crs().ellipsoidAcronym())
                    feat.setAttribute('shape_length',
                                      da.measurePerimeter(geom))
                    feat.setAttribute('shape_area', da.measureArea(geom))
                    fpFeats.append(feat)

                    # update Kappa in cpLayer
                    kappasToUpdate[ft.id()] = {
                        ft.fieldNameIndex('kappa'): kappaMetric
                    }

                iterFeatures.close()
                iterNext.close()

                resCAVs = self.cpLayer.dataProvider().changeAttributeValues(
                    kappasToUpdate)
                QgsMessageLog.logMessage(
                    f"Kappa Update for {kappasToUpdate}, Success: {resCAVs}",
                    tag="APIS",
                    level=Qgis.Success if resCAVs else Qgis.Critical)

                (res,
                 outFeats) = self.fpLayer.dataProvider().addFeatures(fpFeats)

                self.fpLayer.updateExtents()
                if self.canvas.isCachingEnabled():
                    self.fpLayer.triggerRepaint()
                else:
                    self.canvas.refresh()
            else:
                #Caps
                QMessageBox.warning(None, "Layer Capabilities!",
                                    "Layer Capabilities!")
        else:
            #small feature count
            QMessageBox.warning(
                None, "Footprints",
                "Zum Berechnen der senkrecht Footprint müssen mindestens zwei Bilder kartiert werden!"
            )
Ejemplo n.º 16
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
        minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE,
                                             context)

        expression = QgsExpression(
            self.parameterAsString(parameters, self.EXPRESSION, context))
        if expression.hasParserError():
            raise ProcessingException(expression.parserErrorString())

        expressionContext = self.createExpressionContext(parameters, context)
        if not expression.prepare(expressionContext):
            raise ProcessingException(
                self.tr('Evaluation error: {0}').format(
                    expression.evalErrorString()))

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               QgsWkbTypes.Point,
                                               source.sourceCrs())

        da = QgsDistanceArea()
        da.setSourceCrs(source.sourceCrs())
        da.setEllipsoid(context.project().ellipsoid())

        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(source.getFeatures()):
            if feedback.isCanceled():
                break

            expressionContext.setFeature(f)
            value = expression.evaluate(expressionContext)
            if expression.hasEvalError():
                feedback.pushInfo(
                    self.tr('Evaluation error for feature ID {}: {}').format(
                        f.id(), expression.evalErrorString()))
                continue

            fGeom = f.geometry()
            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(value)
            else:
                pointCount = int(round(value * da.measureArea(fGeom)))

            if pointCount == 0:
                feedback.pushInfo(
                    "Skip feature {} as number of points for it is 0.")
                continue

            index = QgsSpatialIndex()
            points = dict()

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            total = 100.0 / pointCount if pointCount else 1

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                if feedback.isCanceled():
                    break

                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                p = QgsPointXY(rx, ry)
                geom = QgsGeometry.fromPoint(p)
                if geom.within(fGeom) and \
                        vector.checkMinDistance(p, index, minDistance, points):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', nPoints)
                    f.setGeometry(geom)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                    index.insertFeature(f)
                    points[nPoints] = p
                    nPoints += 1
                    feedback.setProgress(int(nPoints * total))
                nIterations += 1

            if nPoints < pointCount:
                feedback.pushInfo(
                    self.tr('Could not generate requested number of random '
                            'points. Maximum number of attempts exceeded.'))

            feedback.setProgress(0)

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, model_feedback):
        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(25, model_feedback)
        results = {}
        outputs = {}

        nlcd_rast_output = self.parameterAsBool(parameters,
                                                "OutputNLCDLandCoverRaster",
                                                context)
        nlcd_vect_output = self.parameterAsBool(parameters,
                                                "OutputNLCDLandCoverVector",
                                                context)
        nlcd_rast_imp_output = self.parameterAsBool(
            parameters, "OutputNLCDImperviousRaster", context)
        soil_output = self.parameterAsBool(parameters, "OutputSoilLayer",
                                           context)
        curve_number_output = self.parameterAsBool(parameters,
                                                   "OutputCurveNumberLayer",
                                                   context)

        # Assiging Default CN_Lookup Table
        if parameters["cnlookup"] == None:
            csv_uri = ("file:///" + os.path.join(cmd_folder, "CN_Lookup.csv") +
                       "?delimiter=,")
            csv = QgsVectorLayer(csv_uri, "CN_Lookup.csv", "delimitedtext")
            parameters["cnlookup"] = csv

        area_layer = self.parameterAsVectorLayer(parameters, "areaboundary",
                                                 context)

        EPSGCode = area_layer.crs().authid()
        origEPSGCode = EPSGCode  # preserve orignal EPSGCode to project back to it
        # feedback.pushInfo(str(EPSGCode))

        if check_crs_acceptable(EPSGCode):
            pass
        else:
            # Reproject layer to EPSG:5070
            alg_params = {
                "INPUT": parameters["areaboundary"],
                "OPERATION": "",
                "TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:5070"),
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReprojectLayer5070"] = processing.run(
                "native:reprojectlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )
            area_layer = context.takeResultLayer(
                outputs["ReprojectLayer5070"]["OUTPUT"])

            EPSGCode = area_layer.crs().authid()

        # Check if area of the extent is less than 100,000 Acres
        d = QgsDistanceArea()
        tr_cont = QgsCoordinateTransformContext()
        d.setSourceCrs(area_layer.crs(), tr_cont)
        # d.setEllipsoid(area_layer.crs().ellipsoidAcronym())
        extent_area = d.measureArea(QgsGeometry().fromRect(
            area_layer.extent()))
        area_acres = d.convertAreaMeasurement(extent_area,
                                              QgsUnitTypes.AreaAcres)

        if area_acres > 500000:
            feedback.reportError(
                f"Area Boundary layer extent area should be less than 500,000 acres.\nArea Boundary layer extent area is {round(area_acres,4):,} acres.\n\nExecution Failed",
                True,
            )
            return results
        elif area_acres > 100000:
            feedback.reportError(
                f"Your Area Boundary layer extent area is {round(area_acres,4):,} acres. The recommended extent area is 100,000 acres or less. If the Algorithm fails, rerun with a smaller input layer.\n",
                False,
            )
        else:
            feedback.pushInfo(
                f"Area Boundary layer extent area is {round(area_acres,4):,} acres\n"
            )

        # Get extent of the area boundary layer
        xmin = area_layer.extent().xMinimum()
        ymin = area_layer.extent().yMinimum()
        xmax = area_layer.extent().xMaximum()
        ymax = area_layer.extent().yMaximum()

        BBOX_width = (xmax - xmin) / 30
        BBOX_height = (ymax - ymin) / 30
        BBOX_width_int = round(BBOX_width)
        BBOX_height_int = round(BBOX_height)

        # NLCD Impervious Raster
        if nlcd_rast_imp_output == True:
            request_URL = f"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Impervious_L48/ows?version=1.3.0&service=WMS&layers=NLCD_2016_Impervious_L48&styles&crs={str(EPSGCode)}&format=image/geotiff&request=GetMap&width={str(BBOX_width_int)}&height={str(BBOX_height_int)}&BBOX={str(xmin)},{str(ymin)},{str(xmax)},{str(ymax)}&"

            # Download NLCD Impervious Raster
            try:
                ping_URL = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Impervious_L48/ows"
                r = requests.head(ping_URL, verify=False)
                r.raise_for_status()

                alg_params = {
                    "URL": request_URL,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcdImp"] = processing.run(
                    "native:filedownloader",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except (QgsProcessingException,
                    requests.exceptions.HTTPError) as e:
                feedback.reportError(
                    f"Error: {str(e)}\n\nError requesting land use data from 'www.mrlc.gov'. Most probably because either their server is down or there is a certification issue.\nThis should be temporary. Try again later.\n",
                    True,
                )
                return results

            feedback.setCurrentStep(1)
            if feedback.isCanceled():
                return {}

            # reproject to original crs
            # Warp (reproject)
            if EPSGCode != origEPSGCode:
                alg_params = {
                    "DATA_TYPE": 0,
                    "EXTRA": "",
                    "INPUT": outputs["DownloadNlcdImp"]["OUTPUT"],
                    "MULTITHREADING": False,
                    "NODATA": None,
                    "OPTIONS": "",
                    "RESAMPLING": 0,
                    "SOURCE_CRS": None,
                    "TARGET_CRS":
                    QgsCoordinateReferenceSystem(str(origEPSGCode)),
                    "TARGET_EXTENT": None,
                    "TARGET_EXTENT_CRS": None,
                    "TARGET_RESOLUTION": None,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcdImp"] = processing.run(
                    "gdal:warpreproject",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            # Set layer style
            alg_params = {
                "INPUT": outputs["DownloadNlcdImp"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "NLCD_Raster_Imp.qml"),
            }

            try:  # for QGIS Version later than 3.12
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForRasterLayer"] = processing.run(
                    "qgis:setstyleforrasterlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            feedback.setCurrentStep(2)
            if feedback.isCanceled():
                return {}

        # NLCD Land Cover Data
        if (curve_number_output == True or nlcd_vect_output == True
                or nlcd_rast_output == True):
            request_URL = f"https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Land_Cover_L48/ows?version=1.3.0&service=WMS&layers=NLCD_2016_Land_Cover_L48&styles&crs={str(EPSGCode)}&format=image/geotiff&request=GetMap&width={str(BBOX_width_int)}&height={str(BBOX_height_int)}&BBOX={str(xmin)},{str(ymin)},{str(xmax)},{str(ymax)}&"

            # Download NLCD
            try:
                ping_URL = "https://www.mrlc.gov/geoserver/mrlc_display/NLCD_2016_Land_Cover_L48/ows"
                r = requests.head(ping_URL, verify=False)
                r.raise_for_status()

                alg_params = {
                    "URL": request_URL,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcd"] = processing.run(
                    "native:filedownloader",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except (QgsProcessingException,
                    requests.exceptions.HTTPError) as e:
                feedback.reportError(
                    f"Error: {str(e)}\n\nError requesting land use data from 'www.mrlc.gov'. Most probably because either their server is down or there is a certification issue.\nThis should be temporary. Try again later.\n",
                    True,
                )
                return results

            feedback.setCurrentStep(3)
            if feedback.isCanceled():
                return {}

            # reproject to original crs
            # Warp (reproject)
            if EPSGCode != origEPSGCode:
                alg_params = {
                    "DATA_TYPE": 0,
                    "EXTRA": "",
                    "INPUT": outputs["DownloadNlcd"]["OUTPUT"],
                    "MULTITHREADING": False,
                    "NODATA": None,
                    "OPTIONS": "",
                    "RESAMPLING": 0,
                    "SOURCE_CRS": None,
                    "TARGET_CRS":
                    QgsCoordinateReferenceSystem(str(origEPSGCode)),
                    "TARGET_EXTENT": None,
                    "TARGET_EXTENT_CRS": None,
                    "TARGET_RESOLUTION": None,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadNlcd"] = processing.run(
                    "gdal:warpreproject",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            # Reclassify by table
            alg_params = {
                "DATA_TYPE":
                5,
                "INPUT_RASTER":
                outputs["DownloadNlcd"]["OUTPUT"],
                "NODATA_FOR_MISSING":
                False,
                "NO_DATA":
                -9999,
                "RANGE_BOUNDARIES":
                0,
                "RASTER_BAND":
                1,
                "TABLE":
                QgsExpression(
                    "'0,1,11,1,2,12,2,3,21,3,4,22,4,5,23,5,6,24,6,7,31,7,8,32,8,9,41,9,10,42,10,11,43,11,12,51,12,13,52,13,14,71,14,15,72,15,16,73,16,17,74,17,18,81,18,19,82,19,20,90,20,21,95'"
                ).evaluate(),
                "OUTPUT":
                QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReclassifyByTable"] = processing.run(
                "native:reclassifybytable",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(4)
            if feedback.isCanceled():
                return {}

            # Set layer style
            alg_params = {
                "INPUT": outputs["ReclassifyByTable"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "NLCD_Raster.qml"),
            }

            try:  # for QGIS Version later than 3.12
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForRasterLayer"] = processing.run(
                    "qgis:setstyleforrasterlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            feedback.setCurrentStep(5)
            if feedback.isCanceled():
                return {}

            if curve_number_output == True or nlcd_vect_output == True:
                # Polygonize (raster to vector)
                alg_params = {
                    "BAND": 1,
                    "EIGHT_CONNECTEDNESS": False,
                    "EXTRA": "",
                    "FIELD": "VALUE",
                    "INPUT": outputs["ReclassifyByTable"]["OUTPUT"],
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["PolygonizeRasterToVector"] = processing.run(
                    "gdal:polygonize",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

                feedback.setCurrentStep(6)
                if feedback.isCanceled():
                    return {}

                # Fix geometries
                alg_params = {
                    "INPUT": outputs["PolygonizeRasterToVector"]["OUTPUT"],
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["FixGeometries"] = processing.run(
                    "native:fixgeometries",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

                feedback.setCurrentStep(7)
                if feedback.isCanceled():
                    return {}

                # Set layer style
                alg_params = {
                    "INPUT": outputs["FixGeometries"]["OUTPUT"],
                    "STYLE": os.path.join(cmd_folder, "NLCD_Vector.qml"),
                }
                try:  # for QGIS Version 3.12 and later
                    outputs["SetLayerStyle"] = processing.run(
                        "native:setlayerstyle",
                        alg_params,
                        context=context,
                        feedback=feedback,
                        is_child_algorithm=True,
                    )
                except:  # for QGIS Version older than 3.12
                    outputs["SetStyleForVectorLayer"] = processing.run(
                        "qgis:setstyleforvectorlayer",
                        alg_params,
                        context=context,
                        feedback=feedback,
                        is_child_algorithm=True,
                    )

                feedback.setCurrentStep(8)
                if feedback.isCanceled():
                    return {}

        # Soil Layer
        if soil_output == True or curve_number_output == True:

            # Reproject layer
            alg_params = {
                "INPUT": parameters["areaboundary"],
                "OPERATION": "",
                "TARGET_CRS": QgsCoordinateReferenceSystem("EPSG:4326"),
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReprojectLayer4326"] = processing.run(
                "native:reprojectlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(9)
            if feedback.isCanceled():
                return {}

            # Get Area Boundary layer extent in EPSG:4326
            area_layer_reprojected = context.takeResultLayer(
                outputs["ReprojectLayer4326"]["OUTPUT"])

            # Download Soil

            try:  # request using post rest

                # create vector layer structure to store data
                feedback.pushInfo("Creating POST request...")
                uri = "Polygon?crs=epsg:4326"
                soil_layer = QgsVectorLayer(uri, "soil layer", "memory")
                provider = soil_layer.dataProvider()
                attributes = []
                attr_dict = [
                    {
                        "name": "musym",
                        "type": "str"
                    },
                    {
                        "name": "muname",
                        "type": "str"
                    },
                    {
                        "name": "mustatus",
                        "type": "str"
                    },
                    {
                        "name": "slopegraddcp",
                        "type": "str"
                    },
                    {
                        "name": "slopegradwta",
                        "type": "str"
                    },
                    {
                        "name": "brockdepmin",
                        "type": "str"
                    },
                    {
                        "name": "wtdepannmin",
                        "type": "str"
                    },
                    {
                        "name": "wtdepaprjunmin",
                        "type": "str"
                    },
                    {
                        "name": "flodfreqdcd",
                        "type": "str"
                    },
                    {
                        "name": "flodfreqmax",
                        "type": "str"
                    },
                    {
                        "name": "pondfreqprs",
                        "type": "str"
                    },
                    {
                        "name": "aws025wta",
                        "type": "str"
                    },
                    {
                        "name": "aws050wta",
                        "type": "str"
                    },
                    {
                        "name": "aws0100wta",
                        "type": "str"
                    },
                    {
                        "name": "aws0150wta",
                        "type": "str"
                    },
                    {
                        "name": "drclassdcd",
                        "type": "str"
                    },
                    {
                        "name": "drclasswettest",
                        "type": "str"
                    },
                    {
                        "name": "hydgrpdcd",
                        "type": "str"
                    },
                    {
                        "name": "iccdcd",
                        "type": "str"
                    },
                    {
                        "name": "iccdcdpct",
                        "type": "str"
                    },
                    {
                        "name": "niccdcd",
                        "type": "str"
                    },
                    {
                        "name": "niccdcdpct",
                        "type": "str"
                    },
                    {
                        "name": "engdwobdcd",
                        "type": "str"
                    },
                    {
                        "name": "engdwbdcd",
                        "type": "str"
                    },
                    {
                        "name": "engdwbll",
                        "type": "str"
                    },
                    {
                        "name": "engdwbml",
                        "type": "str"
                    },
                    {
                        "name": "engstafdcd",
                        "type": "str"
                    },
                    {
                        "name": "engstafll",
                        "type": "str"
                    },
                    {
                        "name": "engstafml",
                        "type": "str"
                    },
                    {
                        "name": "engsldcd",
                        "type": "str"
                    },
                    {
                        "name": "engsldcp",
                        "type": "str"
                    },
                    {
                        "name": "englrsdcd",
                        "type": "str"
                    },
                    {
                        "name": "engcmssdcd",
                        "type": "str"
                    },
                    {
                        "name": "engcmssmp",
                        "type": "str"
                    },
                    {
                        "name": "urbrecptdcd",
                        "type": "str"
                    },
                    {
                        "name": "urbrecptwta",
                        "type": "str"
                    },
                    {
                        "name": "forpehrtdcp",
                        "type": "str"
                    },
                    {
                        "name": "hydclprs",
                        "type": "str"
                    },
                    {
                        "name": "awmmfpwwta",
                        "type": "str"
                    },
                    {
                        "name": "mukey",
                        "type": "str"
                    },
                    {
                        "name": "mupolygonkey",
                        "type": "str"
                    },
                    {
                        "name": "areasymbol",
                        "type": "str"
                    },
                    {
                        "name": "nationalmusym",
                        "type": "str"
                    },
                ]

                # initialize fields
                for field in attr_dict:
                    attributes.append(QgsField(field["name"], QVariant.String))
                    provider.addAttributes(attributes)
                    soil_layer.updateFields()

                # get area layer extent polygon as WKT in 4326
                aoi_reproj_wkt = area_layer_reprojected.extent().asWktPolygon()

                # send post request
                body = {
                    "format":
                    "JSON",
                    "query":
                    f"select Ma.*, M.mupolygonkey, M.areasymbol, M.nationalmusym, M.mupolygongeo from mupolygon M, muaggatt Ma where M.mupolygonkey in (select * from SDA_Get_Mupolygonkey_from_intersection_with_WktWgs84('{aoi_reproj_wkt.lower()}')) and M.mukey=Ma.mukey",
                }
                url = "https://sdmdataaccess.sc.egov.usda.gov/TABULAR/post.rest"
                soil_response = requests.post(url, json=body).json()

                feedback.setCurrentStep(10)
                if feedback.isCanceled():
                    return {}

                for row in soil_response["Table"]:
                    # None attribute for empty data
                    row = [None if not attr else attr for attr in row]
                    feat = QgsFeature(soil_layer.fields())
                    # populate data
                    for index, col in enumerate(row):
                        if index != len(attr_dict):
                            feat.setAttribute(attr_dict[index]["name"], col)
                        else:
                            feat.setGeometry(QgsGeometry.fromWkt(col))
                    provider.addFeatures([feat])

                feedback.setCurrentStep(11)
                if feedback.isCanceled():
                    return {}

            except:  # try wfs request
                feedback.reportError(
                    "Error getting soil data through post request. Your input layer maybe too large. Trying WFS download now.\nIf the Algorithm get stuck during download. Terminate the Algorithm and rerun with a smaller input layer.",
                    False,
                )
                xmin_reprojected = area_layer_reprojected.extent().xMinimum()
                ymin_reprojected = area_layer_reprojected.extent().yMinimum()
                xmax_reprojected = area_layer_reprojected.extent().xMaximum()
                ymax_reprojected = area_layer_reprojected.extent().yMaximum()

                request_URL_soil = f"https://sdmdataaccess.sc.egov.usda.gov/Spatial/SDMWGS84GEOGRAPHIC.wfs?SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=mapunitpolyextended&SRSNAME=EPSG:4326&BBOX={str(xmin_reprojected)},{str(ymin_reprojected)},{str(xmax_reprojected)},{str(ymax_reprojected)}"

                alg_params = {
                    "URL": request_URL_soil,
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["DownloadSoil"] = processing.run(
                    "native:filedownloader",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

                feedback.setCurrentStep(12)
                if feedback.isCanceled():
                    return {}

                # Swap X and Y coordinates
                alg_params = {
                    "INPUT": outputs["DownloadSoil"]["OUTPUT"],
                    "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
                }
                outputs["SwapXAndYCoordinates"] = processing.run(
                    "native:swapxy",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

                feedback.setCurrentStep(13)
                if feedback.isCanceled():
                    return {}

                soil_layer = outputs["SwapXAndYCoordinates"]["OUTPUT"]

            # Fix soil layer geometries
            alg_params = {
                "INPUT": soil_layer,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT
            }
            outputs["FixGeometries2"] = processing.run(
                "native:fixgeometries",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(14)
            if feedback.isCanceled():
                return {}

            # Clip Soil Layer
            alg_params = {
                "INPUT": outputs["FixGeometries2"]["OUTPUT"],
                "OVERLAY": parameters["areaboundary"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["Clip"] = processing.run(
                "native:clip",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(15)
            if feedback.isCanceled():
                return {}

            # Reproject Soil
            alg_params = {
                "INPUT": outputs["Clip"]["OUTPUT"],
                "OPERATION": "",
                "TARGET_CRS": QgsCoordinateReferenceSystem(origEPSGCode),
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["ReprojectSoil"] = processing.run(
                "native:reprojectlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(16)
            if feedback.isCanceled():
                return {}

            # Fix soil layer geometries second time
            alg_params = {
                "INPUT": outputs["ReprojectSoil"]["OUTPUT"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["FixGeometries3"] = processing.run(
                "native:fixgeometries",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(17)
            if feedback.isCanceled():
                return {}

            # Set layer style
            alg_params = {
                "INPUT": outputs["FixGeometries3"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "Soil_Layer.qml"),
            }
            try:  # for QGIS Version 3.12 and later
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForVectorLayer"] = processing.run(
                    "qgis:setstyleforvectorlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

            feedback.setCurrentStep(18)
            if feedback.isCanceled():
                return {}

        # Curve Number Calculations
        if curve_number_output == True:

            feedback.pushInfo(
                "Generating Curve Number Layer. This may take a while. Do not cancel."
            )
            # Intersection
            alg_params = {
                "INPUT": outputs["FixGeometries3"]["OUTPUT"],
                "INPUT_FIELDS": ["MUSYM", "HYDGRPDCD", "MUNAME"],
                "OVERLAY": outputs["FixGeometries"]["OUTPUT"],
                "OVERLAY_FIELDS": ["VALUE"],
                "OVERLAY_FIELDS_PREFIX": "",
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["Intersection"] = processing.run(
                "native:intersection",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(19)
            if feedback.isCanceled():
                return {}

            # Create GDCodeTemp
            alg_params = {
                "FIELD_LENGTH": 5,
                "FIELD_NAME": "GDCodeTemp",
                "FIELD_PRECISION": 3,
                "FIELD_TYPE": 2,
                "FORMULA":
                'IF ("HYDGRPDCD" IS NOT NULL, "Value" || "HYDGRPDCD", IF (("MUSYM" = \'W\' OR lower("MUSYM") = \'water\' OR lower("MUNAME") = \'water\' OR "MUNAME" = \'W\'), 11, "VALUE"))',
                "INPUT": outputs["Intersection"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateGdcodetemp"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(20)
            if feedback.isCanceled():
                return {}

            # Create GDCode
            alg_params = {
                "FIELD_LENGTH": 5,
                "FIELD_NAME": "GDCode",
                "FIELD_PRECISION": 3,
                "FIELD_TYPE": 2,
                "FORMULA":
                "if( var('drainedsoilsleaveuncheckedifnotsure') = True,replace(\"GDCodeTemp\", '/D', ''),replace(\"GDCodeTemp\", map('A/', '', 'B/', '', 'C/', '')))",
                "INPUT": outputs["CreateGdcodetemp"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateGdcode"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(21)
            if feedback.isCanceled():
                return {}

            # Create NLCD_LU
            alg_params = {
                "FIELD_LENGTH": 2,
                "FIELD_NAME": "NLCD_LU",
                "FIELD_PRECISION": 3,
                "FIELD_TYPE": 1,
                "FORMULA": '"Value"',
                "INPUT": outputs["CreateGdcode"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateNlcd_lu"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(22)
            if feedback.isCanceled():
                return {}

            # Join with CNLookup
            alg_params = {
                "DISCARD_NONMATCHING": False,
                "FIELD": "GDCode",
                "FIELDS_TO_COPY": ["CN_Join"],
                "FIELD_2": "GDCode",
                "INPUT": outputs["CreateNlcd_lu"]["OUTPUT"],
                "INPUT_2": parameters["cnlookup"],
                "METHOD": 1,
                "PREFIX": "",
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["JoinWithCnlookup"] = processing.run(
                "native:joinattributestable",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(23)
            if feedback.isCanceled():
                return {}

            # Create Integer CN
            alg_params = {
                "FIELD_LENGTH": 3,
                "FIELD_NAME": "CN",
                "FIELD_PRECISION": 0,
                "FIELD_TYPE": 1,
                "FORMULA": "CN_Join  * 1",
                "INPUT": outputs["JoinWithCnlookup"]["OUTPUT"],
                "NEW_FIELD": True,
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["CreateIntegerCn"] = processing.run(
                "qgis:fieldcalculator",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(24)
            if feedback.isCanceled():
                return {}

            # Drop field(s)
            alg_params = {
                "COLUMN": ["VALUE", "GDCodeTemp", "CN_Join"],
                "INPUT": outputs["CreateIntegerCn"]["OUTPUT"],
                "OUTPUT": QgsProcessing.TEMPORARY_OUTPUT,
            }
            outputs["DropFields"] = processing.run(
                "qgis:deletecolumn",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

            feedback.setCurrentStep(25)
            if feedback.isCanceled():
                return {}

            # Set layer style
            alg_params = {
                "INPUT": outputs["DropFields"]["OUTPUT"],
                "STYLE": os.path.join(cmd_folder, "CN_Grid.qml"),
            }
            try:  # for QGIS Version 3.12 and later
                outputs["SetLayerStyle"] = processing.run(
                    "native:setlayerstyle",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )
            except:  # for QGIS Version older than 3.12
                outputs["SetStyleForVectorLayer"] = processing.run(
                    "qgis:setstyleforvectorlayer",
                    alg_params,
                    context=context,
                    feedback=feedback,
                    is_child_algorithm=True,
                )

        if nlcd_rast_output:
            # Load NLCD Raster into project
            alg_params = {
                "INPUT": outputs["ReclassifyByTable"]["OUTPUT"],
                "NAME": "NLCD Land Cover Raster",
            }
            outputs["LoadLayerIntoProject1"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if nlcd_vect_output:
            # Load NLCD Vector Layer into project
            alg_params = {
                "INPUT": outputs["FixGeometries"]["OUTPUT"],
                "NAME": "NLCD Land Cover Vector",
            }
            outputs["LoadLayerIntoProject2"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if nlcd_rast_imp_output:
            # Load NLCD Impervious Raster into project
            alg_params = {
                "INPUT": outputs["DownloadNlcdImp"]["OUTPUT"],
                "NAME": "NLCD Impervious Raster",
            }
            outputs["LoadLayerIntoProject3"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if soil_output:
            # Load Soil Layer into project
            alg_params = {
                "INPUT": outputs["FixGeometries3"]["OUTPUT"],
                "NAME": "SSURGO Soil Layer",
            }
            outputs["LoadLayerIntoProject4"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        if curve_number_output:
            # Load Curve Number Layer into project
            alg_params = {
                "INPUT": outputs["DropFields"]["OUTPUT"],
                "NAME": "Curve Number Layer",
            }
            outputs["LoadLayerIntoProject5"] = processing.run(
                "native:loadlayer",
                alg_params,
                context=context,
                feedback=feedback,
                is_child_algorithm=True,
            )

        # log usage
        with open(os.path.join(cmd_folder, "usage_counter.log"), "r+") as f:
            counter = int(f.readline())
            f.seek(0)
            f.write(str(counter + 1))

        # check if counter is milestone
        if (counter + 1) % 25 == 0:
            appeal_file = NamedTemporaryFile("w", suffix=".html", delete=False)
            self.createHTML(appeal_file.name, counter + 1)
            results["Message"] = appeal_file.name

        return results
Ejemplo n.º 18
0
    def testAreaMeasureAndUnits(self):
        """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the
           calculated areas and units are always consistent
        """

        da = QgsDistanceArea()
        da.setSourceCrs(3452)
        da.setEllipsoidalMode(False)
        da.setEllipsoid("NONE")
        daCRS = QgsCoordinateReferenceSystem()
        daCRS.createFromSrsId(da.sourceCrs())

        polygon = QgsGeometry.fromPolygon([[
            QgsPoint(0, 0),
            QgsPoint(1, 0),
            QgsPoint(1, 1),
            QgsPoint(2, 1),
            QgsPoint(2, 2),
            QgsPoint(0, 2),
            QgsPoint(0, 0),
        ]])

        # We check both the measured area AND the units, in case the logic regarding
        # ellipsoids and units changes in future
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print("measured {} in {}".format(area, QgsUnitTypes.toString(units)))
        assert ((abs(area - 3.0) < 0.00000001
                 and units == QgsUnitTypes.SquareDegrees)
                or (abs(area - 37176087091.5) < 0.1
                    and units == QgsUnitTypes.SquareMeters))

        da.setEllipsoid("WGS84")
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print("measured {} in {}".format(area, QgsUnitTypes.toString(units)))
        assert ((abs(area - 3.0) < 0.00000001
                 and units == QgsUnitTypes.SquareDegrees)
                or (abs(area - 37176087091.5) < 0.1
                    and units == QgsUnitTypes.SquareMeters))

        da.setEllipsoidalMode(True)
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print("measured {} in {}".format(area, QgsUnitTypes.toString(units)))
        # should always be in Meters Squared
        self.assertAlmostEqual(area, 37416879192.9, delta=0.1)
        self.assertEqual(units, QgsUnitTypes.SquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.SquareMiles)
        self.assertAlmostEqual(area, 14446.7378, delta=0.001)

        # now try with a source CRS which is in feet
        polygon = QgsGeometry.fromPolygon([[
            QgsPoint(1850000, 4423000),
            QgsPoint(1851000, 4423000),
            QgsPoint(1851000, 4424000),
            QgsPoint(1852000, 4424000),
            QgsPoint(1852000, 4425000),
            QgsPoint(1851000, 4425000),
            QgsPoint(1850000, 4423000)
        ]])
        da.setSourceCrs(27469)
        da.setEllipsoidalMode(False)
        # measurement should be in square feet
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print("measured {} in {}".format(area, QgsUnitTypes.toString(units)))
        self.assertAlmostEqual(area, 2000000, delta=0.001)
        self.assertEqual(units, QgsUnitTypes.SquareFeet)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.SquareYards)
        self.assertAlmostEqual(area, 222222.2222, delta=0.001)

        da.setEllipsoidalMode(True)
        # now should be in Square Meters again
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print("measured {} in {}".format(area, QgsUnitTypes.toString(units)))
        self.assertAlmostEqual(area, 184149.37, delta=1.0)
        self.assertEqual(units, QgsUnitTypes.SquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.SquareYards)
        self.assertAlmostEqual(area, 220240.8172549, delta=1.0)
Ejemplo n.º 19
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))

        strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
        if self.MIN_DISTANCE in parameters and parameters[self.MIN_DISTANCE] is not None:
            minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
        else:
            minDistance = None

        expressionContext = self.createExpressionContext(parameters, context, source)
        dynamic_value = QgsProcessingParameters.isDynamic(parameters, "VALUE")
        value_property = None
        if self.EXPRESSION in parameters and parameters[self.EXPRESSION] is not None:
            expression = QgsExpression(self.parameterAsString(parameters, self.EXPRESSION, context))
            value = None
            if expression.hasParserError():
                raise QgsProcessingException(expression.parserErrorString())
            expression.prepare(expressionContext)
        else:
            expression = None
            if dynamic_value:
                value_property = parameters["VALUE"]
            value = self.parameterAsDouble(parameters, self.VALUE, context)

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, QgsWkbTypes.Point, source.sourceCrs(),
                                               QgsFeatureSink.RegeneratePrimaryKey)
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        da = QgsDistanceArea()
        da.setSourceCrs(source.sourceCrs(), context.transformContext())
        da.setEllipsoid(context.ellipsoid())

        total = 100.0 / source.featureCount() if source.featureCount() else 0
        current_progress = 0
        pointId = 0
        for current, f in enumerate(source.getFeatures()):
            if feedback.isCanceled():
                break

            if not f.hasGeometry():
                continue

            current_progress = total * current
            feedback.setProgress(current_progress)

            this_value = value
            if value_property is not None or expression is not None:
                expressionContext.setFeature(f)
                if value_property:
                    this_value, _ = value_property.valueAsDouble(expressionContext, value)
                else:
                    this_value = expression.evaluate(expressionContext)
                    if expression.hasEvalError():
                        feedback.pushInfo(
                            self.tr('Evaluation error for feature ID {}: {}').format(f.id(), expression.evalErrorString()))
                        continue

            fGeom = f.geometry()
            engine = QgsGeometry.createGeometryEngine(fGeom.constGet())
            engine.prepareGeometry()

            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(this_value)
            else:
                pointCount = int(round(this_value * da.measureArea(fGeom)))

            if pointCount == 0:
                feedback.pushInfo("Skip feature {} as number of points for it is 0.".format(f.id()))
                continue

            index = None
            if minDistance:
                index = QgsSpatialIndex()
            points = {}

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            feature_total = total / pointCount if pointCount else 1

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                if feedback.isCanceled():
                    break

                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                p = QgsPointXY(rx, ry)
                geom = QgsGeometry.fromPointXY(p)
                if engine.contains(geom.constGet()) and \
                        (not minDistance or vector.checkMinDistance(p, index, minDistance, points)):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', pointId)
                    f.setGeometry(geom)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                    if minDistance:
                        index.addFeature(f)
                    points[nPoints] = p
                    nPoints += 1
                    pointId += 1
                    feedback.setProgress(current_progress + int(nPoints * feature_total))
                nIterations += 1

            if nPoints < pointCount:
                feedback.pushInfo(self.tr('Could not generate requested number of random '
                                          'points. Maximum number of attempts exceeded.'))

        feedback.setProgress(100)

        return {self.OUTPUT: dest_id}
Ejemplo n.º 20
0
    def processAlgorithm(self, parameters, context, feedback):
        layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context)
        value = float(self.getParameterValue(self.VALUE))
        minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
        strategy = self.getParameterValue(self.STRATEGY)

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))
        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context)

        da = QgsDistanceArea()

        features = QgsProcessingUtils.getFeatures(layer, context)
        for current, f in enumerate(features):
            fGeom = f.geometry()
            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(value)
            else:
                pointCount = int(round(value * da.measureArea(fGeom)))

            if pointCount == 0:
                feedback.pushInfo("Skip feature {} as number of points for it is 0.")
                continue

            index = QgsSpatialIndex()
            points = dict()

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            total = 100.0 / pointCount

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                pnt = QgsPointXY(rx, ry)
                geom = QgsGeometry.fromPoint(pnt)
                if geom.within(fGeom) and \
                        vector.checkMinDistance(pnt, index, minDistance, points):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', nPoints)
                    f.setGeometry(geom)
                    writer.addFeature(f)
                    index.insertFeature(f)
                    points[nPoints] = pnt
                    nPoints += 1
                    feedback.setProgress(int(nPoints * total))
                nIterations += 1

            if nPoints < pointCount:
                QgsMessageLog.logMessage(self.tr('Can not generate requested number of random '
                                                 'points. Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO)

            feedback.setProgress(0)

        del writer
Ejemplo n.º 21
0
    def testAreaMeasureAndUnits(self):
        """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the
           calculated areas and units are always consistent
        """

        da = QgsDistanceArea()
        da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext())
        da.setEllipsoid("NONE")

        polygon = QgsGeometry.fromPolygonXY(
            [[
                QgsPointXY(0, 0), QgsPointXY(1, 0), QgsPointXY(1, 1), QgsPointXY(2, 1), QgsPointXY(2, 2), QgsPointXY(0, 2), QgsPointXY(0, 0),
            ]]
        )

        # We check both the measured area AND the units, in case the logic regarding
        # ellipsoids and units changes in future
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or
                (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters))

        da.setEllipsoid("WGS84")
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        # should always be in Meters Squared
        self.assertAlmostEqual(area, 36918093794.121284, delta=0.1)
        self.assertEqual(units, QgsUnitTypes.AreaSquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareMiles)
        self.assertAlmostEqual(area, 14254.155703182701, delta=0.001)

        # now try with a source CRS which is in feet
        polygon = QgsGeometry.fromPolygonXY(
            [[
                QgsPointXY(1850000, 4423000), QgsPointXY(1851000, 4423000), QgsPointXY(1851000, 4424000), QgsPointXY(1852000, 4424000), QgsPointXY(1852000, 4425000), QgsPointXY(1851000, 4425000), QgsPointXY(1850000, 4423000)
            ]]
        )
        da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext())
        da.setEllipsoid("NONE")
        # measurement should be in square feet
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        self.assertAlmostEqual(area, 2000000, delta=0.001)
        self.assertEqual(units, QgsUnitTypes.AreaSquareFeet)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards)
        self.assertAlmostEqual(area, 222222.2222, delta=0.001)

        da.setEllipsoid("WGS84")
        # now should be in Square Meters again
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        self.assertAlmostEqual(area, 185818.59096575077, delta=1.0)
        self.assertEqual(units, QgsUnitTypes.AreaSquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards)
        self.assertAlmostEqual(area, 222237.18521272976, delta=1.0)
Ejemplo n.º 22
0
class SizeCalculator():

    """Special object to handle size calculation with an output unit."""

    def __init__(
            self, coordinate_reference_system, geometry_type, exposure_key):
        """Constructor for the size calculator.

        :param coordinate_reference_system: The Coordinate Reference System of
            the layer.
        :type coordinate_reference_system: QgsCoordinateReferenceSystem

        :param exposure_key: The geometry type of the layer.
        :type exposure_key: qgis.core.QgsWkbTypes.GeometryType
        """
        self.calculator = QgsDistanceArea()
        self.calculator.setSourceCrs(
            coordinate_reference_system,
            QgsProject.instance().transformContext()
        )
        self.calculator.setEllipsoid('WGS84')

        if geometry_type == QgsWkbTypes.LineGeometry:
            self.default_unit = unit_metres
            LOGGER.info('The size calculator is set to use {unit}'.format(
                unit=distance_unit[self.calculator.lengthUnits()]))
        else:
            self.default_unit = unit_square_metres
            LOGGER.info('The size calculator is set to use {unit}'.format(
                unit=distance_unit[self.calculator.areaUnits()]))
        self.geometry_type = geometry_type
        self.output_unit = None
        if exposure_key:
            exposure_definition = definition(exposure_key)
            self.output_unit = exposure_definition['size_unit']

    def measure_distance(self, point_a, point_b):
        """Measure the distance between two points.

        This is added here since QgsDistanceArea object is already called here.

        :param point_a: First Point.
        :type point_a: QgsPoint

        :param point_b: Second Point.
        :type point_b: QgsPoint

        :return: The distance between input points.
        :rtype: float
        """
        return self.calculator.measureLine(point_a, point_b)

    def measure(self, geometry):
        """Measure the length or the area of a geometry.

        :param geometry: The geometry.
        :type geometry: QgsGeometry

        :return: The geometric size in the expected exposure unit.
        :rtype: float
        """
        message = 'Size with NaN value : geometry valid={valid}, WKT={wkt}'
        feature_size = 0
        if geometry.isMultipart():
            # Be careful, the size calculator is not working well on a
            # multipart.
            # So we compute the size part per part. See ticket #3812
            for single in geometry.asGeometryCollection():
                if self.geometry_type == QgsWkbTypes.LineGeometry:
                    geometry_size = self.calculator.measureLength(single)
                else:
                    geometry_size = self.calculator.measureArea(single)
                if not isnan(geometry_size):
                    feature_size += geometry_size
                else:
                    LOGGER.debug(message.format(
                        valid=single.isGeosValid(),
                        wkt=single.asWkt()))
        else:
            if self.geometry_type == QgsWkbTypes.LineGeometry:
                geometry_size = self.calculator.measureLength(geometry)
            else:
                geometry_size = self.calculator.measureArea(geometry)
            if not isnan(geometry_size):
                feature_size = geometry_size
            else:
                LOGGER.debug(message.format(
                    valid=geometry.isGeosValid(),
                    wkt=geometry.asWkt()))

        feature_size = round(feature_size)

        if self.output_unit:
            if self.output_unit != self.default_unit:
                feature_size = convert_unit(
                    feature_size, self.default_unit, self.output_unit)

        return feature_size
    def processAlgorithm(self, parameters, context, feedback):
        # Output table:
        #   source_id | class | area | percentage

        # Base (source) layer
        source = self.parameterAsSource(parameters, self.INPUT, context)

        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        source_crs = source.sourceCrs()
        source_count = source.featureCount()
        source_features = source.getFeatures()

        # Source id field
        id_field = self.parameterAsFields(parameters, self.SOURCE_ID_FIELD,
                                          context)[0]

        # Overlay layer
        overlay = self.parameterAsVectorLayer(parameters, self.OVERLAY,
                                              context)
        #overlay = self.parameterAsSource(
        #    parameters,
        #    self.OVERLAY,
        #    context
        #)
        if overlay is None:
            raise QgsProcessingException(
                "Overlay layer could not be loaded! Is it a valid layer?")

        # Class field
        class_field = self.parameterAsFields(parameters, self.CLASS_FIELD,
                                             context)[0]

        # Output layer
        destId = ''
        fields = QgsFields()
        fields.append(QgsField("id", source.fields().field(id_field).type()))
        fields.append(
            QgsField("class",
                     overlay.fields().field(class_field).type()))
        fields.append(QgsField("area", QVariant.Double))
        fields.append(QgsField("percentage", QVariant.Double))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               QgsWkbTypes.NoGeometry)

        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        da = QgsDistanceArea()
        da.setSourceCrs(source_crs, context.transformContext())
        da.setEllipsoid(context.ellipsoid())

        # loop through input
        step = 100.0 / source_count if source_count > 0 else 0
        i = 0
        feedback.setProgress(1)

        for feature in source_features:
            if feedback.isCanceled():
                break

            id_value = feature[id_field]

            feedback.pushInfo(
                '\n{}) Preparing input feature with "{}" {}...'.format(
                    i + 1, id_field, id_value))

            # 1) Get the current feature in its own layer
            feedback.pushInfo('\nExtracting base polygon...')
            feature_layer = processing.run(
                "native:extractbyattribute",
                {
                    'INPUT': parameters[self.INPUT],
                    'FIELD': id_field,
                    'OPERATOR': 0,  # =
                    'VALUE': id_value,
                    'OUTPUT': 'TEMPORARY_OUTPUT'
                },
                context=context)['OUTPUT']

            if feedback.isCanceled():
                break

            # 2) Extract all overlay polygons by location for the current feature
            feedback.pushInfo('Getting class features by location...')
            overlay_subset = processing.run(
                "native:extractbylocation",
                {
                    'INPUT': overlay,
                    'PREDICATE': [0],  # Intersects
                    'INTERSECT': feature_layer,
                    'OUTPUT': 'TEMPORARY_OUTPUT'
                },
                context=context)['OUTPUT']

            if feedback.isCanceled():
                break

            out_features = list()
            idx_class_field = overlay_subset.fields().indexOf(class_field)

            feedback.pushInfo(
                '\nAnalysing overlaps in input feature with id {}...'.format(
                    id_value))

            if feature.hasGeometry() and not qgsDoubleNear(
                    feature.geometry().area(), 0.0):
                input_geom = feature.geometry()
                input_area = da.measureArea(input_geom)

                # Prepare for lots of intersection tests (for speed)
                geom_engine = QgsGeometry.createGeometryEngine(
                    input_geom.constGet())
                geom_engine.prepareGeometry()

                if feedback.isCanceled():
                    break

                # Iterate classes
                unique_values = overlay_subset.uniqueValues(idx_class_field)
                sub_step = step / len(unique_values) if unique_values else 0
                j = 0

                for class_name in unique_values:
                    feedback.pushInfo(
                        "...Analysing class '{}'...".format(class_name))
                    exp = QgsExpression("{} = '{}'".format(
                        class_field, class_name))
                    request = QgsFeatureRequest(exp).setSubsetOfAttributes(
                        list()).setDestinationCrs(
                            source_crs, context.transformContext(
                            )).setInvalidGeometryCheck(
                                context.invalidGeometryCheck())

                    class_overlay_area = 0

                    # Iterate features by class
                    for class_feature in overlay_subset.getFeatures(request):
                        if feedback.isCanceled():
                            break

                        # Since we know in 2 we get all overlay features that intersect,
                        # we skip the otherwise must-have intersects check and proceed
                        # to get the intersection right away
                        if class_feature.hasGeometry() and not qgsDoubleNear(
                                class_feature.geometry().area(), 0.0):
                            overlay_intersection = geom_engine.intersection(
                                class_feature.geometry().constGet())
                            class_overlay_area += da.measureArea(
                                QgsGeometry(overlay_intersection))

                    if feedback.isCanceled():
                        break

                    out_feature = QgsFeature()
                    out_attrs = [
                        id_value, class_name, class_overlay_area,
                        100 * class_overlay_area / input_area
                    ]
                    out_feature.setAttributes(out_attrs)
                    out_features.append(out_feature)

                    j += 1
                    feedback.setProgress(i * step + j * sub_step)
            else:
                # Input feature has no geometry
                for class_feature in class_layer_features:
                    out_feature = QgsFeature()
                    out_feature.setAttributes(
                        [id_value, class_feature[class_field], None, None])
                    out_features.append(out_feature)

            if not sink.addFeatures(out_features, QgsFeatureSink.FastInsert):
                raise QgsProcessingException(sink.lastError())

            i += 1
            feedback.setProgress(i * step)

        return {self.OUTPUT: dest_id}
Ejemplo n.º 24
0
class ExportGeometryInfo(QgisAlgorithm):

    INPUT = 'INPUT'
    METHOD = 'CALC_METHOD'
    OUTPUT = 'OUTPUT'

    def icon(self):
        return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'export_geometry.png'))

    def tags(self):
        return self.tr('export,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons').split(',')

    def group(self):
        return self.tr('Vector table tools')

    def __init__(self):
        super().__init__()
        self.export_z = False
        self.export_m = False
        self.distance_area = None
        self.calc_methods = [self.tr('Layer CRS'),
                             self.tr('Project CRS'),
                             self.tr('Ellipsoidal')]

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
                                                              self.tr('Input layer')))
        self.addParameter(QgsProcessingParameterEnum(self.METHOD,
                                                     self.tr('Calculate using'), options=self.calc_methods, defaultValue=0))
        self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Added geom info')))

    def name(self):
        return 'exportaddgeometrycolumns'

    def displayName(self):
        return self.tr('Export/Add geometry columns')

    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            areaName = vector.createUniqueFieldName('area', fields)
            fields.append(QgsField(areaName, QVariant.Double))
            perimeterName = vector.createUniqueFieldName('perimeter', fields)
            fields.append(QgsField(perimeterName, QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            lengthName = vector.createUniqueFieldName('length', fields)
            fields.append(QgsField(lengthName, QVariant.Double))
        else:
            xName = vector.createUniqueFieldName('xcoord', fields)
            fields.append(QgsField(xName, QVariant.Double))
            yName = vector.createUniqueFieldName('ycoord', fields)
            fields.append(QgsField(yName, QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                zName = vector.createUniqueFieldName('zcoord', fields)
                fields.append(QgsField(zName, QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                zName = vector.createUniqueFieldName('mvalue', fields)
                fields.append(QgsField(zName, QVariant.Double))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, wkb_type, source.sourceCrs())

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}

    def point_attributes(self, geometry):
        pt = None
        if not geometry.isMultipart():
            pt = geometry.geometry()
        else:
            if geometry.numGeometries() > 0:
                pt = geometry.geometryN(0)
        attrs = []
        if pt:
            attrs.append(pt.x())
            attrs.append(pt.y())
            # add point z/m
            if self.export_z:
                attrs.append(pt.z())
            if self.export_m:
                attrs.append(pt.m())
        return attrs

    def line_attributes(self, geometry):
        return [self.distance_area.measureLength(geometry)]

    def polygon_attributes(self, geometry):
        area = self.distance_area.measureArea(geometry)
        perimeter = self.distance_area.measurePerimeter(geometry)
        return [area, perimeter]
Ejemplo n.º 25
0
class SizeCalculator():
    """Special object to handle size calculation with an output unit."""
    def __init__(self, coordinate_reference_system, geometry_type,
                 exposure_key):
        """Constructor for the size calculator.

        :param coordinate_reference_system: The Coordinate Reference System of
            the layer.
        :type coordinate_reference_system: QgsCoordinateReferenceSystem

        :param exposure_key: The geometry type of the layer.
        :type exposure_key: qgis.core.QgsWkbTypes.GeometryType
        """
        self.calculator = QgsDistanceArea()
        self.calculator.setSourceCrs(coordinate_reference_system,
                                     QgsProject.instance().transformContext())
        self.calculator.setEllipsoid('WGS84')

        if geometry_type == QgsWkbTypes.LineGeometry:
            self.default_unit = unit_metres
            LOGGER.info('The size calculator is set to use {unit}'.format(
                unit=distance_unit[self.calculator.lengthUnits()]))
        else:
            self.default_unit = unit_square_metres
            LOGGER.info('The size calculator is set to use {unit}'.format(
                unit=distance_unit[self.calculator.areaUnits()]))
        self.geometry_type = geometry_type
        self.output_unit = None
        if exposure_key:
            exposure_definition = definition(exposure_key)
            self.output_unit = exposure_definition['size_unit']

    def measure_distance(self, point_a, point_b):
        """Measure the distance between two points.

        This is added here since QgsDistanceArea object is already called here.

        :param point_a: First Point.
        :type point_a: QgsPoint

        :param point_b: Second Point.
        :type point_b: QgsPoint

        :return: The distance between input points.
        :rtype: float
        """
        return self.calculator.measureLine(point_a, point_b)

    def measure(self, geometry):
        """Measure the length or the area of a geometry.

        :param geometry: The geometry.
        :type geometry: QgsGeometry

        :return: The geometric size in the expected exposure unit.
        :rtype: float
        """
        message = 'Size with NaN value : geometry valid={valid}, WKT={wkt}'
        feature_size = 0
        if geometry.isMultipart():
            # Be careful, the size calculator is not working well on a
            # multipart.
            # So we compute the size part per part. See ticket #3812
            for single in geometry.asGeometryCollection():
                if self.geometry_type == QgsWkbTypes.LineGeometry:
                    geometry_size = self.calculator.measureLength(single)
                else:
                    geometry_size = self.calculator.measureArea(single)
                if not isnan(geometry_size):
                    feature_size += geometry_size
                else:
                    LOGGER.debug(
                        message.format(valid=single.isGeosValid(),
                                       wkt=single.asWkt()))
        else:
            if self.geometry_type == QgsWkbTypes.LineGeometry:
                geometry_size = self.calculator.measureLength(geometry)
            else:
                geometry_size = self.calculator.measureArea(geometry)
            if not isnan(geometry_size):
                feature_size = geometry_size
            else:
                LOGGER.debug(
                    message.format(valid=geometry.isGeosValid(),
                                   wkt=geometry.asWkt()))

        feature_size = round(feature_size)

        if self.output_unit:
            if self.output_unit != self.default_unit:
                feature_size = convert_unit(feature_size, self.default_unit,
                                            self.output_unit)

        return feature_size
Ejemplo n.º 26
0
    def testAreaMeasureAndUnits(self):
        """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the
           calculated areas and units are always consistent
        """

        da = QgsDistanceArea()
        da.setSourceCrs(3452)
        da.setEllipsoidalMode(False)
        da.setEllipsoid("NONE")
        daCRS = QgsCoordinateReferenceSystem()
        daCRS = da.sourceCrs()

        polygon = QgsGeometry.fromPolygon(
            [[
                QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 2), QgsPoint(0, 2), QgsPoint(0, 0),
            ]]
        )

        # We check both the measured area AND the units, in case the logic regarding
        # ellipsoids and units changes in future
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or
                (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters))

        da.setEllipsoid("WGS84")
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or
                (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters))

        da.setEllipsoidalMode(True)
        area = da.measureArea(polygon)
        units = da.areaUnits()

        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        # should always be in Meters Squared
        self.assertAlmostEqual(area, 37416879192.9, delta=0.1)
        self.assertEqual(units, QgsUnitTypes.AreaSquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareMiles)
        self.assertAlmostEqual(area, 14446.7378, delta=0.001)

        # now try with a source CRS which is in feet
        polygon = QgsGeometry.fromPolygon(
            [[
                QgsPoint(1850000, 4423000), QgsPoint(1851000, 4423000), QgsPoint(1851000, 4424000), QgsPoint(1852000, 4424000), QgsPoint(1852000, 4425000), QgsPoint(1851000, 4425000), QgsPoint(1850000, 4423000)
            ]]
        )
        da.setSourceCrs(27469)
        da.setEllipsoidalMode(False)
        # measurement should be in square feet
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        self.assertAlmostEqual(area, 2000000, delta=0.001)
        self.assertEqual(units, QgsUnitTypes.AreaSquareFeet)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards)
        self.assertAlmostEqual(area, 222222.2222, delta=0.001)

        da.setEllipsoidalMode(True)
        # now should be in Square Meters again
        area = da.measureArea(polygon)
        units = da.areaUnits()
        print(("measured {} in {}".format(area, QgsUnitTypes.toString(units))))
        self.assertAlmostEqual(area, 184149.37, delta=1.0)
        self.assertEqual(units, QgsUnitTypes.AreaSquareMeters)

        # test converting the resultant area
        area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards)
        self.assertAlmostEqual(area, 220240.8172549, delta=1.0)
Ejemplo n.º 27
0
    def processAlgorithm(self, parameters, context, feedback):
        layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.VECTOR), context)
        fieldName = self.getParameterValue(self.FIELD)
        minDistance = float(self.getParameterValue(self.MIN_DISTANCE))
        strategy = self.getParameterValue(self.STRATEGY)

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))
        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context)

        da = QgsDistanceArea()

        features = QgsProcessingUtils.getFeatures(layer, context)
        for current, f in enumerate(features):
            fGeom = f.geometry()
            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(f[fieldName])
            else:
                pointCount = int(round(f[fieldName] * da.measureArea(fGeom)))

            if pointCount == 0:
                feedback.pushInfo("Skip feature {} as number of points for it is 0.")
                continue

            index = QgsSpatialIndex()
            points = dict()

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            total = 100.0 / pointCount

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                pnt = QgsPointXY(rx, ry)
                geom = QgsGeometry.fromPoint(pnt)
                if geom.within(fGeom) and \
                   vector.checkMinDistance(pnt, index, minDistance, points):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', nPoints)
                    f.setGeometry(geom)
                    writer.addFeature(f)
                    index.insertFeature(f)
                    points[nPoints] = pnt
                    nPoints += 1
                    feedback.setProgress(int(nPoints * total))
                nIterations += 1

            if nPoints < pointCount:
                QgsMessageLog.logMessage(self.tr('Can not generate requested number of random '
                                                 'points. Maximum number of attempts exceeded.'), self.tr('Processing'), QgsMessageLog.INFO)

            feedback.setProgress(0)

        del writer
Ejemplo n.º 28
0
class ExportGeometryInfo(QgisAlgorithm):

    INPUT = 'INPUT'
    METHOD = 'CALC_METHOD'
    OUTPUT = 'OUTPUT'

    def icon(self):
        return QgsApplication.getThemeIcon("/algorithms/mAlgorithmAddGeometryAttributes.svg")

    def svgIconPath(self):
        return QgsApplication.iconPath("/algorithms/mAlgorithmAddGeometryAttributes.svg")

    def tags(self):
        return self.tr('export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons,sinuosity,fields').split(',')

    def group(self):
        return self.tr('Vector geometry')

    def groupId(self):
        return 'vectorgeometry'

    def __init__(self):
        super().__init__()
        self.export_z = False
        self.export_m = False
        self.distance_area = None
        self.calc_methods = [self.tr('Layer CRS'),
                             self.tr('Project CRS'),
                             self.tr('Ellipsoidal')]

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
                                                              self.tr('Input layer')))
        self.addParameter(QgsProcessingParameterEnum(self.METHOD,
                                                     self.tr('Calculate using'), options=self.calc_methods, defaultValue=0))
        self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Added geom info')))

    def name(self):
        return 'exportaddgeometrycolumns'

    def displayName(self):
        return self.tr('Add geometry attributes')

    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
            if not QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('straightdis', QVariant.Double))
                new_fields.append(QgsField('sinuosity', QVariant.Double))
        else:
            new_fields.append(QgsField('xcoord', QVariant.Double))
            new_fields.append(QgsField('ycoord', QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                new_fields.append(QgsField('zcoord', QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                new_fields.append(QgsField('mvalue', QVariant.Double))

        fields = QgsProcessingUtils.combineFields(fields, new_fields)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, wkb_type, source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}

    def point_attributes(self, geometry):
        pt = None
        if not geometry.isMultipart():
            pt = geometry.constGet()
        else:
            if geometry.numGeometries() > 0:
                pt = geometry.geometryN(0)
        attrs = []
        if pt:
            attrs.append(pt.x())
            attrs.append(pt.y())
            # add point z/m
            if self.export_z:
                attrs.append(pt.z())
            if self.export_m:
                attrs.append(pt.m())
        return attrs

    def line_attributes(self, geometry):
        if geometry.isMultipart():
            return [self.distance_area.measureLength(geometry)]
        else:
            curve = geometry.constGet()
            p1 = curve.startPoint()
            p2 = curve.endPoint()
            straight_distance = self.distance_area.measureLine(QgsPointXY(p1), QgsPointXY(p2))
            sinuosity = curve.sinuosity()
            if math.isnan(sinuosity):
                sinuosity = NULL
            return [self.distance_area.measureLength(geometry), straight_distance, sinuosity]

    def polygon_attributes(self, geometry):
        area = self.distance_area.measureArea(geometry)
        perimeter = self.distance_area.measurePerimeter(geometry)
        return [area, perimeter]
Ejemplo n.º 29
0
class ExportGeometryInfo(QgisAlgorithm):

    INPUT = 'INPUT'
    METHOD = 'CALC_METHOD'
    OUTPUT = 'OUTPUT'

    def icon(self):
        return QgsApplication.getThemeIcon(
            "/algorithms/mAlgorithmAddGeometryAttributes.svg")

    def svgIconPath(self):
        return QgsApplication.iconPath(
            "/algorithms/mAlgorithmAddGeometryAttributes.svg")

    def tags(self):
        return self.tr(
            'export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons,sinuosity,fields'
        ).split(',')

    def group(self):
        return self.tr('Vector geometry')

    def groupId(self):
        return 'vectorgeometry'

    def __init__(self):
        super().__init__()
        self.export_z = False
        self.export_m = False
        self.distance_area = None
        self.calc_methods = [
            self.tr('Layer CRS'),
            self.tr('Project CRS'),
            self.tr('Ellipsoidal')
        ]

    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFeatureSource(self.INPUT,
                                                self.tr('Input layer')))
        self.addParameter(
            QgsProcessingParameterEnum(self.METHOD,
                                       self.tr('Calculate using'),
                                       options=self.calc_methods,
                                       defaultValue=0))
        self.addParameter(
            QgsProcessingParameterFeatureSink(self.OUTPUT,
                                              self.tr('Added geom info')))

    def name(self):
        return 'exportaddgeometrycolumns'

    def displayName(self):
        return self.tr('Add geometry attributes')

    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
            if not QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('straightdis', QVariant.Double))
                new_fields.append(QgsField('sinuosity', QVariant.Double))
        else:
            new_fields.append(QgsField('xcoord', QVariant.Double))
            new_fields.append(QgsField('ycoord', QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                new_fields.append(QgsField('zcoord', QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                new_fields.append(QgsField('mvalue', QVariant.Double))

        fields = QgsProcessingUtils.combineFields(fields, new_fields)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields, wkb_type,
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(),
                                            context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(),
                                                    context.project().crs(),
                                                    context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}

    def point_attributes(self, geometry):
        pt = None
        if not geometry.isMultipart():
            pt = geometry.constGet()
        else:
            if geometry.numGeometries() > 0:
                pt = geometry.geometryN(0)
        attrs = []
        if pt:
            attrs.append(pt.x())
            attrs.append(pt.y())
            # add point z/m
            if self.export_z:
                attrs.append(pt.z())
            if self.export_m:
                attrs.append(pt.m())
        return attrs

    def line_attributes(self, geometry):
        if geometry.isMultipart():
            return [self.distance_area.measureLength(geometry)]
        else:
            curve = geometry.constGet()
            p1 = curve.startPoint()
            p2 = curve.endPoint()
            straight_distance = self.distance_area.measureLine(
                QgsPointXY(p1), QgsPointXY(p2))
            sinuosity = curve.sinuosity()
            if math.isnan(sinuosity):
                sinuosity = NULL
            return [
                self.distance_area.measureLength(geometry), straight_distance,
                sinuosity
            ]

    def polygon_attributes(self, geometry):
        area = self.distance_area.measureArea(geometry)
        perimeter = self.distance_area.measurePerimeter(geometry)
        return [area, perimeter]
Ejemplo n.º 30
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))

        strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
        minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)

        expression = QgsExpression(self.parameterAsString(parameters, self.EXPRESSION, context))
        if expression.hasParserError():
            raise QgsProcessingException(expression.parserErrorString())

        expressionContext = self.createExpressionContext(parameters, context, source)
        expression.prepare(expressionContext)

        fields = QgsFields()
        fields.append(QgsField('id', QVariant.Int, '', 10, 0))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, QgsWkbTypes.Point, source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey)
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        da = QgsDistanceArea()
        da.setSourceCrs(source.sourceCrs(), context.transformContext())
        da.setEllipsoid(context.project().ellipsoid())

        total = 100.0 / source.featureCount() if source.featureCount() else 0
        current_progress = 0
        for current, f in enumerate(source.getFeatures()):
            if feedback.isCanceled():
                break

            if not f.hasGeometry():
                continue

            current_progress = total * current
            feedback.setProgress(current_progress)

            expressionContext.setFeature(f)
            value = expression.evaluate(expressionContext)
            if expression.hasEvalError():
                feedback.pushInfo(
                    self.tr('Evaluation error for feature ID {}: {}').format(f.id(), expression.evalErrorString()))
                continue

            fGeom = f.geometry()
            engine = QgsGeometry.createGeometryEngine(fGeom.constGet())
            engine.prepareGeometry()

            bbox = fGeom.boundingBox()
            if strategy == 0:
                pointCount = int(value)
            else:
                pointCount = int(round(value * da.measureArea(fGeom)))

            if pointCount == 0:
                feedback.pushInfo("Skip feature {} as number of points for it is 0.".format(f.id()))
                continue

            index = QgsSpatialIndex()
            points = dict()

            nPoints = 0
            nIterations = 0
            maxIterations = pointCount * 200
            feature_total = total / pointCount if pointCount else 1

            random.seed()

            while nIterations < maxIterations and nPoints < pointCount:
                if feedback.isCanceled():
                    break

                rx = bbox.xMinimum() + bbox.width() * random.random()
                ry = bbox.yMinimum() + bbox.height() * random.random()

                p = QgsPointXY(rx, ry)
                geom = QgsGeometry.fromPointXY(p)
                if engine.contains(geom.constGet()) and \
                        vector.checkMinDistance(p, index, minDistance, points):
                    f = QgsFeature(nPoints)
                    f.initAttributes(1)
                    f.setFields(fields)
                    f.setAttribute('id', nPoints)
                    f.setGeometry(geom)
                    sink.addFeature(f, QgsFeatureSink.FastInsert)
                    index.addFeature(f)
                    points[nPoints] = p
                    nPoints += 1
                    feedback.setProgress(current_progress + int(nPoints * feature_total))
                nIterations += 1

            if nPoints < pointCount:
                feedback.pushInfo(self.tr('Could not generate requested number of random '
                                          'points. Maximum number of attempts exceeded.'))

        feedback.setProgress(100)

        return {self.OUTPUT: dest_id}
Ejemplo n.º 31
0
class MeasureAreaTool(QgsMapTool):
    finished = pyqtSignal()

    def __init__(self, canvas, msglog):
        super().__init__(canvas)
        self.canvas = canvas
        self.msglog = msglog
        self.start_point = self.middle_point = self.end_point = None
        self.rubber_band = QgsRubberBand(self.canvas,
                                         QgsWkbTypes.PolygonGeometry)
        self.rubber_band.setColor(QColor(255, 0, 0, 100))
        self.rubber_band.setWidth(3)
        self.rubber_band_points = QgsRubberBand(self.canvas,
                                                QgsWkbTypes.PointGeometry)
        self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rubber_band_points.setIconSize(10)
        self.rubber_band_points.setColor(QColor(255, 0, 0, 150))

        crs = self.canvas.mapSettings().destinationCrs()
        self.area_calc = QgsDistanceArea()
        self.area_calc.setSourceCrs(crs,
                                    QgsProject.instance().transformContext())
        self.area_calc.setEllipsoid(crs.ellipsoidAcronym())
        self.reset()

    def reset(self):
        """ Reset log message and rubber band"""
        self.msglog.logMessage("")
        self.start_point = self.end_point = None
        self.rubber_band.reset(QgsWkbTypes.PolygonGeometry)
        self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)

    def canvasPressEvent(self, event):
        pass

    def canvasReleaseEvent(self, event):
        transform = self.canvas.getCoordinateTransform()
        point = transform.toMapCoordinates(event.pos().x(), event.pos().y())

        if self.start_point and event.button() == Qt.RightButton:
            multipoint = self.rubber_band.asGeometry()
            area = self.area_calc.measureArea(multipoint)
            anglemsg = QMessageBox(self.parent())
            anglemsg.finished.connect(self.deactivate)
            anglemsg.setWindowTitle("Measure area tool")
            anglemsg.setText("Area: {} ".format(
                self.area_calc.formatArea(area, 3,
                                          QgsUnitTypes.AreaSquareMeters,
                                          True)))
            anglemsg.exec()
            self.finish()
        elif self.start_point:
            self.rubber_band.addPoint(point)
            self.rubber_band_points.addPoint(point)

        else:
            self.start_point = point
            self.rubber_band.addPoint(self.start_point)
            self.rubber_band_points.addPoint(self.start_point)

    def canvasMoveEvent(self, e):
        if self.start_point and not self.end_point:
            transform = self.canvas.getCoordinateTransform()
            point = transform.toMapCoordinates(e.pos().x(), e.pos().y())
            self.rubber_band.movePoint(point)

            multipoint = self.rubber_band.asGeometry()
            area = self.area_calc.measureArea(multipoint)
            self.msglog.logMessage("")
            self.msglog.logMessage(
                "Current area: {} ".format(
                    self.area_calc.formatArea(area, 3,
                                              QgsUnitTypes.AreaSquareMeters,
                                              True)), "Measure Area:", 0)

    def keyPressEvent(self, event):
        """
        When escape key is pressed, line is restarted
        """
        if event.key() == Qt.Key_Escape:
            self.reset()

    def finish(self):
        self.reset()
        self.finished.emit()