def testEquality(self): segment1 = QgsLineSegment2D(QgsPointXY(1, 2), QgsPointXY(3, 4)) segment2 = QgsLineSegment2D(QgsPointXY(1, 2), QgsPointXY(3, 4)) self.assertEqual(segment1, segment2) self.assertFalse(segment1 != segment2) segment2 = QgsLineSegment2D(QgsPointXY(1, 2), QgsPointXY(3, 5)) self.assertNotEqual(segment1, segment2) self.assertTrue(segment1 != segment2) segment2 = QgsLineSegment2D(QgsPointXY(1, 2), QgsPointXY(5, 4)) self.assertNotEqual(segment1, segment2) self.assertTrue(segment1 != segment2) segment2 = QgsLineSegment2D(QgsPointXY(1, 5), QgsPointXY(3, 4)) self.assertNotEqual(segment1, segment2) self.assertTrue(segment1 != segment2) segment2 = QgsLineSegment2D(QgsPointXY(5, 2), QgsPointXY(3, 4)) self.assertNotEqual(segment1, segment2) self.assertTrue(segment1 != segment2)
def testChangeGeometry(self): # test changing geometries values from an edit buffer # make a layer with two features layer = createEmptyLayer() self.assertTrue(layer.startEditing()) # add two features f1 = QgsFeature(layer.fields(), 1) f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2))) f1.setAttributes(["test", 123]) self.assertTrue(layer.addFeature(f1)) f2 = QgsFeature(layer.fields(), 2) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 4))) f2.setAttributes(["test2", 246]) self.assertTrue(layer.addFeature(f2)) layer.commitChanges() layer.startEditing() self.assertEqual(layer.editBuffer().changedGeometries(), {}) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) # change geometry layer.changeGeometry(1, QgsGeometry.fromPointXY(QgsPointXY(10, 20))) # test contents of buffer self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 10) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.undoStack().count(), 1) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 10) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) # apply second change to same feature layer.beginEditCommand( 'second change' ) # need to use an edit command to avoid the two geometry changes being merged layer.changeGeometry(1, QgsGeometry.fromPointXY(QgsPointXY(100, 200))) layer.endEditCommand() # test contents of buffer self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 100) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.undoStack().count(), 2) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 100) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) layer.changeGeometry(2, QgsGeometry.fromPointXY(QgsPointXY(20, 40))) # test contents of buffer self.assertEqual(set(layer.editBuffer().changedGeometries().keys()), set([1, 2])) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 100) self.assertEqual( layer.editBuffer().changedGeometries()[2].constGet().x(), 20) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.undoStack().count(), 3) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 100) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 20) layer.undoStack().undo() self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 100) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 100) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) layer.undoStack().undo() self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 10) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) layer.undoStack().undo() self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), []) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 1) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) expression = QgsExpression( self.parameterAsString(parameters, self.EXPRESSION, context)) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expressionContext = self.createExpressionContext( parameters, context, source) expression.prepare(expressionContext) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) total = 100.0 / source.featureCount() if source.featureCount() else 0 current_progress = 0 for current, f in enumerate(source.getFeatures()): if feedback.isCanceled(): break if not f.hasGeometry(): continue current_progress = total * current feedback.setProgress(current_progress) expressionContext.setFeature(f) value = expression.evaluate(expressionContext) if expression.hasEvalError(): feedback.pushInfo( self.tr('Evaluation error for feature ID {}: {}').format( f.id(), expression.evalErrorString())) continue fGeom = f.geometry() engine = QgsGeometry.createGeometryEngine(fGeom.constGet()) engine.prepareGeometry() bbox = fGeom.boundingBox() if strategy == 0: pointCount = int(value) else: pointCount = int(round(value * da.measureArea(fGeom))) if pointCount == 0: feedback.pushInfo( "Skip feature {} as number of points for it is 0.".format( f.id())) continue index = QgsSpatialIndex() points = dict() nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 feature_total = total / pointCount if pointCount else 1 random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) if engine.contains(geom.constGet()) and \ vector.checkMinDistance(p, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.addFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(current_progress + int(nPoints * feature_total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr('Could not generate requested number of random ' 'points. Maximum number of attempts exceeded.')) feedback.setProgress(100) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) endPoints = self.parameterAsSource(parameters, self.END_POINTS, context) if endPoints is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.END_POINTS)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = endPoints.fields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo( QCoreApplication.translate('ShortestPathPointToLayer', 'Loading end points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = endPoints.getFeatures(request) total = 100.0 / endPoints.featureCount() if endPoints.featureCount( ) else 0 points = [startPoint] source_attributes = {} i = 1 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo( QCoreApplication.translate('ShortestPathPointToLayer', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo( QCoreApplication.translate('ShortestPathPointToLayer', 'Calculating shortest paths…')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, nPoints): if feedback.isCanceled(): break idxEnd = graph.findVertex(snappedPoints[i]) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(startPoint.toString(), points[i].toString())) feedback.reportError(msg) # add feature with no geometry feat.clearGeometry() attrs = source_attributes[i] attrs.extend([NULL, points[i].toString()]) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) continue route = [graph.vertex(idxEnd).point()] cost = costs[idxEnd] current = idxEnd while current != idxStart: current = graph.edge(tree[current]).fromVertex() route.append(graph.vertex(current).point()) route.reverse() geom = QgsGeometry.fromPolylineXY(route) attrs = source_attributes[i] attrs.extend([ startPoint.toString(), points[i].toString(), cost / multiplier ]) feat.setAttributes(attrs) feat.setGeometry(geom) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) group_field_name = self.parameterAsString(parameters, self.GROUP_FIELD, context) order_field_name = self.parameterAsString(parameters, self.ORDER_FIELD, context) date_format = self.parameterAsString(parameters, self.DATE_FORMAT, context) text_dir = self.parameterAsString(parameters, self.OUTPUT_TEXT_DIR, context) group_field_index = source.fields().lookupField(group_field_name) order_field_index = source.fields().lookupField(order_field_name) if group_field_index >= 0: group_field_def = source.fields().at(group_field_index) else: group_field_def = None order_field_def = source.fields().at(order_field_index) fields = QgsFields() if group_field_def is not None: fields.append(group_field_def) begin_field = QgsField(order_field_def) begin_field.setName('begin') fields.append(begin_field) end_field = QgsField(order_field_def) end_field.setName('end') fields.append(end_field) output_wkb = QgsWkbTypes.LineString if QgsWkbTypes.hasM(source.wkbType()): output_wkb = QgsWkbTypes.addM(output_wkb) if QgsWkbTypes.hasZ(source.wkbType()): output_wkb = QgsWkbTypes.addZ(output_wkb) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, output_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) points = dict() features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([group_field_index, order_field_index]), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().constGet().clone() if group_field_index >= 0: group = f.attributes()[group_field_index] else: group = 1 order = f.attributes()[order_field_index] if date_format != '': order = datetime.strptime(str(order), date_format) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) current = 0 total = 100.0 / len(points) if points else 1 for group, vertices in list(points.items()): if feedback.isCanceled(): break vertices.sort(key=lambda x: (x[0] is None, x[0])) f = QgsFeature() attributes = [] if group_field_index >= 0: attributes.append(group) attributes.extend([vertices[0][0], vertices[-1][0]]) f.setAttributes(attributes) line = [node[1] for node in vertices] if text_dir: fileName = os.path.join(text_dir, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') for i in range(len(line)): if i == 0: fl.write('startAt=%f;%f;90\n' % (line[i].x(), line[i].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(QgsPointXY(line[i - 1]), QgsPointXY(line[i])) fl.write('%f;%f;90\n' % (angle, distance)) f.setGeometry(QgsGeometry(QgsLineString(line))) sink.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource( parameters, self.INPUT, context ) sampled_raster = self.parameterAsRasterLayer( parameters, self.RASTERCOPY, context ) columnPrefix = self.parameterAsString( parameters, self.COLUMN_PREFIX, context ) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) source_fields = source.fields() raster_fields = QgsFields() # append field to vector as columnPrefix_bandCount for b in range(sampled_raster.bandCount()): raster_fields.append(QgsField( columnPrefix + str('_{}'.format(b + 1)), QVariant.Double ) ) # combine all the vector fields out_fields = QgsProcessingUtils.combineFields(source_fields, raster_fields) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, out_fields, source.wkbType(), source.sourceCrs() ) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures() # create the coordinates transformation context ct = QgsCoordinateTransform(source.sourceCrs(), sampled_raster.crs(), context.transformContext()) for n, i in enumerate(source.getFeatures()): attrs = i.attributes() if i.geometry().isMultipart() and i.geometry().constGet().partCount() > 1: sink.addFeature(i, QgsFeatureSink.FastInsert) feedback.setProgress(int(n * total)) feedback.reportError(self.tr('Impossible to sample data of multipart feature {}.').format(i.id())) continue # get the feature geometry as point point = QgsPointXY() if i.geometry().isMultipart(): point = i.geometry().asMultiPoint()[0] else: point = i.geometry().asPoint() # reproject to raster crs try: point = ct.transform(point) except QgsCsException: for b in range(sampled_raster.bandCount()): attrs.append(None) i.setAttributes(attrs) sink.addFeature(i, QgsFeatureSink.FastInsert) feedback.setProgress(int(n * total)) feedback.reportError(self.tr('Could not reproject feature {} to raster CRS').format(i.id())) continue for b in range(sampled_raster.bandCount()): value, ok = sampled_raster.dataProvider().sample(point, b + 1) if ok: attrs.append(value) else: attrs.append(NULL) i.setAttributes(attrs) sink.addFeature(i, QgsFeatureSink.FastInsert) feedback.setProgress(int(n * total)) return {self.OUTPUT: dest_id}
def ovals(self, sink, source, width, height, rotation, segments, feedback): features = source.getFeatures() ft = QgsFeature() total = 100.0 / source.featureCount() if source.featureCount() else 0 if rotation >= 0: for current, feat in enumerate(features): if feedback.isCanceled(): break if not feat.hasGeometry(): continue w = feat[width] h = feat[height] angle = feat[rotation] if not w or not h or not angle: feedback.pushInfo( self.tr('Feature {} has empty ' 'width, height or angle. ' 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 yOffset = h / 2.0 phi = angle * math.pi / 180 point = feat.geometry().asPoint() x = point.x() y = point.y() points = [] for t in [(2 * math.pi) / segments * i for i in range(segments)]: points.append( (xOffset * math.cos(t), yOffset * math.sin(t))) polygon = [[ QgsPointXY( i[0] * math.cos(phi) + i[1] * math.sin(phi) + x, -i[0] * math.sin(phi) + i[1] * math.cos(phi) + y) for i in points ]] ft.setGeometry(QgsGeometry.fromPolygonXY(polygon)) ft.setAttributes(feat.attributes()) sink.addFeature(ft, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): if feedback.isCanceled(): break if not feat.hasGeometry(): continue w = feat[width] h = feat[height] if not w or not h: feedback.pushInfo( self.tr('Feature {} has empty ' 'width or height. ' 'Skipping...'.format(feat.id()))) continue xOffset = w / 2.0 yOffset = h / 2.0 point = feat.geometry().asPoint() x = point.x() y = point.y() points = [] for t in [(2 * math.pi) / segments * i for i in range(segments)]: points.append( (xOffset * math.cos(t), yOffset * math.sin(t))) polygon = [[QgsPointXY(i[0] + x, i[1] + y) for i in points]] ft.setGeometry(QgsGeometry.fromPolygonXY(polygon)) ft.setAttributes(feat.attributes()) sink.addFeature(ft, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total))
def UpdateBeamsData(packet, cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL): ''' Update Beams Values ''' lat = packet.GetSensorLatitude() lon = packet.GetSensorLongitude() alt = packet.GetSensorTrueAltitude() beamsLyr = qgsu.selectLayerByName(Beams_lyr) try: if beamsLyr is not None: beamsLyr.startEditing() if beamsLyr.featureCount() == 0: # UL featureUL = QgsFeature() featureUL.setAttributes( [lon, lat, alt, cornerPointUL[1], cornerPointUL[0]]) surface = QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointUL[1], cornerPointUL[0]) ]) featureUL.setGeometry(surface) beamsLyr.addFeatures([featureUL]) # UR featureUR = QgsFeature() featureUR.setAttributes( [lon, lat, alt, cornerPointUR[1], cornerPointUR[0]]) surface = QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointUR[1], cornerPointUR[0]) ]) featureUR.setGeometry(surface) beamsLyr.addFeatures([featureUR]) # LR featureLR = QgsFeature() featureLR.setAttributes( [lon, lat, alt, cornerPointLR[1], cornerPointLR[0]]) surface = QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointLR[1], cornerPointLR[0]) ]) featureLR.setGeometry(surface) beamsLyr.addFeatures([featureLR]) # LL featureLL = QgsFeature() featureLL.setAttributes( [lon, lat, alt, cornerPointLL[1], cornerPointLL[0]]) surface = QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointLL[1], cornerPointLL[0]) ]) featureLL.setGeometry(surface) beamsLyr.addFeatures([featureLL]) else: beamsLyr.beginEditCommand("ChangeGeometry + ChangeAttribute") # UL fetId = 1 attrib = { 0: lon, 1: lat, 2: alt, 3: cornerPointUL[1], 4: cornerPointUL[0] } beamsLyr.dataProvider().changeAttributeValues({fetId: attrib}) beamsLyr.dataProvider().changeGeometryValues({ fetId: QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointUL[1], cornerPointUL[0]) ]) }) # UR fetId = 2 attrib = { 0: lon, 1: lat, 2: alt, 3: cornerPointUR[1], 4: cornerPointUR[0] } beamsLyr.dataProvider().changeAttributeValues({fetId: attrib}) beamsLyr.dataProvider().changeGeometryValues({ fetId: QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointUR[1], cornerPointUR[0]) ]) }) # LR fetId = 3 attrib = { 0: lon, 1: lat, 2: alt, 3: cornerPointLR[1], 4: cornerPointLR[0] } beamsLyr.dataProvider().changeAttributeValues({fetId: attrib}) beamsLyr.dataProvider().changeGeometryValues({ fetId: QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointLR[1], cornerPointLR[0]) ]) }) # LL fetId = 4 attrib = { 0: lon, 1: lat, 2: alt, 3: cornerPointLL[1], 4: cornerPointLL[0] } beamsLyr.dataProvider().changeAttributeValues({fetId: attrib}) beamsLyr.dataProvider().changeGeometryValues({ fetId: QgsGeometry.fromPolylineXY([ QgsPointXY(lon, lat), QgsPointXY(cornerPointLL[1], cornerPointLL[0]) ]) }) beamsLyr.endEditCommand() CommonLayer(beamsLyr) except Exception as e: qgsu.showUserAndLogMessage( QCoreApplication.translate("QgsFmvUtils", "Failed Update Beams Layer! : "), str(e)) return
def UpdateFootPrintData(cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL): ''' Update Footprint Values ''' footprintLyr = qgsu.selectLayerByName(Footprint_lyr) try: if footprintLyr is not None: footprintLyr.startEditing() if footprintLyr.featureCount() == 0: feature = QgsFeature() feature.setAttributes([ cornerPointUL[1], cornerPointUL[0], cornerPointUR[1], cornerPointUR[0], cornerPointLR[1], cornerPointLR[0], cornerPointLL[1], cornerPointLL[0] ]) surface = QgsGeometry.fromPolygonXY([[ QgsPointXY(cornerPointUL[1], cornerPointUL[0]), QgsPointXY(cornerPointUR[1], cornerPointUR[0]), QgsPointXY(cornerPointLR[1], cornerPointLR[0]), QgsPointXY(cornerPointLL[1], cornerPointLL[0]), QgsPointXY(cornerPointUL[1], cornerPointUL[0]) ]]) feature.setGeometry(surface) footprintLyr.addFeatures([feature]) else: footprintLyr.beginEditCommand( "ChangeGeometry + ChangeAttribute") fetId = 1 attrib = { 0: cornerPointUL[1], 1: cornerPointUL[0], 2: cornerPointUR[1], 3: cornerPointUR[0], 4: cornerPointLR[1], 5: cornerPointLR[0], 6: cornerPointLL[1], 7: cornerPointLL[0] } footprintLyr.dataProvider().changeAttributeValues( {fetId: attrib}) footprintLyr.dataProvider().changeGeometryValues({ fetId: QgsGeometry.fromPolygonXY([[ QgsPointXY(cornerPointUL[1], cornerPointUL[0]), QgsPointXY(cornerPointUR[1], cornerPointUR[0]), QgsPointXY(cornerPointLR[1], cornerPointLR[0]), QgsPointXY(cornerPointLL[1], cornerPointLL[0]), QgsPointXY(cornerPointUL[1], cornerPointUL[0]) ]]) }) footprintLyr.endEditCommand() CommonLayer(footprintLyr) except Exception as e: qgsu.showUserAndLogMessage( QCoreApplication.translate("QgsFmvUtils", "Failed Update FootPrint Layer! : "), str(e)) return
def clip_voronoi(self, edges, c, width, height, extent, exX, exY): """Clip voronoi function based on code written for Inkscape. Copyright (C) 2010 Alvin Penner, [email protected] """ def clip_line(x1, y1, x2, y2, w, h, x, y): if x1 < 0 - x and x2 < 0 - x: return [0, 0, 0, 0] if x1 > w + x and x2 > w + x: return [0, 0, 0, 0] if x1 < 0 - x: y1 = (y1 * x2 - y2 * x1) / (x2 - x1) x1 = 0 - x if x2 < 0 - x: y2 = (y1 * x2 - y2 * x1) / (x2 - x1) x2 = 0 - x if x1 > w + x: y1 = y1 + (w + x - x1) * (y2 - y1) / (x2 - x1) x1 = w + x if x2 > w + x: y2 = y1 + (w + x - x1) * (y2 - y1) / (x2 - x1) x2 = w + x if y1 < 0 - y and y2 < 0 - y: return [0, 0, 0, 0] if y1 > h + y and y2 > h + y: return [0, 0, 0, 0] if x1 == x2 and y1 == y2: return [0, 0, 0, 0] if y1 < 0 - y: x1 = (x1 * y2 - x2 * y1) / (y2 - y1) y1 = 0 - y if y2 < 0 - y: x2 = (x1 * y2 - x2 * y1) / (y2 - y1) y2 = 0 - y if y1 > h + y: x1 = x1 + (h + y - y1) * (x2 - x1) / (y2 - y1) y1 = h + y if y2 > h + y: x2 = x1 + (h + y - y1) * (x2 - x1) / (y2 - y1) y2 = h + y return [x1, y1, x2, y2] lines = [] hasXMin = False hasYMin = False hasXMax = False hasYMax = False for edge in edges: if edge[1] >= 0 and edge[2] >= 0: # Two vertices [x1, y1, x2, y2] = clip_line( c.vertices[edge[1]][0], c.vertices[edge[1]][1], c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, exX, exY, ) elif edge[1] >= 0: # Only one vertex if c.lines[edge[0]][1] == 0: # Vertical line xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] if c.vertices[edge[1]][1] > (height + exY) / 2: ytemp = height + exY else: ytemp = 0 - exX else: xtemp = width + exX ytemp = (c.lines[edge[0]][2] - (width + exX) * c.lines[edge[0]][0]) / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line( c.vertices[edge[1]][0], c.vertices[edge[1]][1], xtemp, ytemp, width, height, exX, exY, ) elif edge[2] >= 0: # Only one vertex if c.lines[edge[0]][1] == 0: # Vertical line xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] if c.vertices[edge[2]][1] > (height + exY) / 2: ytemp = height + exY else: ytemp = 0.0 - exY else: xtemp = 0.0 - exX ytemp = c.lines[edge[0]][2] / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line( xtemp, ytemp, c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, exX, exY, ) if x1 or x2 or y1 or y2: lines.append( QgsPointXY(x1 + extent.xMinimum(), y1 + extent.yMinimum())) lines.append( QgsPointXY(x2 + extent.xMinimum(), y2 + extent.yMinimum())) if 0 - exX in (x1, x2): hasXMin = True if 0 - exY in (y1, y2): hasYMin = True if height + exY in (y1, y2): hasYMax = True if width + exX in (x1, x2): hasXMax = True if hasXMin: if hasYMax: lines.append( QgsPointXY(extent.xMinimum() - exX, height + extent.yMinimum() + exY)) if hasYMin: lines.append( QgsPointXY(extent.xMinimum() - exX, extent.yMinimum() - exY)) if hasXMax: if hasYMax: lines.append( QgsPointXY(width + extent.xMinimum() + exX, height + extent.yMinimum() + exY)) if hasYMin: lines.append( QgsPointXY(width + extent.xMinimum() + exX, extent.yMinimum() - exY)) return lines
def diamonds(self, sink, source, width, height, rotation, feedback): features = source.getFeatures() ft = QgsFeature() total = 100.0 / source.featureCount() if source.featureCount() else 0 if rotation >= 0: for current, feat in enumerate(features): if feedback.isCanceled(): break if not feat.hasGeometry(): continue w = feat[width] h = feat[height] angle = feat[rotation] # block 0/NULL width or height, but allow 0 as angle value if not w or not h: feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty ' 'width or height. ' 'Skipping…').format(feat.id())) continue if angle is NULL: feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty ' 'angle. ' 'Skipping…').format(feat.id())) continue xOffset = w / 2.0 yOffset = h / 2.0 phi = angle * math.pi / 180 point = feat.geometry().asPoint() x = point.x() y = point.y() points = [(0.0, -yOffset), (-xOffset, 0.0), (0.0, yOffset), (xOffset, 0.0)] polygon = [[QgsPointXY(i[0] * math.cos(phi) + i[1] * math.sin(phi) + x, -i[0] * math.sin(phi) + i[1] * math.cos(phi) + y) for i in points]] ft.setGeometry(QgsGeometry.fromPolygonXY(polygon)) ft.setAttributes(feat.attributes()) sink.addFeature(ft, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): if feedback.isCanceled(): break if not feat.hasGeometry(): continue w = feat[width] h = feat[height] if not w or not h: feedback.pushInfo(QCoreApplication.translate('RectanglesOvalsDiamondsVariable', 'Feature {} has empty ' 'width or height. ' 'Skipping…').format(feat.id())) continue xOffset = w / 2.0 yOffset = h / 2.0 point = feat.geometry().asPoint() x = point.x() y = point.y() points = [(0.0, -yOffset), (-xOffset, 0.0), (0.0, yOffset), (xOffset, 0.0)] polygon = [[QgsPointXY(i[0] + x, i[1] + y) for i in points]] ft.setGeometry(QgsGeometry.fromPolygonXY(polygon)) ft.setAttributes(feat.attributes()) sink.addFeature(ft, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) proximity = self.parameterAsDouble(parameters, self.PROXIMITY, context) radius = self.parameterAsDouble(parameters, self.DISTANCE, context) horizontal = self.parameterAsBool(parameters, self.HORIZONTAL, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 def searchRect(p): return QgsRectangle(p.x() - proximity, p.y() - proximity, p.x() + proximity, p.y() + proximity) index = QgsSpatialIndex() # NOTE: this is a Python port of QgsPointDistanceRenderer::renderFeature. If refining this algorithm, # please port the changes to QgsPointDistanceRenderer::renderFeature also! clustered_groups = [] group_index = {} group_locations = {} for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().asPoint() other_features_within_radius = index.intersects(searchRect(point)) if not other_features_within_radius: index.insertFeature(f) group = [f] clustered_groups.append(group) group_index[f.id()] = len(clustered_groups) - 1 group_locations[f.id()] = point else: # find group with closest location to this point (may be more than one within search tolerance) min_dist_feature_id = other_features_within_radius[0] min_dist = group_locations[min_dist_feature_id].distance(point) for i in range(1, len(other_features_within_radius)): candidate_id = other_features_within_radius[i] new_dist = group_locations[candidate_id].distance(point) if new_dist < min_dist: min_dist = new_dist min_dist_feature_id = candidate_id group_index_pos = group_index[min_dist_feature_id] group = clustered_groups[group_index_pos] # calculate new centroid of group old_center = group_locations[min_dist_feature_id] group_locations[min_dist_feature_id] = QgsPointXY( (old_center.x() * len(group) + point.x()) / (len(group) + 1.0), (old_center.y() * len(group) + point.y()) / (len(group) + 1.0)) # add to a group clustered_groups[group_index_pos].append(f) group_index[f.id()] = group_index_pos feedback.setProgress(int(current * total)) current = 0 total = 100.0 / len(clustered_groups) if clustered_groups else 1 feedback.setProgress(0) fullPerimeter = 2 * math.pi for group in clustered_groups: if feedback.isCanceled(): break count = len(group) if count == 1: sink.addFeature(group[0], QgsFeatureSink.FastInsert) else: angleStep = fullPerimeter / count if count == 2 and horizontal: currentAngle = math.pi / 2 else: currentAngle = 0 old_point = group_locations[group[0].id()] for f in group: if feedback.isCanceled(): break sinusCurrentAngle = math.sin(currentAngle) cosinusCurrentAngle = math.cos(currentAngle) dx = radius * sinusCurrentAngle dy = radius * cosinusCurrentAngle # we want to keep any existing m/z values point = f.geometry().constGet().clone() point.setX(old_point.x() + dx) point.setY(old_point.y() + dy) f.setGeometry(QgsGeometry(point)) sink.addFeature(f, QgsFeatureSink.FastInsert) currentAngle += angleStep current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
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) # allow only regular point layers. no Multipoints if (source.wkbType() == 4 or source.wkbType() == 1004 or source.wkbType() == 3004): raise QgsProcessingException("MultiPoint layer is not supported!") # radius = self.parameterAsString( # parameters, # self.RADIUS, # context # ) # mode = self.parameterAsEnum( # parameters, # self.MODES, # context # ) categories = self.parameterAsEnums(parameters, self.KEYS, context) # feedback.pushInfo(addressField) # 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)) fields = QgsFields() fields.append(QgsField("id", QVariant.String)) fields.append(QgsField("origin_id", QVariant.Int)) fields.append(QgsField("title", QVariant.String)) fields.append(QgsField("label", QVariant.String)) fields.append(QgsField("distance", QVariant.Double)) fields.append(QgsField("categories", QVariant.String)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(4326)) # Send some information to the user feedback.pushInfo('{} points for POI finding'.format( source.featureCount())) # 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() # get the keys: creds = self.loadCredFunctionAlg() # convert categories to list for API call: categoriesList = [] #self.keys[keyField].split(" | ")[1] for category in categories: categoriesList.append(self.keys[category]) categories = ",".join(categoriesList) layerCRS = source.sourceCrs() if layerCRS != QgsCoordinateReferenceSystem(4326): sourceCrs = source.sourceCrs() destCrs = QgsCoordinateReferenceSystem(4326) tr = QgsCoordinateTransform(sourceCrs, destCrs, QgsProject.instance()) for current, feature in enumerate(features): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break # convert coordinates: if layerCRS != QgsCoordinateReferenceSystem(4326): # we reproject: geom = feature.geometry() newGeom = tr.transform(geom.asPoint()) x = newGeom.x() y = newGeom.y() else: x = feature.geometry().asPoint().x() y = feature.geometry().asPoint().y() coordinates = str(y) + "," + str(x) # get the location from the API: header = {"referer": "HQGIS"} ApiUrl = 'https://browse.search.hereapi.com/v1/browse?at=' + coordinates + \ "&categories=" + categories + "&limit=100&apiKey=" + creds["id"] feedback.pushInfo('calling Url {}'.format(ApiUrl)) r = requests.get(ApiUrl, headers=header) responsePlaces = json.loads(r.text)["items"] for place in responsePlaces: lat = place["position"]["lat"] lng = place["position"]["lng"] # iterate over categories: categoriesResp = [] for cat in place["categories"]: categoriesResp.append(cat["id"]) fet = QgsFeature() fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(lng, lat))) fet.setAttributes([ place["id"], feature.id(), place["title"], place["address"]["label"], place["distance"], ";".join(categoriesResp) ]) sink.addFeature(fet, QgsFeatureSink.FastInsert) #lat = responseAddress["Location"]["DisplayPosition"]["Latitude"] #lng = responseAddress["Location"]["DisplayPosition"]["Longitude"] # Add a feature in the sink # feedback.pushInfo(str(lat)) # fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(lng,lat))) # fet.setAttributes([ # Update the progress bar feedback.setProgress(int(current * total)) # To run another Processing algorithm as part of this algorithm, you can use # processing.run(...). Make sure you pass the current context and feedback # to processing.run to ensure that all temporary layer outputs are available # to the executed algorithm, and that the executed algorithm can send feedback # reports to the user (and correctly handle cancelation and progress # reports!) # 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}
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = startPoints.fields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Loading start points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount() else 0 points = [] source_attributes = {} i = 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo(QCoreApplication.translate('ServiceAreaFromLayer', 'Calculating service areas…')) graph = builder.graph() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) vertices = [] upperBoundary = [] lowerBoundary = [] total = 100.0 / len(snappedPoints) if snappedPoints else 1 for i, p in enumerate(snappedPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) for j, v in enumerate(cost): if v > travelCost and tree[j] != -1: vertexId = graph.edge(tree[j]).fromVertex() if cost[vertexId] <= travelCost: vertices.append(j) for j in vertices: upperBoundary.append(graph.vertex(graph.edge(tree[j]).toVertex()).point()) lowerBoundary.append(graph.vertex(graph.edge(tree[j]).fromVertex()).point()) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) feat.setGeometry(geomUpper) attrs = source_attributes[i] attrs.extend(['upper', origPoint]) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) attrs[-2] = 'lower' feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) vertices[:] = [] upperBoundary[:] = [] lowerBoundary[:] = [] feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder( iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint]) feedback.pushInfo(self.tr('Calculating service area...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = [] for i, v in enumerate(cost): if v > travelCost and tree[i] != -1: vertexId = graph.edge(tree[i]).outVertex() if cost[vertexId] <= travelCost: vertices.append(i) upperBoundary = [] lowerBoundary = [] for i in vertices: upperBoundary.append( graph.vertex(graph.edge(tree[i]).inVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge(tree[i]).outVertex()).point()) feedback.pushInfo(self.tr('Writing results...')) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName(self.OUTPUT_POINTS).getVectorWriter( fields, QgsWkbTypes.MultiPoint, layer.crs(), context) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName(self.OUTPUT_POLYGON).getVectorWriter( fields, QgsWkbTypes.Polygon, layer.crs(), context) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer
def processAlgorithm(self, parameters, context, feedback): spacing = self.parameterAsDouble(parameters, self.SPACING, context) inset = self.parameterAsDouble(parameters, self.INSET, context) randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context) isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context) crs = self.parameterAsCrs(parameters, self.CRS, context) extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, crs) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine( extent_geom.constGet()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if feedback.isCanceled(): break if randomize: geom = QgsGeometry().fromPointXY( QgsPointXY( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry().fromPointXY(QgsPointXY(x, y)) if extent_engine.intersects(geom.constGet()): f.setAttribute('id', count) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) x += pSpacing count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing return {self.OUTPUT: dest_id}
def clip_voronoi(self, edges, c, width, height, extent, point, xyminmax): """Clip voronoi function based on code written for Inkscape. Copyright (C) 2010 Alvin Penner, [email protected] Clips one Thiessen polygon (convex polygon) to extent """ pt_x = point.x() - extent.xMinimum() pt_y = point.y() - extent.yMinimum() (xmin, ymin, xmax, ymax) = xyminmax def isclose(a, b, rel_tol=1e-9, abs_tol=0.0): return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) def clip_line(x1, y1, x2, y2, w, h): if x1 < 0 and x2 < 0: # Completely to the left return [0, 0, 0, 0] if x1 > w and x2 > w: # Completely to the right return [0, 0, 0, 0] if y1 < 0 and y2 < 0: # Completely below return [0, 0, 0, 0] if y1 > h and y2 > h: # Completely above return [0, 0, 0, 0] # Clip on the left envelope boundary if x1 < 0: # First point to the left y1 = (y1 * x2 - y2 * x1) / (x2 - x1) x1 = 0 if x2 < 0: # Last point to the left y2 = (y1 * x2 - y2 * x1) / (x2 - x1) x2 = 0 # Clip on the right envelope boundary if x1 > w: # First point to the right y1 = y1 + (w - x1) * (y2 - y1) / (x2 - x1) x1 = w if x2 > w: # Last point to the right y2 = y1 + (w - x1) * (y2 - y1) / (x2 - x1) x2 = w if isclose(x1, x2) and isclose(y1, y2): return [0, 0, 0, 0] # Clip on the bottom envelope boundary if y1 < 0: # First point below x1 = (x1 * y2 - x2 * y1) / (y2 - y1) y1 = 0 if y2 < 0: # Second point below x2 = (x1 * y2 - x2 * y1) / (y2 - y1) y2 = 0 # Clip on the top envelope boundary if y1 > h: # First point above x1 = x1 + (h - y1) * (x2 - x1) / (y2 - y1) y1 = h if y2 > h: # Second point above x2 = x1 + (h - y1) * (x2 - x1) / (y2 - y1) y2 = h if isclose(x1, x2) and isclose(y1, y2): return [0, 0, 0, 0] return [x1, y1, x2, y2] bndpoints = [] hasXMin = False hasYMin = False hasXMax = False hasYMax = False XMinNumber = 0 XMaxNumber = 0 YMinNumber = 0 YMaxNumber = 0 # The same line may appear twice for collinear input points, # so have to remember which lines have contributed XMinLine = -1 XMaxLine = -1 YMinLine = -1 YMaxLine = -1 for edge in edges: if edge[1] >= 0 and edge[2] >= 0: # Two vertices [x1, y1, x2, y2 ] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height) elif edge[1] >= 0: # Only one (left) vertex if c.lines[edge[0]][1] == 0: # Vertical line #xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] xtemp = c.vertices[edge[1]][0] ytemp = 0 - 1 #if c.vertices[edge[1]][1] > height / 2: # ytemp = height #else: # ytemp = 0 else: # Create an end of the line at the right edge - OK xtemp = width ytemp = (c.lines[edge[0]][2] - width * c.lines[edge[0]][0]) / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line(c.vertices[edge[1]][0], c.vertices[edge[1]][1], xtemp, ytemp, width, height) elif edge[2] >= 0: # Only one (right) vertex if c.lines[edge[0]][1] == 0: # Vertical line #xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] xtemp = c.vertices[edge[2]][0] ytemp = height + 1 #if c.vertices[edge[2]][1] > height / 2: # ytemp = height #else: # ytemp = 0.0 else: # End the line at the left edge - OK xtemp = 0.0 ytemp = c.lines[edge[0]][2] / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line( xtemp, ytemp, c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, ) else: # No vertex, only a line if c.lines[edge[0]][1] == 0: # Vertical line - should not happen xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] ytemp = 0.0 xend = xtemp yend = height else: # End the line at both edges - ??? xtemp = 0.0 ytemp = c.lines[edge[0]][2] / c.lines[edge[0]][1] xend = width yend = (c.lines[edge[0]][2] - width * c.lines[edge[0]][0]) / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line( xtemp, ytemp, xend, yend, width, height, ) if x1 or x2 or y1 or y2: bndpoints.append( QgsPointXY(x1 + extent.xMinimum(), y1 + extent.yMinimum())) bndpoints.append( QgsPointXY(x2 + extent.xMinimum(), y2 + extent.yMinimum())) if 0 in (x1, x2): hasXMin = True if XMinLine != edge[0]: XMinNumber = XMinNumber + 1 XMinLine = edge[0] if 0 in (y1, y2): hasYMin = True if YMinLine != edge[0]: YMinNumber = YMinNumber + 1 YMinLine = edge[0] if height in (y1, y2): hasYMax = True if YMaxLine != edge[0]: YMaxNumber = YMaxNumber + 1 YMaxLine = edge[0] if width in (x1, x2): hasXMax = True if XMaxLine != edge[0]: XMaxNumber = XMaxNumber + 1 XMaxLine = edge[0] # Add auxiliary points for corner cases, if necessary (duplicate # points is not a problem - will be ignored later). # a) Extreme input points (lowest, leftmost, rightmost, highest) # A point can be extreme on both axis if pt_x == xmin: # leftmost point if XMinNumber == 0: bndpoints.append( QgsPointXY(extent.xMinimum(), extent.yMinimum())) bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) elif XMinNumber == 1: if hasYMin: bndpoints.append( QgsPointXY(extent.xMinimum(), extent.yMinimum())) elif hasYMax: bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) elif pt_x == xmax: # rightmost point if XMaxNumber == 0: bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) elif XMaxNumber == 1: if hasYMin: bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) elif hasYMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) if pt_y == ymin: # lowest point if YMinNumber == 0: bndpoints.append( QgsPointXY(extent.xMinimum(), extent.yMinimum())) bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) elif YMinNumber == 1: if hasXMin: bndpoints.append( QgsPointXY(extent.xMinimum(), extent.yMinimum())) elif hasXMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) elif pt_y == ymax: # highest point if YMaxNumber == 0: bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) elif YMaxNumber == 1: if hasXMin: bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) elif hasXMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) # b) Polygon that covers the x or the y extent: if hasYMin and hasYMax: if YMaxNumber > 1: if hasXMin: bndpoints.append( QgsPointXY(extent.xMinimum(), extent.yMinimum())) elif hasXMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) elif YMinNumber > 1: if hasXMin: bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) elif hasXMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) elif hasXMin and hasXMax: if XMaxNumber > 1: if hasYMin: bndpoints.append( QgsPointXY(extent.xMinimum(), extent.yMinimum())) elif hasYMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) elif XMinNumber > 1: if hasYMin: bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) elif hasYMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) # c) Simple corners: if XMinNumber == 1 and YMinNumber == 1 and not hasXMax and not hasYMax: bndpoints.append(QgsPointXY(extent.xMinimum(), extent.yMinimum())) if XMinNumber == 1 and YMaxNumber == 1 and not hasXMax and not hasYMin: bndpoints.append( QgsPointXY(extent.xMinimum(), height + extent.yMinimum())) if XMaxNumber == 1 and YMinNumber == 1 and not hasXMin and not hasYMax: bndpoints.append( QgsPointXY(width + extent.xMinimum(), extent.yMinimum())) if XMaxNumber == 1 and YMaxNumber == 1 and not hasXMin and not hasYMin: bndpoints.append( QgsPointXY(width + extent.xMinimum(), height + extent.yMinimum())) return bndpoints
def testQgsGeometryRepr(self): p = QgsPointXY(123.456, 987.654) g = QgsGeometry.fromPointXY(p) self.assertTrue(g.__repr__().startswith('<QgsGeometry: Point (123.456'))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.PrmInputLayer, context) shapetype = self.parameterAsInt(parameters, self.PrmShapeType, context) azimuthmode = self.parameterAsInt(parameters, self.PrmAzimuthMode, context) startanglecol = self.parameterAsString(parameters, self.PrmAzimuth1Field, context) endanglecol = self.parameterAsString(parameters, self.PrmAzimuth2Field, context) radiusCol = self.parameterAsString(parameters, self.PrmRadiusField, context) startangle = self.parameterAsDouble(parameters, self.PrmDefaultAzimuth1, context) endangle = self.parameterAsDouble(parameters, self.PrmDefaultAzimuth2, context) radius = self.parameterAsDouble(parameters, self.PrmDefaultRadius, context) segments = self.parameterAsInt(parameters, self.PrmDrawingSegments, context) units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure, context) export_geom = self.parameterAsBool(parameters, self.PrmExportInputGeometry, context) measureFactor = conversionToMeters(units) radius *= measureFactor ptSpacing = 360.0 / segments srcCRS = source.sourceCrs() fields = source.fields() if export_geom: names = fields.names() name_x, name_y = settings.getGeomNames(names) fields.append(QgsField(name_x, QVariant.Double)) fields.append(QgsField(name_y, QVariant.Double)) if shapetype == 0: (sink, dest_id) = self.parameterAsSink(parameters, self.PrmOutputLayer, context, fields, QgsWkbTypes.Polygon, srcCRS) else: (sink, dest_id) = self.parameterAsSink(parameters, self.PrmOutputLayer, context, fields, QgsWkbTypes.LineString, srcCRS) if srcCRS != epsg4326: geomTo4326 = QgsCoordinateTransform(srcCRS, epsg4326, QgsProject.instance()) toSinkCrs = QgsCoordinateTransform(epsg4326, srcCRS, QgsProject.instance()) featureCount = source.featureCount() total = 100.0 / featureCount if featureCount else 0 numbad = 0 iterator = source.getFeatures() for cnt, feature in enumerate(iterator): if feedback.isCanceled(): break try: pts = [] pt = feature.geometry().asPoint() pt_orig_x = pt.x() pt_orig_y = pt.y() # make sure the coordinates are in EPSG:4326 if srcCRS != epsg4326: pt = geomTo4326.transform(pt.x(), pt.y()) pts.append(pt) if startanglecol: sangle = float(feature[startanglecol]) else: sangle = startangle if endanglecol: eangle = float(feature[endanglecol]) else: eangle = endangle if azimuthmode == 1: width = abs(eangle) / 2.0 eangle = sangle + width sangle -= width if radiusCol: dist = float(feature[radiusCol]) * measureFactor else: dist = radius sangle = sangle % 360 eangle = eangle % 360 if sangle > eangle: # We are crossing the 0 boundry so lets just subtract # 360 from it. sangle -= 360.0 while sangle < eangle: g = geod.Direct(pt.y(), pt.x(), sangle, dist, Geodesic.LATITUDE | Geodesic.LONGITUDE) pts.append(QgsPointXY(g['lon2'], g['lat2'])) sangle += ptSpacing # add this number of degrees to the angle g = geod.Direct(pt.y(), pt.x(), eangle, dist, Geodesic.LATITUDE | Geodesic.LONGITUDE) pts.append(QgsPointXY(g['lon2'], g['lat2'])) pts.append(pt) makeIdlCrossingsPositive(pts) # If the Output crs is not 4326 transform the points to the proper crs if srcCRS != epsg4326: for x, ptout in enumerate(pts): pts[x] = toSinkCrs.transform(ptout) f = QgsFeature() if shapetype == 0: f.setGeometry(QgsGeometry.fromPolygonXY([pts])) else: f.setGeometry(QgsGeometry.fromPolylineXY(pts)) attr = feature.attributes() if export_geom: attr.append(pt_orig_x) attr.append(pt_orig_y) f.setAttributes(attr) sink.addFeature(f) except Exception: numbad += 1 if cnt % 100 == 0: feedback.setProgress(int(cnt * total)) if numbad > 0: feedback.pushInfo( tr("{} out of {} features had invalid parameters and were ignored." .format(numbad, featureCount))) return {self.PrmOutputLayer: dest_id}
def testQgsPointXYRepr(self): p = QgsPointXY(123.456, 987.654) self.assertTrue(p.__repr__().startswith('<QgsPointXY: POINT(123.456'))
def _hexagonGrid(self, sink, bbox, hSpacing, vSpacing, hOverlay, vOverlay, feedback): feat = QgsFeature() # To preserve symmetry, hspacing is fixed relative to vspacing xVertexLo = 0.288675134594813 * vSpacing xVertexHi = 0.577350269189626 * vSpacing hSpacing = xVertexLo + xVertexHi hOverlay = hSpacing - hOverlay if hOverlay < 0: raise QgsProcessingException( self.tr('To preserve symmetry, hspacing is fixed relative to vspacing\n \ hspacing is fixed at: {0} and hoverlay is fixed at: {1}\n \ hoverlay cannot be negative. Increase hoverlay.').format(hSpacing, hOverlay) ) halfVSpacing = vSpacing / 2.0 columns = int(math.ceil(float(bbox.width()) / hOverlay)) rows = int(math.ceil(float(bbox.height()) / (vSpacing - vOverlay))) cells = rows * columns count_update = cells * 0.05 id = 1 count = 0 for col in range(columns): if feedback.isCanceled(): break # (column + 1) and (row + 1) calculation is used to maintain # topology between adjacent shapes and avoid overlaps/holes # due to rounding errors x1 = bbox.xMinimum() + (col * hOverlay) # far left x2 = x1 + (xVertexHi - xVertexLo) # left x3 = bbox.xMinimum() + (col * hOverlay) + hSpacing # right x4 = x3 + (xVertexHi - xVertexLo) # far right for row in range(rows): if (col % 2) == 0: y1 = bbox.yMaximum() + (row * vOverlay) - (((row * 2) + 0) * halfVSpacing) # hi y2 = bbox.yMaximum() + (row * vOverlay) - (((row * 2) + 1) * halfVSpacing) # mid y3 = bbox.yMaximum() + (row * vOverlay) - (((row * 2) + 2) * halfVSpacing) # lo else: y1 = bbox.yMaximum() + (row * vOverlay) - (((row * 2) + 1) * halfVSpacing) # hi y2 = bbox.yMaximum() + (row * vOverlay) - (((row * 2) + 2) * halfVSpacing) # mid y3 = bbox.yMaximum() + (row * vOverlay) - (((row * 2) + 3) * halfVSpacing) # lo polyline = [] polyline.append(QgsPointXY(x1, y2)) polyline.append(QgsPointXY(x2, y1)) polyline.append(QgsPointXY(x3, y1)) polyline.append(QgsPointXY(x4, y2)) polyline.append(QgsPointXY(x3, y3)) polyline.append(QgsPointXY(x2, y3)) polyline.append(QgsPointXY(x1, y2)) feat.setGeometry(QgsGeometry.fromPolygonXY([polyline])) feat.setAttributes([x1, y1, x4, y3, id]) sink.addFeature(feat, QgsFeatureSink.FastInsert) id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / cells * 100))
def processAlgorithm(self, parameters, context, feedback): # Get variables from dialog source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) field_name = self.parameterAsString(parameters, self.FIELD, context) kneighbors = self.parameterAsInt(parameters, self.KNEIGHBORS, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) current = 0 # Get properties of the field the grouping is based on if use_field: field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append( source.fields()[field_index] ) # Add a field with the name of the grouping field # Initialize writer (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) success = False fid = 0 # Get unique values of grouping field unique_values = source.uniqueValues(field_index) total = 100.0 / float( source.featureCount() * len(unique_values)) for unique in unique_values: points = [] filter = QgsExpression.createFieldEqualityExpression( field_name, unique) request = QgsFeatureRequest().setFilterExpression(filter) request.setSubsetOfAttributes([]) # Get features with the grouping attribute equal to the current grouping value features = source.getFeatures(request) for in_feature in features: if feedback.isCanceled(): break # Add points or vertices of more complex geometry points.extend(extract_points(in_feature.geometry())) current += 1 feedback.setProgress(int(current * total)) # A minimum of 3 points is necessary to proceed if len(points) >= 3: out_feature = QgsFeature() the_hull = concave_hull(points, kneighbors) if the_hull: vertex = [ QgsPointXY(point[0], point[1]) for point in the_hull ] poly = QgsGeometry().fromPolygonXY([vertex]) out_feature.setGeometry(poly) # Give the polygon the same attribute as the point grouping attribute out_feature.setAttributes([fid, unique]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) success = True # at least one polygon created fid += 1 if not success: raise QgsProcessingException( 'No hulls could be created. Most likely there were not at least three unique points in any of the groups.' ) else: # Field parameter provided but can't read from it raise QgsProcessingException('Unable to find grouping field') else: # Not grouped by field # Initialize writer (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) points = [] request = QgsFeatureRequest() request.setSubsetOfAttributes([]) features = source.getFeatures(request) # Get all features total = 100.0 / source.featureCount() if source.featureCount( ) else 0 for in_feature in features: if feedback.isCanceled(): break # Add points or vertices of more complex geometry points.extend(extract_points(in_feature.geometry())) current += 1 feedback.setProgress(int(current * total)) # A minimum of 3 points is necessary to proceed if len(points) >= 3: out_feature = QgsFeature() the_hull = concave_hull(points, kneighbors) if the_hull: vertex = [ QgsPointXY(point[0], point[1]) for point in the_hull ] poly = QgsGeometry().fromPolygonXY([vertex]) out_feature.setGeometry(poly) out_feature.setAttributes([0]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: # the_hull returns None only when there are less than three points after cleaning raise QgsProcessingException( 'At least three unique points are required to create a concave hull.' ) else: raise QgsProcessingException( 'At least three points are required to create a concave hull.' ) return {self.OUTPUT: dest_id}
def test_resetSnappingIndex(self): self.pointsLayer.setDependencies([]) self.linesLayer.setDependencies([]) self.pointsLayer2.setDependencies([]) ms = QgsMapSettings() ms.setOutputSize(QSize(100, 100)) ms.setExtent(QgsRectangle(0, 0, 1, 1)) self.assertTrue(ms.hasValidSettings()) u = QgsSnappingUtils() u.setMapSettings(ms) cfg = u.config() cfg.setEnabled(True) cfg.setMode(QgsSnappingConfig.AdvancedConfiguration) cfg.setIndividualLayerSettings(self.pointsLayer, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) m = u.snapToMap(QPoint(95, 100)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(1, 0)) f = QgsFeature(self.linesLayer.fields()) f.setId(1) geom = QgsGeometry.fromWkt("LINESTRING(0 0,1 1)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() l1 = len([f for f in self.pointsLayer.getFeatures()]) self.assertEqual(l1, 4) m = u.snapToMap(QPoint(95, 0)) # snapping not updated self.pointsLayer.setDependencies([]) self.assertEqual(m.isValid(), False) # set layer dependencies self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())]) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(2) geom = QgsGeometry.fromWkt("LINESTRING(0 0,0.5 0.5)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the snapped point is OK m = u.snapToMap(QPoint(45, 50)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.5, 0.5)) self.pointsLayer.setDependencies([]) # test chained layer dependencies A -> B -> C cfg.setIndividualLayerSettings(self.pointsLayer2, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())]) self.pointsLayer2.setDependencies([QgsMapLayerDependency(self.pointsLayer.id())]) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(3) geom = QgsGeometry.fromWkt("LINESTRING(0 0.2,0.5 0.8)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the second snapped point is OK m = u.snapToMap(QPoint(75, 100 - 80)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.7, 0.8)) self.pointsLayer.setDependencies([]) self.pointsLayer2.setDependencies([])
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) 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)) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = source.featureCount() total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break # pick random feature fid = random.randint(0, featureCount - 1) f = next( source.getFeatures( request.setFilterFid(fid).setSubsetOfAttributes([]))) fGeom = f.geometry() if fGeom.isMultipart(): lines = fGeom.asMultiPolyline() # pick random line lineId = random.randint(0, len(lines) - 1) vertices = lines[lineId] else: vertices = fGeom.asPolyline() # pick random segment if len(vertices) == 2: vid = 0 else: vid = random.randint(0, len(vertices) - 2) startPoint = vertices[vid] endPoint = vertices[vid + 1] length = da.measureLine(startPoint, endPoint) dist = length * random.random() if dist > minDistance: d = dist / (length - dist) 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(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.addFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr( 'Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
from qgis.core import ( QgsFeature, QgsGeometry, QgsPointXY, QgsFeatureRequest, QgsExpression, QgsProject, QgsOfflineEditing, ) # Tet features, fields: [id, name, geometry] # "id" is used as a pk to retriev features by attribute TEST_FEATURES = [ (1, 'name 1', QgsPointXY(9, 45)), (2, 'name 2', QgsPointXY(9.5, 45.5)), (3, 'name 3', QgsPointXY(9.5, 46)), (4, 'name 4', QgsPointXY(10, 46.5)), ] # Additional features for insert test TEST_FEATURES_INSERT = [ (5, 'name 5', QgsPointXY(9.7, 45.7)), (6, 'name 6', QgsPointXY(10.6, 46.6)), ] class OfflineTestBase(object): """Generic test methods for all online providers"""
def setUp(self): self.mPoint = QgsPointXY(10.0, 10.0)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) bbox = source.sourceExtent() sourceIndex = QgsSpatialIndex(source, feedback) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) ids = sourceIndex.intersects(geom.buffer(5, 5).boundingBox()) if len(ids) > 0 and \ vector.checkMinDistance(p, index, minDistance, points): request = QgsFeatureRequest().setFilterFids( ids).setSubsetOfAttributes([]) for f in source.getFeatures(request): if feedback.isCanceled(): break tmpGeom = f.geometry() if geom.within(tmpGeom): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.insertFeature(f) points[nPoints] = p nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: feedback.pushInfo( self.tr( 'Could not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) return {self.OUTPUT: dest_id}
def testDateTimeWriteShapefile(self): """Check writing date and time fields to an ESRI shapefile.""" ml = QgsVectorLayer( ('Point?crs=epsg:4326&field=id:int&' 'field=date_f:date&field=time_f:time&field=dt_f:datetime'), 'test', 'memory') self.assertTrue(ml.isValid()) provider = ml.dataProvider() self.assertIsNotNone(provider) ft = QgsFeature() ft.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 10))) ft.setAttributes([ 1, QDate(2014, 3, 5), QTime(13, 45, 22), QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22)) ]) res, features = provider.addFeatures([ft]) self.assertTrue(res) self.assertTrue(features) dest_file_name = os.path.join(str(QDir.tempPath()), 'datetime.shp') crs = QgsCoordinateReferenceSystem() crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, dest_file_name, 'utf-8', crs, 'ESRI Shapefile') self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') fields = created_layer.dataProvider().fields() self.assertEqual( fields.at(fields.indexFromName('date_f')).type(), QVariant.Date) # shapefiles do not support time types, result should be string self.assertEqual( fields.at(fields.indexFromName('time_f')).type(), QVariant.String) # shapefiles do not support datetime types, result should be string self.assertEqual( fields.at(fields.indexFromName('dt_f')).type(), QVariant.String) f = next(created_layer.getFeatures(QgsFeatureRequest())) date_idx = created_layer.fields().lookupField('date_f') self.assertIsInstance(f.attributes()[date_idx], QDate) self.assertEqual(f.attributes()[date_idx], QDate(2014, 3, 5)) time_idx = created_layer.fields().lookupField('time_f') # shapefiles do not support time types self.assertIsInstance(f.attributes()[time_idx], str) self.assertEqual(f.attributes()[time_idx], '13:45:22') # shapefiles do not support datetime types datetime_idx = created_layer.fields().lookupField('dt_f') self.assertIsInstance(f.attributes()[datetime_idx], str) self.assertEqual( f.attributes()[datetime_idx], QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22)).toString("yyyy/MM/dd hh:mm:ss.zzz"))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) fields = QgsFields() fields.append(QgsField('POINTA', QVariant.Double, '', 24, 15)) fields.append(QgsField('POINTB', QVariant.Double, '', 24, 15)) fields.append(QgsField('POINTC', QVariant.Double, '', 24, 15)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) pts = [] ptDict = {} ptNdx = -1 c = voronoi.Context() features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break geom = QgsGeometry(inFeat.geometry()) if geom.isNull(): continue if geom.isMultipart(): points = geom.asMultiPoint() else: points = [geom.asPoint()] for n, point in enumerate(points): x = point.x() y = point.y() pts.append((x, y)) ptNdx += 1 ptDict[ptNdx] = (inFeat.id(), n) feedback.setProgress(int(current * total)) if len(pts) < 3: raise QgsProcessingException( self.tr('Input file should contain at least 3 points. Choose ' 'another file and try again.')) uniqueSet = set(item for item in pts) ids = [pts.index(item) for item in uniqueSet] sl = voronoi.SiteList([voronoi.Site(*i) for i in uniqueSet]) c.triangulate = True voronoi.voronoi(sl, c) triangles = c.triangles feat = QgsFeature() total = 100.0 / len(triangles) if triangles else 1 for current, triangle in enumerate(triangles): if feedback.isCanceled(): break indices = list(triangle) indices.append(indices[0]) polygon = [] attrs = [] step = 0 for index in indices: fid, n = ptDict[ids[index]] request = QgsFeatureRequest().setFilterFid(fid) inFeat = next(source.getFeatures(request)) geom = QgsGeometry(inFeat.geometry()) if geom.isMultipart(): point = QgsPointXY(geom.asMultiPoint()[n]) else: point = QgsPointXY(geom.asPoint()) polygon.append(point) if step <= 3: attrs.append(ids[index]) step += 1 feat.setAttributes(attrs) geometry = QgsGeometry().fromPolygonXY([polygon]) feat.setGeometry(geometry) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testGettersSetters(self): segment = QgsLineSegment2D(QgsPointXY(1, 2), QgsPointXY(3, 4)) self.assertEqual(segment.start(), QgsPointXY(1, 2)) self.assertEqual(segment.end(), QgsPointXY(3, 4)) self.assertEqual(segment.startX(), 1) self.assertEqual(segment.startY(), 2) self.assertEqual(segment.endX(), 3) self.assertEqual(segment.endY(), 4) segment.setStartX(5) self.assertEqual(segment.start(), QgsPointXY(5, 2)) self.assertEqual(segment.end(), QgsPointXY(3, 4)) self.assertEqual(segment.startX(), 5) self.assertEqual(segment.startY(), 2) self.assertEqual(segment.endX(), 3) self.assertEqual(segment.endY(), 4) segment.setStartY(6) self.assertEqual(segment.start(), QgsPointXY(5, 6)) self.assertEqual(segment.end(), QgsPointXY(3, 4)) self.assertEqual(segment.startX(), 5) self.assertEqual(segment.startY(), 6) self.assertEqual(segment.endX(), 3) self.assertEqual(segment.endY(), 4) segment.setEndX(7) self.assertEqual(segment.start(), QgsPointXY(5, 6)) self.assertEqual(segment.end(), QgsPointXY(7, 4)) self.assertEqual(segment.startX(), 5) self.assertEqual(segment.startY(), 6) self.assertEqual(segment.endX(), 7) self.assertEqual(segment.endY(), 4) segment.setEndY(8) self.assertEqual(segment.start(), QgsPointXY(5, 6)) self.assertEqual(segment.end(), QgsPointXY(7, 8)) self.assertEqual(segment.startX(), 5) self.assertEqual(segment.startY(), 6) self.assertEqual(segment.endX(), 7) self.assertEqual(segment.endY(), 8) segment.setStart(QgsPointXY(1, 2)) self.assertEqual(segment.start(), QgsPointXY(1, 2)) self.assertEqual(segment.end(), QgsPointXY(7, 8)) self.assertEqual(segment.startX(), 1) self.assertEqual(segment.startY(), 2) self.assertEqual(segment.endX(), 7) self.assertEqual(segment.endY(), 8) segment.setEnd(QgsPointXY(3, 4)) self.assertEqual(segment.start(), QgsPointXY(1, 2)) self.assertEqual(segment.end(), QgsPointXY(3, 4)) self.assertEqual(segment.startX(), 1) self.assertEqual(segment.startY(), 2) self.assertEqual(segment.endX(), 3) self.assertEqual(segment.endY(), 4)