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 testMeasureLine(self):
     #   +-+
     #   | |
     # +-+ +
     linestring = QgsGeometry.fromPolyline(
         [QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 0)]
     )
     da = QgsDistanceArea()
     length = da.measureLength(linestring)
     myMessage = "Expected:\n%f\nGot:\n%f\n" % (4, length)
     assert length == 4, myMessage
Ejemplo n.º 3
0
 def testMeasureLine(self):
     #   +-+
     #   | |
     # +-+ +
     linestring = QgsGeometry.fromPolyline(
         [QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 0), ]
     )
     da = QgsDistanceArea()
     length = da.measureLength(linestring)
     myMessage = ('Expected:\n%f\nGot:\n%f\n' %
                  (4, length))
     assert length == 4, myMessage
Ejemplo n.º 4
0
 def testMeasureMultiLine(self):
     #   +-+ +-+-+
     #   | | |   |
     # +-+ + +   +-+
     linestring = QgsGeometry.fromMultiPolyline(
         [
             [QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 0)],
             [QgsPoint(3, 0), QgsPoint(3, 1), QgsPoint(5, 1), QgsPoint(5, 0), QgsPoint(6, 0)],
         ]
     )
     da = QgsDistanceArea()
     length = da.measureLength(linestring)
     myMessage = "Expected:\n%f\nGot:\n%f\n" % (9, length)
     assert length == 9, myMessage
Ejemplo n.º 5
0
 def testMeasureMultiLine(self):
     #   +-+ +-+-+
     #   | | |   |
     # +-+ + +   +-+
     linestring = QgsGeometry.fromMultiPolyline(
         [
             [QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 0), ],
             [QgsPoint(3, 0), QgsPoint(3, 1), QgsPoint(5, 1), QgsPoint(5, 0), QgsPoint(6, 0), ]
         ]
     )
     da = QgsDistanceArea()
     length = da.measureLength(linestring)
     myMessage = ('Expected:\n%f\nGot:\n%f\n' %
                  (9, length))
     assert length == 9, myMessage
    def highlight_courant(self):
        """highlight animated flowline layer where Courant number is higher than a given value (use velocity variable as flowline-results)"""

        if len(QgsProject.instance().mapLayersByName("line_results")) == 0:
            self.iface.messageBar().pushMessage(
                "Warning",
                'Couldn\'t find line_results layer, click "Animation on" button',
                level=Qgis.Warning,
            )
            return

        # layer found
        canvas = self.iface.mapCanvas()

        line_results = QgsProject.instance().mapLayersByName("line_results")[0]
        global_settings_layer = QgsProject.instance().mapLayersByName(
            "v2_global_settings"
        )[0]
        timestep = list(global_settings_layer.getFeatures())[0][
            "sim_time_step"
        ]  # [0] -> [self.selected_scenario_index]
        d = QgsDistanceArea()
        d.setEllipsoid("WGS84")

        features = line_results.getFeatures()
        for feature in features:
            kcu = feature["kcu"]
            if kcu in [0, 1, 2, 3, 5, 100, 101]:
                geometry = feature.geometry()
                length = d.measureLength(geometry)

                velocity = abs(feature["result"])

                courant = velocity * timestep / length

                if courant > self.courantThreshold.value():
                    color = QtGui.QColor(Qt.red)
                    highlight = QgsHighlight(canvas, feature, line_results)
                    highlight.setColor(color)
                    highlight.setMinWidth(courant / 2)
                    # highlight.setBuffer()
                    color.setAlpha(50)
                    highlight.setFillColor(color)
                    highlight.show()
                    self.highlights.append(highlight)
Ejemplo n.º 7
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.º 8
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.º 9
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.º 10
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
Ejemplo n.º 11
0
    def processAlgorithm(self, progress):
        lineLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.LINES))
        polyLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.POLYGONS))
        lengthFieldName = self.getParameterValue(self.LEN_FIELD)
        countFieldName = self.getParameterValue(self.COUNT_FIELD)

        (idxLength, fieldList) = vector.findOrCreateField(polyLayer,
                                                          polyLayer.fields(), lengthFieldName)
        (idxCount, fieldList) = vector.findOrCreateField(polyLayer, fieldList,
                                                         countFieldName)

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fieldList.toList(), polyLayer.wkbType(), polyLayer.crs())

        spatialIndex = vector.spatialindex(lineLayer)

        ftLine = QgsFeature()
        ftPoly = QgsFeature()
        outFeat = QgsFeature()
        inGeom = QgsGeometry()
        outGeom = QgsGeometry()
        distArea = QgsDistanceArea()

        features = vector.features(polyLayer)
        total = 100.0 / len(features)
        hasIntersections = False
        for current, ftPoly in enumerate(features):
            inGeom = ftPoly.geometry()
            attrs = ftPoly.attributes()
            count = 0
            length = 0
            hasIntersections = False
            lines = spatialIndex.intersects(inGeom.boundingBox())
            engine = None
            if len(lines) > 0:
                hasIntersections = True
                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                engine.prepareGeometry()

            if hasIntersections:
                request = QgsFeatureRequest().setFilterFids(lines).setSubsetOfAttributes([])
                for ftLine in lineLayer.getFeatures(request):
                    tmpGeom = ftLine.geometry()
                    if engine.intersects(tmpGeom.geometry()):
                        outGeom = inGeom.intersection(tmpGeom)
                        length += distArea.measureLength(outGeom)
                        count += 1

            outFeat.setGeometry(inGeom)
            if idxLength == len(attrs):
                attrs.append(length)
            else:
                attrs[idxLength] = length
            if idxCount == len(attrs):
                attrs.append(count)
            else:
                attrs[idxCount] = count
            outFeat.setAttributes(attrs)
            writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))

        del writer
