def testAreaMeasureAndUnits(self): """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the calculated areas and units are always consistent """ da = QgsDistanceArea() da.setSourceCrs(3452) da.setEllipsoidalMode(False) da.setEllipsoid("NONE") daCRS = QgsCoordinateReferenceSystem() daCRS = da.sourceCrs() polygon = QgsGeometry.fromPolygon( [[ QgsPoint(0, 0), QgsPoint(1, 0), QgsPoint(1, 1), QgsPoint(2, 1), QgsPoint(2, 2), QgsPoint(0, 2), QgsPoint(0, 0), ]] ) # We check both the measured area AND the units, in case the logic regarding # ellipsoids and units changes in future area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters)) da.setEllipsoid("WGS84") area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters)) da.setEllipsoidalMode(True) area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) # should always be in Meters Squared self.assertAlmostEqual(area, 37416879192.9, delta=0.1) self.assertEqual(units, QgsUnitTypes.AreaSquareMeters) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareMiles) self.assertAlmostEqual(area, 14446.7378, delta=0.001) # now try with a source CRS which is in feet polygon = QgsGeometry.fromPolygon( [[ QgsPoint(1850000, 4423000), QgsPoint(1851000, 4423000), QgsPoint(1851000, 4424000), QgsPoint(1852000, 4424000), QgsPoint(1852000, 4425000), QgsPoint(1851000, 4425000), QgsPoint(1850000, 4423000) ]] ) da.setSourceCrs(27469) da.setEllipsoidalMode(False) # measurement should be in square feet area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(area, 2000000, delta=0.001) self.assertEqual(units, QgsUnitTypes.AreaSquareFeet) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards) self.assertAlmostEqual(area, 222222.2222, delta=0.001) da.setEllipsoidalMode(True) # now should be in Square Meters again area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(area, 184149.37, delta=1.0) self.assertEqual(units, QgsUnitTypes.AreaSquareMeters) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards) self.assertAlmostEqual(area, 220240.8172549, delta=1.0)
class SizeCalculator(): """Special object to handle size calculation with an output unit.""" def __init__( self, coordinate_reference_system, geometry_type, exposure_key): """Constructor for the size calculator. :param coordinate_reference_system: The Coordinate Reference System of the layer. :type coordinate_reference_system: QgsCoordinateReferenceSystem :param exposure_key: The geometry type of the layer. :type exposure_key: qgis.core.QgsWkbTypes.GeometryType """ self.calculator = QgsDistanceArea() self.calculator.setSourceCrs( coordinate_reference_system, QgsProject.instance().transformContext() ) self.calculator.setEllipsoid('WGS84') if geometry_type == QgsWkbTypes.LineGeometry: self.default_unit = unit_metres LOGGER.info('The size calculator is set to use {unit}'.format( unit=distance_unit[self.calculator.lengthUnits()])) else: self.default_unit = unit_square_metres LOGGER.info('The size calculator is set to use {unit}'.format( unit=distance_unit[self.calculator.areaUnits()])) self.geometry_type = geometry_type self.output_unit = None if exposure_key: exposure_definition = definition(exposure_key) self.output_unit = exposure_definition['size_unit'] def measure_distance(self, point_a, point_b): """Measure the distance between two points. This is added here since QgsDistanceArea object is already called here. :param point_a: First Point. :type point_a: QgsPoint :param point_b: Second Point. :type point_b: QgsPoint :return: The distance between input points. :rtype: float """ return self.calculator.measureLine(point_a, point_b) def measure(self, geometry): """Measure the length or the area of a geometry. :param geometry: The geometry. :type geometry: QgsGeometry :return: The geometric size in the expected exposure unit. :rtype: float """ message = 'Size with NaN value : geometry valid={valid}, WKT={wkt}' feature_size = 0 if geometry.isMultipart(): # Be careful, the size calculator is not working well on a # multipart. # So we compute the size part per part. See ticket #3812 for single in geometry.asGeometryCollection(): if self.geometry_type == QgsWkbTypes.LineGeometry: geometry_size = self.calculator.measureLength(single) else: geometry_size = self.calculator.measureArea(single) if not isnan(geometry_size): feature_size += geometry_size else: LOGGER.debug(message.format( valid=single.isGeosValid(), wkt=single.asWkt())) else: if self.geometry_type == QgsWkbTypes.LineGeometry: geometry_size = self.calculator.measureLength(geometry) else: geometry_size = self.calculator.measureArea(geometry) if not isnan(geometry_size): feature_size = geometry_size else: LOGGER.debug(message.format( valid=geometry.isGeosValid(), wkt=geometry.asWkt())) feature_size = round(feature_size) if self.output_unit: if self.output_unit != self.default_unit: feature_size = convert_unit( feature_size, self.default_unit, self.output_unit) return feature_size
def testAreaMeasureAndUnits(self): """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the calculated areas and units are always consistent """ da = QgsDistanceArea() da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") polygon = QgsGeometry.fromPolygonXY( [[ QgsPointXY(0, 0), QgsPointXY(1, 0), QgsPointXY(1, 1), QgsPointXY(2, 1), QgsPointXY(2, 2), QgsPointXY(0, 2), QgsPointXY(0, 0), ]] ) # We check both the measured area AND the units, in case the logic regarding # ellipsoids and units changes in future area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters)) da.setEllipsoid("WGS84") area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) # should always be in Meters Squared self.assertAlmostEqual(area, 36918093794.121284, delta=0.1) self.assertEqual(units, QgsUnitTypes.AreaSquareMeters) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareMiles) self.assertAlmostEqual(area, 14254.155703182701, delta=0.001) # now try with a source CRS which is in feet polygon = QgsGeometry.fromPolygonXY( [[ QgsPointXY(1850000, 4423000), QgsPointXY(1851000, 4423000), QgsPointXY(1851000, 4424000), QgsPointXY(1852000, 4424000), QgsPointXY(1852000, 4425000), QgsPointXY(1851000, 4425000), QgsPointXY(1850000, 4423000) ]] ) da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # measurement should be in square feet area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(area, 2000000, delta=0.001) self.assertEqual(units, QgsUnitTypes.AreaSquareFeet) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards) self.assertAlmostEqual(area, 222222.2222, delta=0.001) da.setEllipsoid("WGS84") # now should be in Square Meters again area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(area, 185818.59096575077, delta=1.0) self.assertEqual(units, QgsUnitTypes.AreaSquareMeters) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards) self.assertAlmostEqual(area, 222237.18521272976, delta=1.0)
def testAreaMeasureAndUnits(self): """Test a variety of area measurements in different CRS and ellipsoid modes, to check that the calculated areas and units are always consistent """ da = QgsDistanceArea() da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") polygon = QgsGeometry.fromPolygonXY( [[ QgsPointXY(0, 0), QgsPointXY(1, 0), QgsPointXY(1, 1), QgsPointXY(2, 1), QgsPointXY(2, 2), QgsPointXY(0, 2), QgsPointXY(0, 0), ]] ) # We check both the measured area AND the units, in case the logic regarding # ellipsoids and units changes in future area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) assert ((abs(area - 3.0) < 0.00000001 and units == QgsUnitTypes.AreaSquareDegrees) or (abs(area - 37176087091.5) < 0.1 and units == QgsUnitTypes.AreaSquareMeters)) da.setEllipsoid("WGS84") area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) # should always be in Meters Squared self.assertAlmostEqual(area, 37416879192.9, delta=0.1) self.assertEqual(units, QgsUnitTypes.AreaSquareMeters) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareMiles) self.assertAlmostEqual(area, 14446.7378, delta=0.001) # now try with a source CRS which is in feet polygon = QgsGeometry.fromPolygonXY( [[ QgsPointXY(1850000, 4423000), QgsPointXY(1851000, 4423000), QgsPointXY(1851000, 4424000), QgsPointXY(1852000, 4424000), QgsPointXY(1852000, 4425000), QgsPointXY(1851000, 4425000), QgsPointXY(1850000, 4423000) ]] ) da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # measurement should be in square feet area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(area, 2000000, delta=0.001) self.assertEqual(units, QgsUnitTypes.AreaSquareFeet) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards) self.assertAlmostEqual(area, 222222.2222, delta=0.001) da.setEllipsoid("WGS84") # now should be in Square Meters again area = da.measureArea(polygon) units = da.areaUnits() print(("measured {} in {}".format(area, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(area, 184149.37, delta=1.0) self.assertEqual(units, QgsUnitTypes.AreaSquareMeters) # test converting the resultant area area = da.convertAreaMeasurement(area, QgsUnitTypes.AreaSquareYards) self.assertAlmostEqual(area, 220240.8172549, delta=1.0)
class SizeCalculator(object): """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) self.calculator.setEllipsoid('WGS84') self.calculator.setEllipsoidalMode(True) 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.exportToWkt())) 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.exportToWkt())) 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
class SplitMapTool(QgsMapToolEdit): def __init__(self, canvas, layer, actionMoveVertices, actionAddVertices, actionRemoveVertices, actionMoveSegment, actionLineClose, actionLineOpen, actionMoveLine): super(SplitMapTool, self).__init__(canvas) self.canvas = canvas self.scene = canvas.scene() self.layer = layer self.actionMoveVertices = actionMoveVertices self.actionAddVertices = actionAddVertices self.actionRemoveVertices = actionRemoveVertices self.actionMoveSegment = actionMoveSegment self.actionLineClose = actionLineClose self.actionLineOpen = actionLineOpen self.actionMoveLine = actionMoveLine self.initialize() def initialize(self): try: self.canvas.renderStarting.disconnect(self.mapCanvasChanged) except: pass self.canvas.renderStarting.connect(self.mapCanvasChanged) try: self.layer.editingStopped.disconnect(self.stopCapturing) except: pass self.layer.editingStopped.connect(self.stopCapturing) self.selectedFeatures = self.layer.selectedFeatures() self.rubberBand = None self.tempRubberBand = None self.capturedPoints = [] self.capturing = False self.setCursor(Qt.CrossCursor) self.proj = QgsProject.instance() self.labels = [] self.vertices = [] self.calculator = QgsDistanceArea() self.calculator.setSourceCrs(self.layer.dataProvider().crs(), QgsProject.instance().transformContext()) self.calculator.setEllipsoid( self.layer.dataProvider().crs().ellipsoidAcronym()) self.drawingLine = False self.movingVertices = False self.addingVertices = False self.removingVertices = False self.movingSegment = False self.movingLine = False self.showingVertices = False self.movingVertex = -1 self.movingSegm = -1 self.movingLineInitialPoint = None self.lineClosed = False def restoreAction(self): self.addingVertices = False self.removingVertices = False self.movingVertices = False self.movingSegment = False self.movingLine = False self.showingVertices = False self.drawingLine = True self.movingVertex = -1 self.movingLineInitialPoint = None self.deleteVertices() self.redrawRubberBand() self.redrawTempRubberBand() self.canvas.scene().addItem(self.tempRubberBand) self.redrawActions() def mapCanvasChanged(self): self.redrawAreas() if self.showingVertices: self.redrawVertices() def canvasMoveEvent(self, event): if self.drawingLine and not self.lineClosed: if self.tempRubberBand != None and self.capturing: mapPoint = self.toMapCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPoint) self.redrawAreas(event.pos()) if self.movingVertices and self.movingVertex >= 0: layerPoint = self.toLayerCoordinates(self.layer, event.pos()) self.capturedPoints[self.movingVertex] = layerPoint if self.lineClosed and self.movingVertex == 0: self.capturedPoints[len(self.capturedPoints) - 1] = layerPoint self.redrawRubberBand() self.redrawVertices() self.redrawAreas() if self.movingSegment and self.movingSegm >= 0: currentPoint = self.toLayerCoordinates(self.layer, event.pos()) distance = self.distancePoint(currentPoint, self.movingLineInitialPoint) bearing = self.movingLineInitialPoint.azimuth(currentPoint) self.capturedPoints[self.movingSegm] = self.projectPoint( self.capturedPoints[self.movingSegm], distance, bearing) self.capturedPoints[self.movingSegm + 1] = self.projectPoint( self.capturedPoints[self.movingSegm + 1], distance, bearing) if self.lineClosed: if self.movingSegm == 0: self.capturedPoints[ len(self.capturedPoints) - 1] = self.projectPoint( self.capturedPoints[len(self.capturedPoints) - 1], distance, bearing) elif self.movingSegm == len(self.capturedPoints) - 2: self.capturedPoints[0] = self.projectPoint( self.capturedPoints[0], distance, bearing) self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.movingLineInitialPoint = currentPoint if self.movingLine and self.movingLineInitialPoint != None: currentPoint = self.toLayerCoordinates(self.layer, event.pos()) distance = self.distancePoint(currentPoint, self.movingLineInitialPoint) bearing = self.movingLineInitialPoint.azimuth(currentPoint) for i in range(len(self.capturedPoints)): self.capturedPoints[i] = self.projectPoint( self.capturedPoints[i], distance, bearing) self.redrawRubberBand() self.redrawAreas() self.movingLineInitialPoint = currentPoint def projectPoint(self, point, distance, bearing): rads = bearing * pi / 180.0 dx = distance * sin(rads) dy = distance * cos(rads) return QgsPointXY(point.x() + dx, point.y() + dy) def redrawAreas(self, mousePos=None): self.deleteLabels() if self.capturing and len(self.capturedPoints) > 0: for i in range(len(self.selectedFeatures)): geometry = QgsGeometry(self.selectedFeatures[i].geometry()) movingPoints = list(self.capturedPoints) if mousePos != None: movingPoints.append( self.toLayerCoordinates(self.layer, mousePos)) result, newGeometries, topoTestPoints = geometry.splitGeometry( movingPoints, self.proj.topologicalEditing()) self.addLabel(geometry) if newGeometries != None and len(newGeometries) > 0: for i in range(len(newGeometries)): self.addLabel(newGeometries[i]) def addLabel(self, geometry): area = self.calculator.measureArea(geometry) labelPoint = geometry.pointOnSurface().vertexAt(0) label = QGraphicsTextItem("%.2f" % round(area, 2)) label.setHtml( "<div style=\"color:#ffffff;background:#111111;padding:5px\">" + "%.2f" % round(area, 2) + " " + areaUnits[self.calculator.areaUnits()] + "</div>") point = self.toMapCoordinatesV2(self.layer, labelPoint) label.setPos(self.toCanvasCoordinates(QgsPointXY(point.x(), point.y()))) self.scene.addItem(label) self.labels.append(label) def deleteLabels(self): for i in range(len(self.labels)): self.scene.removeItem(self.labels[i]) self.labels = [] def canvasPressEvent(self, event): if self.movingVertices: for i in range(len(self.capturedPoints)): point = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex = self.toCanvasCoordinates( QgsPointXY(point.x(), point.y())) if self.distancePoint(event.pos(), currentVertex) <= maxDistanceHitTest: self.movingVertex = i break if self.movingSegment: for i in range(len(self.capturedPoints) - 1): vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex1 = self.toCanvasCoordinates( QgsPointXY(vertex1.x(), vertex1.y())) vertex2 = self.toMapCoordinates(self.layer, self.capturedPoints[i + 1]) currentVertex2 = self.toCanvasCoordinates( QgsPointXY(vertex2.x(), vertex2.y())) if self.distancePointLine( event.pos().x(), event.pos().y(), currentVertex1.x(), currentVertex1.y(), currentVertex2.x(), currentVertex2.y()) <= maxDistanceHitTest: self.movingSegm = i break self.movingLineInitialPoint = self.toLayerCoordinates( self.layer, event.pos()) def distancePoint(self, eventPos, vertexPos): return sqrt((eventPos.x() - vertexPos.x())**2 + (eventPos.y() - vertexPos.y())**2) def canvasReleaseEvent(self, event): if self.movingVertices or self.movingSegment or self.movingLine: if event.button() == Qt.RightButton: self.finishOperation() elif self.addingVertices: if event.button() == Qt.LeftButton: self.addVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() elif self.removingVertices: if event.button() == Qt.LeftButton: self.removeVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() else: if event.button() == Qt.LeftButton: if not self.lineClosed: if not self.capturing: self.startCapturing() self.addEndingVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() self.movingVertex = -1 self.movingSegm = -1 self.movingLineInitialPoint = None self.redrawActions() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.stopCapturing() if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: self.finishOperation() event.accept() self.redrawActions() def finishOperation(self): self.doSplit() self.stopCapturing() self.initialize() self.startCapturing() def doSplit(self): if self.capturedPoints != None: self.layer.splitFeatures(self.capturedPoints, self.proj.topologicalEditing()) def startCapturing(self): self.prepareRubberBand() self.prepareTempRubberBand() self.drawingLine = True self.capturing = True self.redrawActions() def prepareRubberBand(self): color = QColor("red") color.setAlphaF(0.78) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBand.setWidth(1) self.rubberBand.setColor(color) self.rubberBand.show() def prepareTempRubberBand(self): color = QColor("red") color.setAlphaF(0.78) self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRubberBand.setWidth(1) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() def redrawRubberBand(self): self.canvas.scene().removeItem(self.rubberBand) self.prepareRubberBand() for i in range(len(self.capturedPoints)): point = self.capturedPoints[i] if point.__class__ == QgsPoint: vertexCoord = self.toMapCoordinatesV2(self.layer, self.capturedPoints[i]) vertexCoord = QgsPointXY(vertexCoord.x(), vertexCoord.y()) else: vertexCoord = self.toMapCoordinates(self.layer, self.capturedPoints[i]) self.rubberBand.addPoint(vertexCoord) def redrawTempRubberBand(self): if self.tempRubberBand != None: self.tempRubberBand.reset(QgsWkbTypes.LineGeometry) self.tempRubberBand.addPoint( self.toMapCoordinates( self.layer, self.capturedPoints[len(self.capturedPoints) - 1])) def stopCapturing(self): self.deleteLabels() self.deleteVertices() if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.drawingLine = False self.movingVertices = False self.showingVertices = False self.capturing = False self.capturedPoints = [] self.canvas.refresh() self.redrawActions() def addEndingVertex(self, canvasPoint): mapPoint = self.toMapCoordinates(canvasPoint) layerPoint = self.toLayerCoordinates(self.layer, canvasPoint) self.rubberBand.addPoint(mapPoint) self.capturedPoints.append(layerPoint) self.tempRubberBand.reset(QgsWkbTypes.LineGeometry) self.tempRubberBand.addPoint(mapPoint) def removeLastVertex(self): if not self.capturing: return rubberBandSize = self.rubberBand.numberOfVertices() tempRubberBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if rubberBandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if rubberBandSize > 1: if tempRubberBandSize > 1: point = self.rubberBand.getPoint(0, rubberBandSize - 2) self.tempRubberBand.movePoint(tempRubberBandSize - 2, point) else: self.tempRubberBand.reset(self.bandType()) del self.capturedPoints[-1] def addVertex(self, pos): newCapturedPoints = [] for i in range(len(self.capturedPoints) - 1): newCapturedPoints.append(self.capturedPoints[i]) vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex1 = self.toCanvasCoordinates( QgsPointXY(vertex1.x(), vertex1.y())) vertex2 = self.toMapCoordinates(self.layer, self.capturedPoints[i + 1]) currentVertex2 = self.toCanvasCoordinates( QgsPointXY(vertex2.x(), vertex2.y())) distance = self.distancePointLine(pos.x(), pos.y(), currentVertex1.x(), currentVertex1.y(), currentVertex2.x(), currentVertex2.y()) if distance <= maxDistanceHitTest: layerPoint = self.toLayerCoordinates(self.layer, pos) newCapturedPoints.append(layerPoint) newCapturedPoints.append(self.capturedPoints[len(self.capturedPoints) - 1]) self.capturedPoints = newCapturedPoints self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.redrawActions() def removeVertex(self, pos): deletedFirst = False deletedLast = False newCapturedPoints = [] for i in range(len(self.capturedPoints)): vertex = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex = self.toCanvasCoordinates( QgsPointXY(vertex.x(), vertex.y())) if not self.distancePoint(pos, currentVertex) <= maxDistanceHitTest: newCapturedPoints.append(self.capturedPoints[i]) elif i == 0: deletedFirst = True elif i == len(self.capturedPoints) - 1: deletedLast = True self.capturedPoints = newCapturedPoints if deletedFirst and deletedLast: self.lineClosed = False self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.redrawActions() if len(self.capturedPoints) <= 2: self.stopRemovingVertices() def startMovingVertices(self): self.stopMovingLine() self.stopAddingVertices() self.stopRemovingVertices() self.stopMovingSegment() self.actionMoveVertices.setChecked(True) self.movingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopMovingVertices(self): self.movingVertices = False self.actionMoveVertices.setChecked(False) self.restoreAction() def startAddingVertices(self): self.stopMovingVertices() self.stopRemovingVertices() self.stopMovingLine() self.stopMovingSegment() self.actionAddVertices.setChecked(True) self.addingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopAddingVertices(self): self.addVertices = False self.actionAddVertices.setChecked(False) self.restoreAction() def startRemovingVertices(self): self.stopMovingVertices() self.stopAddingVertices() self.stopMovingLine() self.stopMovingSegment() self.actionRemoveVertices.setChecked(True) self.removingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopRemovingVertices(self): self.removingVertices = False self.actionRemoveVertices.setChecked(False) self.restoreAction() def startMovingSegment(self): self.stopMovingVertices() self.stopMovingLine() self.stopAddingVertices() self.stopRemovingVertices() self.actionMoveSegment.setChecked(True) self.movingSegment = True self.showingVertices = False self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopMovingSegment(self): self.movingSegment = False self.actionMoveSegment.setChecked(False) self.restoreAction() def startMovingLine(self): self.stopMovingVertices() self.stopAddingVertices() self.stopRemovingVertices() self.stopMovingSegment() self.actionMoveLine.setChecked(True) self.movingLine = True self.showingVertices = False self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawAreas() self.redrawActions() def stopMovingLine(self): self.actionMoveLine.setChecked(False) self.restoreAction() def lineClose(self): self.lineClosed = True self.capturedPoints.append(self.capturedPoints[0]) self.redrawRubberBand() self.redrawTempRubberBand() self.redrawAreas() self.redrawActions() def lineOpen(self): self.lineClosed = False del self.capturedPoints[-1] self.redrawRubberBand() self.redrawTempRubberBand() self.redrawAreas() self.redrawActions() def showVertices(self): for i in range(len(self.capturedPoints)): vertexc = self.toMapCoordinates(self.layer, self.capturedPoints[i]) vertexCoords = self.toCanvasCoordinates( QgsPointXY(vertexc.x(), vertexc.y())) if i == self.movingVertex: vertex = self.scene.addRect(vertexCoords.x() - 5, vertexCoords.y() - 5, 10, 10, QPen(QColor("green")), QBrush(QColor("green"))) self.vertices.append(vertex) elif i == len(self.capturedPoints ) - 1 and self.movingVertex == 0 and self.lineClosed: vertex = self.scene.addRect(vertexCoords.x() - 5, vertexCoords.y() - 5, 10, 10, QPen(QColor("green")), QBrush(QColor("green"))) self.vertices.append(vertex) else: vertex = self.scene.addRect(vertexCoords.x() - 4, vertexCoords.y() - 4, 8, 8, QPen(QColor("red")), QBrush(QColor("red"))) self.vertices.append(vertex) def deleteVertices(self): for i in range(len(self.vertices)): self.scene.removeItem(self.vertices[i]) self.vertices = [] def lineMagnitude(self, x1, y1, x2, y2): return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)) def distancePointLine(self, px, py, x1, y1, x2, y2): magnitude = self.lineMagnitude(x1, y1, x2, y2) if magnitude < 0.00000001: distance = 9999 return distance u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) u = u1 / (magnitude * magnitude) if (u < 0.00001) or (u > 1): ix = self.lineMagnitude(px, py, x1, y1) iy = self.lineMagnitude(px, py, x2, y2) if ix > iy: distance = iy else: distance = ix else: ix = x1 + u * (x2 - x1) iy = y1 + u * (y2 - y1) distance = self.lineMagnitude(px, py, ix, iy) return distance def redrawVertices(self): self.deleteVertices() self.showVertices() def redrawActions(self): self.redrawActionMoveVertices() self.redrawActionAddVertices() self.redrawActionRemoveVertices() self.redrawActionMoveSegment() self.redrawActionLineClose() self.redrawActionLineOpen() self.redrawActionMoveLine() def redrawActionMoveVertices(self): self.actionMoveVertices.setEnabled(False) if len(self.capturedPoints) > 0: self.actionMoveVertices.setEnabled(True) def redrawActionAddVertices(self): self.actionAddVertices.setEnabled(False) if len(self.capturedPoints) >= 2: self.actionAddVertices.setEnabled(True) def redrawActionRemoveVertices(self): self.actionRemoveVertices.setEnabled(False) if len(self.capturedPoints) > 2: self.actionRemoveVertices.setEnabled(True) def redrawActionMoveSegment(self): self.actionMoveSegment.setEnabled(False) if len(self.capturedPoints) > 2: self.actionMoveSegment.setEnabled(True) def redrawActionLineClose(self): self.actionLineClose.setEnabled(False) if not self.lineClosed and len(self.capturedPoints) >= 3: self.actionLineClose.setEnabled(True) def redrawActionLineOpen(self): self.actionLineOpen.setEnabled(False) if self.lineClosed: self.actionLineOpen.setEnabled(True) def redrawActionMoveLine(self): self.actionMoveLine.setEnabled(False) if len(self.capturedPoints) > 0: self.actionMoveLine.setEnabled(True)