Ejemplo n.º 12
0
    def run(self):
        """Run method that performs all the real work"""

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
            self.dlg = flowTraceDialog()

        # get current selected layer
        clayer = self.iface.mapCanvas().currentLayer()

        # make sure that the selected layer is a QgsVectorLayer of
        # QgsWkbTypes.GeometryType.LineGeometry type before opening the dialog
        if clayer is None or clayer.type() != 0 or clayer.geometryType() != 1:
            return

        # set layer name in dialog
        self.dlg.labelLayer.setText(clayer.name())
        # set number of selected features in dialog
        self.dlg.labelNumFeatures.setText(str(len(clayer.selectedFeatures())))
        # print(self.iface.mapCanvas().mapUnits())
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # get direct from dialog
        direction = self.dlg.downstream_radio_button.isChecked()

        #setup distance
        distance = QgsDistanceArea()
        # the unit of measure will be set to the same as the layer
        # maybe it would be better to set it to the map CRS
        distance.setSourceCrs(clayer.sourceCrs(),
                              QgsProject.instance().transformContext())

        if result:
            # get crs for tolerance setting
            crs = self.iface.activeLayer().crs().authid()
            # print (crs)
            if crs == 'EPSG:4269':
                tolerance = .0001
            else:
                tolerance = self.dlg.SpinBoxTolerance.value()

            #index and create sets from layer
            index = QgsSpatialIndex(clayer)
            selection_set = set(clayer.selectedFeatureIds())
            final_set = selection_set.copy()
            dict_features = {
                feature.id(): feature
                for feature in clayer.getFeatures()
            }

            #loop thru selection set
            while selection_set:
                # get upstream/downstream node of next feature
                feature = dict_features[selection_set.pop()]
                nodes = self.get_geometry(feature.geometry())
                upstream_coord = nodes[0 - direction]

                # select all features around selected node
                # using a bounding box
                upstream_coord_x = upstream_coord.x()
                upstream_coord_y = upstream_coord.y()

                rectangle = QgsRectangle(upstream_coord_x - tolerance,
                                         upstream_coord_y - tolerance,
                                         upstream_coord_x + tolerance,
                                         upstream_coord_y + tolerance)

                ls_fids = index.intersects(rectangle)

                #iterate thru intersected features
                for fid in ls_fids:
                    if fid not in final_set:
                        # get downstream/upstream coordinates
                        feature = dict_features[fid]
                        nodes = self.get_geometry(feature.geometry())
                        downstream_coord = nodes[direction - 1]

                        #get distance between downstream and upstream nodes
                        dist = math.sqrt(
                            (downstream_coord.x() - upstream_coord_x)**2 +
                            (downstream_coord.y() - upstream_coord_y)**2)

                        if dist <= tolerance:
                            # if within tolerance, adds feature to selection and final set
                            # set values being unique, a duplicate won't be created if
                            # already present in the selection set
                            final_set.add(fid)
                            selection_set.add(fid)

            #calculate total length
            list_length = [
                distance.measureLength(dict_features[fid].geometry())
                for fid in final_set
            ]
            total_length = sum(list_length)

            #select features using final_set
            clayer.selectByIds(list(final_set))
            self.iface.mapCanvas().refresh()

            #add message bar about number of features selected and length
            message = self.tr(
                "{} features selected totalling {} {} in length.".format(
                    len(final_set), round(total_length, 2),
                    QgsUnitTypes.toString(distance.lengthUnits())))
            self.iface.messageBar().pushMessage("Flow Trace Completed",
                                                message, 0, 10)
Ejemplo n.º 13
0
    def processAlgorithm(self, progress):
        lineLayer = dataobjects.getObjectFromUri(
            self.getParameterValue(self.LINES))
        polyLayer = dataobjects.getObjectFromUri(
            self.getParameterValue(self.POLYGONS))
        lengthFieldName = self.getParameterValue(self.LEN_FIELD)
        countFieldName = self.getParameterValue(self.COUNT_FIELD)

        (idxLength,
         fieldList) = vector.findOrCreateField(polyLayer, polyLayer.fields(),
                                               lengthFieldName)
        (idxCount,
         fieldList) = vector.findOrCreateField(polyLayer, fieldList,
                                               countFieldName)

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fieldList.toList(), polyLayer.wkbType(), polyLayer.crs())

        spatialIndex = vector.spatialindex(lineLayer)

        ftLine = QgsFeature()
        ftPoly = QgsFeature()
        outFeat = QgsFeature()
        inGeom = QgsGeometry()
        outGeom = QgsGeometry()
        distArea = QgsDistanceArea()

        features = vector.features(polyLayer)
        total = 100.0 / len(features)
        hasIntersections = False
        for current, ftPoly in enumerate(features):
            inGeom = ftPoly.geometry()
            attrs = ftPoly.attributes()
            count = 0
            length = 0
            hasIntersections = False
            lines = spatialIndex.intersects(inGeom.boundingBox())
            engine = None
            if len(lines) > 0:
                hasIntersections = True
                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                engine.prepareGeometry()

            if hasIntersections:
                request = QgsFeatureRequest().setFilterFids(
                    lines).setSubsetOfAttributes([])
                for ftLine in lineLayer.getFeatures(request):
                    tmpGeom = ftLine.geometry()
                    if engine.intersects(tmpGeom.geometry()):
                        outGeom = inGeom.intersection(tmpGeom)
                        length += distArea.measureLength(outGeom)
                        count += 1

            outFeat.setGeometry(inGeom)
            if idxLength == len(attrs):
                attrs.append(length)
            else:
                attrs[idxLength] = length
            if idxCount == len(attrs):
                attrs.append(count)
            else:
                attrs[idxCount] = count
            outFeat.setAttributes(attrs)
            writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))

        del writer
Ejemplo n.º 14
0
    def run(self):
        """Run method that performs all the real work"""

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
            self.dlg = flowTraceDialog()

        # get current selected layer
        clayer = self.iface.mapCanvas().currentLayer()
        # set layer name in dialog
        self.dlg.labelLayer.setText(clayer.name())
        # set number of selected features in dialog
        self.dlg.labelNumFeatures.setText(str(len(clayer.selectedFeatures())))
        # print(self.iface.mapCanvas().mapUnits())
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # get direct from dialog
        direction = self.dlg.downstream_radio_button.isChecked()

        final_list = []
        #add tolerance value

        # setup total length
        totallength = 0.0
        # distance = 0

        #setup distance
        distance = QgsDistanceArea()
        # the unit of measure will be set to the same as the layer
        # maybe it would be better to set it to the map CRS
        distance.setSourceCrs(clayer.sourceCrs(),
                              QgsProject.instance().transformContext())

        if result:
            #setup final selection list

            #setup temporary selection list
            selection_list = []
            #add tolerance value
            tolerance = 1

            #get current layer
            clayer = self.iface.mapCanvas().currentLayer()
            # print (clayer.name())

            if clayer is None:
                return

            #get provider
            provider = clayer.dataProvider()

            #get selected features
            features = clayer.selectedFeatures()

            # get crs for tolerance setting
            crs = self.iface.activeLayer().crs().authid()
            # print (crs)
            if crs == 'EPSG:4269':
                rec = .0001
                tolerance = .0001
            else:
                #rec = .1
                rec = self.dlg.SpinBoxTolerance.value()

            #iterate thru features to add to lists
            for feature in features:
                # add selected features to final list
                final_list.append(feature.id())
                # add selected features to selection list for while loop
                selection_list.append(feature.id())
                #get feature geometry
                geom = feature.geometry()

                totallength = totallength + distance.measureLength(geom)
                # print (QgsDistanceArea.lengthUnits)

                # https://qgis.org/api/classQgsWkbTypes.html
                if geom.type() != 1:
                    print("Geometry not allowed")
                    QMessageBox.information(
                        None, "Flow Trace",
                        "Geometry not allowed, \nPlease select line geometry only."
                    )
                    return

            #loop thru selection list
            while selection_list:

                #get selected features
                request = QgsFeatureRequest().setFilterFid(selection_list[0])
                # request = QgsFeatureRequest()
                feature = next(clayer.getFeatures(request))
                geom = feature.geometry()

                # get nodes
                nodes = self.get_geometry(feature.geometry())

                # get upstream node
                if direction:
                    upstream_coord = nodes[-1]
                    # print (upstream_coord)
                else:
                    upstream_coord = nodes[0]
                    # print (upstream_coord)

                # select all features around upstream coordinate
                # using a bounding box
                rectangle = QgsRectangle(upstream_coord.x() - rec,
                                         upstream_coord.y() - rec,
                                         upstream_coord.x() + rec,
                                         upstream_coord.y() + rec)
                # rectangle = QgsRectangle (minx, miny, maxx, maxy)
                request = QgsFeatureRequest().setFilterRect(rectangle)
                features = clayer.getFeatures(request)

                #iterate thru requested features
                for feature in features:
                    # get nodes
                    nodes = self.get_geometry(feature.geometry())
                    #downstream_coord = nodes[-1]

                    # get upstream node
                    if direction:
                        downstream_coord = nodes[0]
                        # print (upstream_coord)
                    else:
                        downstream_coord = nodes[-1]
                        # print (upstream_coord)

                    #get distance from downstream node to upstream node
                    dist = distance.measureLine(downstream_coord,
                                                upstream_coord)

                    if dist < tolerance:
                        #add feature to final list
                        final_list.append(feature.id())

                        if feature.id() not in selection_list:
                            #add feature to selection list
                            selection_list.append(feature.id())
                            # Length from Line
                            totallength = totallength + distance.measureLength(
                                feature.geometry())

                #remove feature from selection list
                selection_list.pop(0)

        #select features using final_list
        for fid in final_list:
            clayer.select(fid)

        #refresh the canvas
        self.iface.mapCanvas().refresh()

        #add message box about length and number of features
        QMessageBox.information(
            None, "Flow Trace Complete",
            "Total Features Selected: " + str(len(final_list)) + "\r\n" +
            " Length: " + str(round(totallength, 2)) + ' ' +
            QgsUnitTypes.toString(distance.lengthUnits()))
Ejemplo n.º 15
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.º 16
0
    def write_line_length(self,
                          layer,
                          ellipsoid,
                          transform_context,
                          m=False,
                          km=False,
                          nm=False):
        '''Write length attribute [m/km/nm] to layer

        Parameters
        ----------
        layer : QgsVectorLayer
            input line vector layer
        ellipsoid : str
            QgsCoordinateReferenceSystem.ellipsoidAcronym()
        transform_context : QgsCoordinateTransformContext
            transform context for coordinate transformation
        m : boolean
            write meters (Default value = False)
        km : boolean
            write kilometers (Default value = False)
        nm : boolean
            write nautical miles (Default value = False)

        Returns
        -------
        error : boolean
            0/1 - no error/error
        result : str or None
            output or error msg if error == 1

        '''
        # if no lengths shall be written, return
        if m == km == nm == False:
            return 1, 'No length units selected, no attributes created!\n'

        # set some field names
        prefix = 'length_'
        m_field = f'{prefix}m'
        km_field = f'{prefix}km'
        nm_field = f'{prefix}nm'

        # get CRS of input layer
        crs_layer = layer.crs()

        # Initialize Distance calculator class with ellipsoid
        da = QgsDistanceArea()
        da.setSourceCrs(crs_layer, transform_context)
        da.setEllipsoid(ellipsoid)

        with edit(layer):
            # delete fields previously created by Cruise Tools
            self.delete_fields_by_prefix(layer, prefix)

            # create fields for length_m and/or length_nm
            if m:
                layer.addAttribute(
                    QgsField(m_field, QVariant.Double, len=15, prec=2))
            if km:
                layer.addAttribute(
                    QgsField(km_field, QVariant.Double, len=15, prec=3))
            if nm:
                layer.addAttribute(
                    QgsField(nm_field, QVariant.Double, len=15, prec=3))

            # update attribute table fields
            layer.updateFields()

            # get all features
            features = self.get_features(layer, selected=False)

            for feature in features:
                # get geometry of feature
                geom = feature.geometry()

                # measure feature length in meters
                len = da.measureLength(geom)

                # set field values according to the calculated length
                if m:
                    len_m = da.convertLengthMeasurement(
                        len, QgsUnitTypes.DistanceMeters)
                    len_m = round(len_m, 2)
                    feature.setAttribute(layer.fields().indexFromName(m_field),
                                         len_m)
                if km:
                    len_km = da.convertLengthMeasurement(
                        len, QgsUnitTypes.DistanceKilometers)
                    len_km = round(len_km, 5)
                    feature.setAttribute(
                        layer.fields().indexFromName(km_field), len_km)
                if nm:
                    len_nm = da.convertLengthMeasurement(
                        len, QgsUnitTypes.DistanceNauticalMiles)
                    len_nm = round(len_nm, 5)
                    feature.setAttribute(
                        layer.fields().indexFromName(nm_field), len_nm)

                # check if speed_kn exists
                f_idx_speed = layer.fields().indexFromName('speed_kn')
                f_idx_time = layer.fields().indexFromName('time_h')
                if (f_idx_speed != -1) and (f_idx_time != -1):
                    # if yes, get value
                    speed_kn = feature.attributes()[f_idx_speed]
                    if speed_kn != None:
                        # if value not NULL, calculate time and write it to time_h field
                        len_nm = da.convertLengthMeasurement(
                            len, QgsUnitTypes.DistanceNauticalMiles)
                        time_h = round(len_nm / speed_kn, 2)
                        feature.setAttribute(f_idx_time, time_h)

                # update attribute table
                layer.updateFeature(feature)

        return 0, None
Ejemplo n.º 17
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.º 18
0
    def run(self):
        """Run method that performs all the real work"""

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
            self.dlg = BathCreatorDialog()

        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            # Get Values from GUI
            POLYGON_LAYER_NAME = self.dlg.polygone_value
            DEM_LAYER_NAME = self.dlg.dem_value
            DEM_BAND = '1'
            DELTA = float(self.dlg.delta_value)
            OUTPUT_FILE_NAME = self.dlg.csv_value
            LINE_LAYER = self.dlg.line_value

            # Input check
            ret = self._checkInput(POLYGON_LAYER_NAME, LINE_LAYER)
            if ret == False:
                return False

            # Start work
            layer = QgsProject.instance().mapLayersByName(LINE_LAYER)
            features = layer[0].getFeatures()
            d = QgsDistanceArea()
            data = []
            for feat in features:
                data_dic = {}

                # Read fields from the vector line
                data_dic['SEGMENT'] = feat["SEGMENT"]
                data_dic['ELWS'] = feat["ELWS"]
                data_dic['FRIC'] = feat["FRIC"]

                # Get vector line length
                data_dic['DLX'] = round(d.measureLength(feat.geometry()), 2)

                # Calculate line angle
                data_dic['PHI0'] = self._calculateAangle(feat)

                data.append(data_dic)
            QgsMessageLog.logMessage('Data collected from features: ' +
                                     str(data),
                                     tag="Create_Bathymetry",
                                     level=Qgis.Info)

            # Calculate volume by the cells value in each buffer
            histograms = processing.run(
                "native:zonalhistogram", {
                    'INPUT_RASTER': DEM_LAYER_NAME,
                    'RASTER_BAND': DEM_BAND,
                    'INPUT_VECTOR': POLYGON_LAYER_NAME,
                    'COLUMN_PREFIX': '',
                    'OUTPUT': 'memory:'
                })
            dem = QgsProject.instance().mapLayersByName(DEM_LAYER_NAME)
            cell_size = dem[0].rasterUnitsPerPixelX(
            ) * dem[0].rasterUnitsPerPixelY()  # get cell size m^2

            # remove all none number fields (like SEGMENT or anything else in the layer)
            fields_list = histograms['OUTPUT'].fields().names()
            heights_list = []
            for i in fields_list:
                try:
                    f = float(i)
                except:
                    pass
                else:
                    heights_list.append(f)
            features = histograms['OUTPUT'].getFeatures()
            volume_data = calcVolumes(heights_list, features, DELTA,
                                      cell_size)  # output is list of dic
            QgsMessageLog.logMessage('Volume clculation summary: ' +
                                     str(volume_data),
                                     tag="Create_Bathymetry",
                                     level=Qgis.Info)

            # Calculate width
            width_data = self._calcWidth(data, volume_data, DELTA)

            # Get the border segments into the data
            sorted_width = sorted(width_data, key=lambda k: k['SEGMENT'])
            n = len(sorted_width[0]['data'])  # number of layers
            sorted_width.insert(0, {
                'SEGMENT': 1,
                'data': [0] * n
            })  # insert first empty segment
            data.append({
                'SEGMENT': 1,
                'ELWS': '0',
                'FRIC': '0',
                'DLX': '0',
                'PHI0': '0'
            })
            seq = [x['SEGMENT'] for x in sorted_width]
            for i in range(
                    max(seq)
            ):  #Iterate over all segments and add the missing border empty segments
                if i + 1 != sorted_width[i]['SEGMENT']:
                    sorted_width.insert(i, {'SEGMENT': i + 1, 'data': [0] * n})
                    data.append({
                        'SEGMENT': i + 1,
                        'ELWS': '0',
                        'FRIC': '0',
                        'DLX': '0',
                        'PHI0': '0'
                    })
                    sorted_width.insert(i + 1, {
                        'SEGMENT': i + 2,
                        'data': [0] * n
                    })
                    data.append({
                        'SEGMENT': i + 2,
                        'ELWS': '0',
                        'FRIC': '0',
                        'DLX': '0',
                        'PHI0': '0'
                    })
            sorted_width.insert(i + 1, {'SEGMENT': i + 2, 'data': [0] * n})
            data.append({
                'SEGMENT': i + 2,
                'ELWS': '0',
                'FRIC': '0',
                'DLX': '0',
                'PHI0': '0'
            })  # insert last empty segment

            regular_list = [
            ]  # simplify the data structure before writing the csv
            for d in range(len(sorted_width)):
                regular_list.append(sorted_width[d]['data'])
            ret = self._writeExcel(data, regular_list, DELTA, OUTPUT_FILE_NAME)
            QgsMessageLog.logMessage(message='Execution finished ',
                                     tag="Create_Bathymetry",
                                     level=Qgis.Info)
            if ret == True:
                iface.messageBar().pushMessage(
                    "Whoo",
                    "Finished creating the bathymetric file",
                    level=Qgis.Success,
                    duration=10)
Ejemplo n.º 19
0
    def processAlgorithm(self, parameters, context, feedback):
        # get input variables
        source = self.parameterAsSource(parameters, self.INPUT_LINE, context)
        swath_angle_field = self.parameterAsString(parameters,
                                                   self.SWATH_ANGLE_FIELD,
                                                   context)
        swath_angle_fallback = self.parameterAsInt(parameters,
                                                   self.SWATH_ANGLE, context)
        raster_layer = self.parameterAsRasterLayer(parameters,
                                                   self.INPUT_RASTER, context)
        band_number = self.parameterAsInt(parameters, self.BAND, context)

        # copy of the field name for later
        swath_angle_field_name = swath_angle_field

        # set new default values in config
        feedback.pushConsoleInfo(
            self.tr(f'Storing new default settings in config...'))
        self.config.set(self.module, 'swath_angle', swath_angle_fallback)

        # get crs's
        crs_line = source.sourceCrs()
        crs_raster = raster_layer.crs()

        # get project transform_context
        transform_context = context.transformContext()

        # CRS transformation to "WGS84/World Mercator" for MBES coverage operations
        crs_mercator = QgsCoordinateReferenceSystem('EPSG:3395')
        trans_line2merc = QgsCoordinateTransform(crs_line, crs_mercator,
                                                 transform_context)

        # CRS transformation to raster layer CRS for depth sampling
        trans_merc2raster = QgsCoordinateTransform(crs_mercator, crs_raster,
                                                   transform_context)
        trans_line2raster = QgsCoordinateTransform(crs_line, crs_raster,
                                                   transform_context)
        crs_geo = QgsCoordinateReferenceSystem('EPSG:4326')
        trans_line2geo = QgsCoordinateTransform(crs_line, crs_geo,
                                                transform_context)

        # initialize distance tool
        da = QgsDistanceArea()
        da.setSourceCrs(crs_mercator, transform_context)
        da.setEllipsoid(crs_mercator.ellipsoidAcronym())

        # empty lists for unioned buffers
        buffer_union_list = []

        # get (selected) features
        features = source.getFeatures()

        # feedback
        total = 100.0 / source.featureCount() if source.featureCount() else 0

        # loop through features
        feedback.pushConsoleInfo(self.tr(f'Densifying line features...'))
        feedback.pushConsoleInfo(self.tr(f'Extracting vertices...'))
        feedback.pushConsoleInfo(self.tr(f'Sampling values...'))
        feedback.pushConsoleInfo(
            self.tr(f'Computing depth dependent buffers...'))
        feedback.pushConsoleInfo(self.tr(f'Unionizing output features...'))
        for feature_id, feature in enumerate(features):
            # get feature geometry
            feature_geom = feature.geometry()

            # check for LineString geometry
            if QgsWkbTypes.isSingleType(feature_geom.wkbType()):
                # get list of vertices
                vertices_list = feature_geom.asPolyline()
                vertices_list = [vertices_list]

            # check for MultiLineString geometry
            elif QgsWkbTypes.isMultiType(feature_geom.wkbType()):
                # get list of list of vertices per multiline part
                vertices_list = feature_geom.asMultiPolyline()

            for part_id, vertices in enumerate(vertices_list):
                # transform vertices CRS to "WGS 84 / World Mercator"
                vertices_t = []
                for vertex in vertices:
                    vertex_trans = trans_line2merc.transform(vertex)
                    vertices_t.append(vertex_trans)

                # get centroid as point for UTM zone selection
                centroid = feature_geom.centroid()
                centroid_point = centroid.asPoint()

                # check if centroid needs to be transformed to get x/y in lon/lat
                if not crs_line.isGeographic():
                    centroid_point = trans_line2geo.transform(centroid_point)

                # get UTM zone of feature for buffering
                lat, lon = centroid_point.y(), centroid_point.x()
                crs_utm = self.get_UTM_zone(lat, lon)

                # create back and forth transformations for later
                trans_merc2utm = QgsCoordinateTransform(
                    crs_mercator, crs_utm, transform_context)
                trans_utm2line = QgsCoordinateTransform(
                    crs_utm, crs_line, transform_context)

                # split line into segments
                for segment_id in range(len(vertices_t) - 1):
                    # ===== (1) DENSIFY LINE VERTICES =====
                    # create new Polyline geometry for line segment
                    segment_geom = QgsGeometry.fromPolylineXY(
                        [vertices_t[segment_id], vertices_t[segment_id + 1]])

                    # measure ellipsoidal distance between start and end vertex
                    segment_length = da.measureLength(segment_geom)

                    # calculate number of extra vertices to insert
                    extra_vertices = int(segment_length //
                                         self.vertex_distance)

                    # create additional vertices along line segment
                    segment_geom_dense = segment_geom.densifyByCount(
                        extra_vertices - 1)

                    # initialize additional fields
                    feature_id_field = QgsField('feature_id',
                                                QVariant.Int,
                                                'Integer',
                                                len=5,
                                                prec=0)
                    part_id_field = QgsField('part_id',
                                             QVariant.Int,
                                             'Integer',
                                             len=5,
                                             prec=0)
                    segment_id_field = QgsField('segment_id',
                                                QVariant.Int,
                                                'Integer',
                                                len=5,
                                                prec=0)

                    # list for segment buffers
                    buffer_list = []

                    # loop over all vertices of line segment
                    for i, vertex in enumerate(segment_geom_dense.vertices()):
                        # === CREATE POINT FEATURE ===
                        # initialize feature and set geometry
                        fpoint = QgsFeature()
                        fpoint.setGeometry(vertex)

                        # sample bathymetry grid
                        # get point geometry (as QgsPointXY)
                        fpoint_geom = fpoint.geometry()
                        pointXY = fpoint_geom.asPoint()

                        # transform point to raster CRS
                        pointXY_raster = trans_merc2raster.transform(
                            pointXY)  #trans_line2raster.transform(pointXY)

                        # sample raster at point location
                        pointXY_depth, error_check = raster_layer.dataProvider(
                        ).sample(pointXY_raster, 1)

                        # check if valid depth was sampled, otherwise skip point
                        if error_check == False:
                            continue

                        # create depth-dependant buffer:
                        # check if swath_angle field is selected
                        if swath_angle_field != '':
                            # get value from field
                            swath_angle_field_value = feature.attribute(
                                swath_angle_field)
                            # check if value is set (not NOLL)
                            if swath_angle_field_value == None:
                                # if NULL, set fallback
                                swath_angle = swath_angle_fallback
                            else:
                                # othervise take value from field
                                swath_angle = swath_angle_field_value
                        # or if no field was selected use fallback value right away
                        else:
                            swath_angle = swath_angle_fallback

                        # calculate buffer radius (swath width from depth and swath angle)
                        buffer_radius = round(
                            tan(radians(swath_angle / 2)) * abs(pointXY_depth),
                            0)

                        # transform point from mercator zu UTM
                        fpoint_geom.transform(trans_merc2utm)

                        # create buffer
                        buffer = fpoint_geom.buffer(buffer_radius, 10)

                        # transform buffer back to initial input CRS
                        buffer.transform(trans_utm2line)

                        # store buffer in list
                        buffer_list.append(buffer)

                    # check if any points in this segment have been sampled
                    if buffer_list == []:
                        continue

                    # dissolve point buffers of line segment:
                    # dissolve all polygons based on line vertices into single feature
                    buffer_union = QgsGeometry().unaryUnion(buffer_list)

                    # set fields and attributes:
                    # empty fields
                    buffer_fields = QgsFields()

                    # loop through line feature fields
                    for field in feature.fields():
                        # and append all but the 'fid' field (if it exists)
                        if field.name() != 'fid':
                            buffer_fields.append(field)

                    # append extra buffer fields (intial feature id, part id and segment id
                    for buffer_field in [
                            feature_id_field, part_id_field, segment_id_field
                    ]:
                        buffer_fields.append(buffer_field)

                    # if no input swath_angle field was selected on input, create one
                    if swath_angle_field == '':
                        swath_angle_field_name = 'mbes_swath_angle'
                        buffer_fields.append(
                            QgsField(swath_angle_field_name,
                                     QVariant.Int,
                                     'Integer',
                                     len=5,
                                     prec=0))

                    # initialize polygon feature
                    fpoly = QgsFeature(buffer_fields)

                    # set attributes for polygon feature
                    for field in feature.fields():
                        # ignore 'fid' again
                        if field.name() != 'fid':
                            # set attribute from feature to buffer
                            fpoly.setAttribute(field.name(),
                                               feature.attribute(field.name()))

                    # set addtional buffer fields
                    fpoly.setAttribute('feature_id', feature_id)
                    fpoly.setAttribute('part_id', part_id)
                    fpoly.setAttribute('segment_id', segment_id)
                    fpoly.setAttribute(swath_angle_field_name, swath_angle)

                    # set geometry
                    fpoly.setGeometry(buffer_union)

                    # store segment coverage polygon
                    if fpoly.hasGeometry() and fpoly.isValid():
                        buffer_union_list.append(fpoly)

            # set progess
            feedback.setProgress(int(feature_id * total))

        # if buffer_union_list is empty, no buffer features where created
        if buffer_union_list == []:
            raise Exception(
                'No depth values could be sampled from the input raster!')

        # creating feature sink
        feedback.pushConsoleInfo(self.tr(f'Creating feature sink...'))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, buffer_fields,
                                               QgsWkbTypes.MultiPolygon,
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        # write coverage features to sink
        feedback.pushConsoleInfo(self.tr(f'Writing features...'))
        sink.addFeatures(buffer_union_list, QgsFeatureSink.FastInsert)

        # make variables accessible for post processing
        self.output = dest_id

        result = {self.OUTPUT: self.output}

        return result
Ejemplo n.º 20
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:
            if QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('numparts', QVariant.Int))
            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):
        attrs = []
        if not geometry.isMultipart():
            pt = geometry.constGet()
            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())
        else:
            attrs = [geometry.constGet().numGeometries()]
        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.º 21
0
    def processAlgorithm(self, parameters, context, feedback):
        line_source = self.parameterAsSource(parameters, self.LINES, context)
        poly_source = self.parameterAsSource(parameters, self.POLYGONS,
                                             context)

        length_field_name = self.parameterAsString(parameters, self.LEN_FIELD,
                                                   context)
        count_field_name = self.parameterAsString(parameters, self.COUNT_FIELD,
                                                  context)

        fields = poly_source.fields()
        if fields.lookupField(length_field_name) < 0:
            fields.append(QgsField(length_field_name, QVariant.Double))
        length_field_index = fields.lookupField(length_field_name)
        if fields.lookupField(count_field_name) < 0:
            fields.append(QgsField(count_field_name, QVariant.Int))
        count_field_index = fields.lookupField(count_field_name)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields,
                                               poly_source.wkbType(),
                                               poly_source.sourceCrs())

        spatialIndex = QgsSpatialIndex(
            line_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(
                []).setDestinationCrs(poly_source.sourceCrs())), feedback)

        distArea = QgsDistanceArea()
        distArea.setSourceCrs(poly_source.sourceCrs())
        distArea.setEllipsoid(context.project().ellipsoid())

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

            output_feature = QgsFeature()
            count = 0
            length = 0
            if poly_feature.hasGeometry():
                poly_geom = poly_feature.geometry()
                has_intersections = False
                lines = spatialIndex.intersects(poly_geom.boundingBox())
                engine = None
                if len(lines) > 0:
                    has_intersections = True
                    # use prepared geometries for faster intersection tests
                    engine = QgsGeometry.createGeometryEngine(
                        poly_geom.constGet())
                    engine.prepareGeometry()

                if has_intersections:
                    request = QgsFeatureRequest().setFilterFids(
                        lines).setSubsetOfAttributes([]).setDestinationCrs(
                            poly_source.sourceCrs())
                    for line_feature in line_source.getFeatures(request):
                        if feedback.isCanceled():
                            break

                        if engine.intersects(
                                line_feature.geometry().constGet()):
                            outGeom = poly_geom.intersection(
                                line_feature.geometry())
                            length += distArea.measureLength(outGeom)
                            count += 1

                output_feature.setGeometry(poly_geom)

            attrs = poly_feature.attributes()
            if length_field_index == len(attrs):
                attrs.append(length)
            else:
                attrs[length_field_index] = length
            if count_field_index == len(attrs):
                attrs.append(count)
            else:
                attrs[count_field_index] = count
            output_feature.setAttributes(attrs)
            sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 22
0
    def processAlgorithm(self, parameters, context, feedback):
        line_source = self.parameterAsSource(parameters, self.LINES, context)
        if line_source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.LINES))

        poly_source = self.parameterAsSource(parameters, self.POLYGONS, context)
        if poly_source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.POLYGONS))

        length_field_name = self.parameterAsString(parameters, self.LEN_FIELD, context)
        count_field_name = self.parameterAsString(parameters, self.COUNT_FIELD, context)

        fields = poly_source.fields()
        if fields.lookupField(length_field_name) < 0:
            fields.append(QgsField(length_field_name, QVariant.Double))
        length_field_index = fields.lookupField(length_field_name)
        if fields.lookupField(count_field_name) < 0:
            fields.append(QgsField(count_field_name, QVariant.Int))
        count_field_index = fields.lookupField(count_field_name)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, poly_source.wkbType(), poly_source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))

        spatialIndex = QgsSpatialIndex(line_source.getFeatures(
            QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs(), context.transformContext())), feedback)

        distArea = QgsDistanceArea()
        distArea.setSourceCrs(poly_source.sourceCrs(), context.transformContext())
        distArea.setEllipsoid(context.project().ellipsoid())

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

            output_feature = QgsFeature()
            count = 0
            length = 0
            if poly_feature.hasGeometry():
                poly_geom = poly_feature.geometry()
                has_intersections = False
                lines = spatialIndex.intersects(poly_geom.boundingBox())
                engine = None
                if len(lines) > 0:
                    has_intersections = True
                    # use prepared geometries for faster intersection tests
                    engine = QgsGeometry.createGeometryEngine(poly_geom.constGet())
                    engine.prepareGeometry()

                if has_intersections:
                    request = QgsFeatureRequest().setFilterFids(lines).setSubsetOfAttributes([]).setDestinationCrs(poly_source.sourceCrs(), context.transformContext())
                    for line_feature in line_source.getFeatures(request):
                        if feedback.isCanceled():
                            break

                        if engine.intersects(line_feature.geometry().constGet()):
                            outGeom = poly_geom.intersection(line_feature.geometry())
                            length += distArea.measureLength(outGeom)
                            count += 1

                output_feature.setGeometry(poly_geom)

            attrs = poly_feature.attributes()
            if length_field_index == len(attrs):
                attrs.append(length)
            else:
                attrs[length_field_index] = length
            if count_field_index == len(attrs):
                attrs.append(count)
            else:
                attrs[count_field_index] = count
            output_feature.setAttributes(attrs)
            sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Ejemplo n.º 23
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))

        pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context)
        minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
        MaxTriesPerPoint = self.parameterAsDouble(parameters, self.MAXTRIESPERPOINT, 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))
      
        totNPoints = 0  # The total number of points generated
        featureCount = source.featureCount()
        total = 100.0 / (pointCount * featureCount) if pointCount else 1
        random.seed()

        index = QgsSpatialIndex()
        points = dict()

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

        maxIterations = pointCount * MaxTriesPerPoint
        for f in source.getFeatures():
            lineGeoms = []
            lineCount = 0
            fGeom = f.geometry()
            feedback.pushInfo('fGeom: ' + str(fGeom))
            totLineLength = da.measureLength(fGeom)
            feedback.pushInfo('fGeom totLineLength: ' + str(totLineLength))
            # Explode multi part
            if fGeom.isMultipart():
                for aLine in fGeom.asMultiPolyline():
                    lineGeoms.append(aLine)
                #lines = fGeom.asMultiPolyline()
                # pick random line
                #lineId = random.randint(0, len(lines) - 1)
                #vertices = lines[lineId]
            else:
                lineGeoms.append(fGeom.asPolyline())
                #vertices = fGeom.asPolyline()
            feedback.pushInfo('lineGeoms: ' + str(lineGeoms))


            # Generate points on the line geometry / geometries
            nPoints = 0
            nIterations = 0
            while nIterations < maxIterations and nPoints < pointCount:
                if feedback.isCanceled():
                    break
                #feedback.pushInfo('nIterations: ' + str(nIterations))
                # Get the random "position" for this point
                randomLength = random.random() * totLineLength
                feedback.pushInfo('randomLength: ' + str(randomLength))
                currLength = 0
                prefLength = 0
                # Go through the parts
                for l in lineGeoms:
                    if feedback.isCanceled():
                        break
                    currGeom = QgsGeometry.fromPolylineXY(l)
                    #lineLength = da.measureLength(QgsGeometry.fromPolylineXY(l))
                    lineLength = da.measureLength(currGeom)
                    prevLength = currLength
                    currLength += lineLength
                    feedback.pushInfo('l lineLength: ' + str(lineLength) + ' currLength: ' + str(currLength))
                    vertices = l
                    # Skip if this is not the "selected" part
                    if currLength < randomLength:
                        continue
                    #randomLength -= currLength
                    #vertices = QgsGeometry.fromPolylineXY(l)
                    feedback.pushInfo('l/vertices: ' + str(vertices))

                    #randomLength = random.random() * lineLength
                    #distanceToVertex(vid)

                    # find the segment for the new point
                    # and calculate the offset (remainDistance) on that segment
                    remainDist = randomLength - prevLength
                    feedback.pushInfo('remainDist1: ' + str(remainDist))
                    if len(vertices) == 2:
                        vid = 0
                        #remainDist = randomLength - currLength
                    else:
                        vid = 0
                        #while (fGeom.distanceToVertex(vid)) < randomLength:
                        #while (currGeom.distanceToVertex(vid)) < randomLength:
                        currDist = currGeom.distanceToVertex(vid)
                        prevDist = currDist
                        while currDist < remainDist and vid < len(vertices):
                            vid += 1
                            prevDist = currDist
                            currDist = currGeom.distanceToVertex(vid)
                            feedback.pushInfo('currdist: ' + str(currDist) + ' vid: ' + str(vid))
                        if vid == len(vertices):
                            feedback.pushInfo('**** vid = len(vertices)! ****')
                        vid -= 1
                        feedback.pushInfo('currdist2: ' + str(currDist) + ' vid: ' + str(vid))
                        remainDist = remainDist - prevDist
                    feedback.pushInfo('remainDist2: ' + str(remainDist))
                    if remainDist <= 0:
                        continue
                    startPoint = vertices[vid]
                    endPoint = vertices[vid + 1]
                    length = da.measureLine(startPoint, endPoint)
                    # if remainDist > minDistance:
                    d = remainDist / (length - remainDist)
                    rx = (startPoint.x() + d * endPoint.x()) / (1 + d)
                    ry = (startPoint.y() + d * endPoint.y()) / (1 + d)

                    # generate random point
                    p = QgsPointXY(rx, ry)
                    geom = QgsGeometry.fromPointXY(p)
                    if vector.checkMinDistance(p, index, minDistance, points):
                        f = QgsFeature(totNPoints)
                        f.initAttributes(1)
                        f.setFields(fields)
                        f.setAttribute('id', totNPoints)
                        f.setGeometry(geom)
                        sink.addFeature(f, QgsFeatureSink.FastInsert)
                        index.addFeature(f)
                        points[nPoints] = p
                        nPoints += 1
                        totNPoints += 1
                        feedback.setProgress(int(totNPoints * total))
                    break
                nIterations += 1

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

        return {self.OUTPUT: dest_id, self.OUTPUT_POINTS: nPoints}
Ejemplo n.º 24
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        # Retrieve the feature source and sink. The 'dest_id' variable is used
        # to uniquely identify the feature sink, and must be included in the
        # dictionary returned by the processAlgorithm function.
        source = self.parameterAsSource(parameters, self.INPUT, context)
        nearest_source = self.parameterAsSource(parameters, self.NEAREST,
                                                context)

        # If source was not found, throw an exception to indicate that the algorithm
        # encountered a fatal error. The exception text can be any string, but in this
        # case we use the pre-built invalidSourceError method to return a standard
        # helper text for when a source cannot be evaluated
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))
        if nearest_source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.NEAREST))

        output_fields = QgsProcessingUtils.combineFields(
            source.fields(), nearest_source.fields(), 'nearest_')
        output_fields.append(QgsField('distance', QVariant.Double))

        # create an empty layer for the results to go into
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, output_fields,
                                               QgsWkbTypes.LineString,
                                               source.sourceCrs())

        # If sink was not created, throw an exception to indicate that the algorithm
        # encountered a fatal error. The exception text can be any string, but in this
        # case we use the pre-built invalidSinkError method to return a standard
        # helper text for when a sink cannot be evaluated
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        # Compute the number of steps to display within the progress bar and
        # get features from source
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()

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

        for current, feature in enumerate(features):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            # feature is our polygon feature we need to join to something else
            # find the nearest feature in the other layer

            # create a line which joins ours polygon to the nearest feature
            # (shortest line possible!)

            shortest_line = None
            shortest_line_length = 99999999999999
            closest_feature = None
            for candidate_feature in nearest_source.getFeatures(
                    QgsFeatureRequest().setDestinationCrs(
                        source.sourceCrs(), context.transformContext())):
                line = feature.geometry().shortestLine(
                    candidate_feature.geometry())
                line_length = da.measureLength(line)
                if line_length < shortest_line_length:
                    shortest_line = line
                    shortest_line_length = line_length
                    closest_feature = candidate_feature

            # now shortest_line is the best one!
            # create the output feature
            output_feature = QgsFeature()
            output_feature.setGeometry(shortest_line)
            attrs = feature.attributes()
            attrs.extend(closest_feature.attributes())
            attrs.append(shortest_line_length)
            output_feature.setAttributes(attrs)

            # Add a feature in the sink
            sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

            # Update the progress bar
            feedback.setProgress(int(current * total))

        # Return the results of the algorithm. In this case our only result is
        # the feature sink which contains the processed features, but some
        # algorithms may return multiple feature sinks, calculated numeric
        # statistics, etc. These should all be included in the returned
        # dictionary, with keys matching the feature corresponding parameter
        # or output names.
        return {self.OUTPUT: dest_id}