def testLengthMeasureAndUnits(self): """Test a variety of length measurements in different CRS and ellipsoid modes, to check that the calculated lengths and units are always consistent """ da = QgsDistanceArea() da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(3452), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # We check both the measured length AND the units, in case the logic regarding # ellipsoids and units changes in future distance = da.measureLine(QgsPointXY(1, 1), QgsPointXY(2, 3)) units = da.lengthUnits() print(("measured {} in {}".format(distance, QgsUnitTypes.toString(units)))) assert ((abs(distance - 2.23606797) < 0.00000001 and units == QgsUnitTypes.DistanceDegrees) or (abs(distance - 248.52) < 0.01 and units == QgsUnitTypes.DistanceMeters)) da.setEllipsoid("WGS84") distance = da.measureLine(QgsPointXY(1, 1), QgsPointXY(2, 3)) units = da.lengthUnits() print(("measured {} in {}".format(distance, QgsUnitTypes.toString(units)))) # should always be in Meters self.assertAlmostEqual(distance, 247555.57, delta=0.01) self.assertEqual(units, QgsUnitTypes.DistanceMeters) # test converting the resultant length distance = da.convertLengthMeasurement(distance, QgsUnitTypes.DistanceNauticalMiles) self.assertAlmostEqual(distance, 133.669, delta=0.01) # now try with a source CRS which is in feet da.setSourceCrs(QgsCoordinateReferenceSystem.fromSrsId(27469), QgsProject.instance().transformContext()) da.setEllipsoid("NONE") # measurement should be in feet distance = da.measureLine(QgsPointXY(1, 1), QgsPointXY(2, 3)) units = da.lengthUnits() print(("measured {} in {}".format(distance, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(distance, 2.23606797, delta=0.000001) self.assertEqual(units, QgsUnitTypes.DistanceFeet) # test converting the resultant length distance = da.convertLengthMeasurement(distance, QgsUnitTypes.DistanceMeters) self.assertAlmostEqual(distance, 0.6815, delta=0.001) da.setEllipsoid("WGS84") # now should be in Meters again distance = da.measureLine(QgsPointXY(1, 1), QgsPointXY(2, 3)) units = da.lengthUnits() print(("measured {} in {}".format(distance, QgsUnitTypes.toString(units)))) self.assertAlmostEqual(distance, 0.67953772, delta=0.000001) self.assertEqual(units, QgsUnitTypes.DistanceMeters) # test converting the resultant length distance = da.convertLengthMeasurement(distance, QgsUnitTypes.DistanceFeet) self.assertAlmostEqual(distance, 2.2294, delta=0.001)
def testLengthMeasureAndUnits(self): """Test a variety of length measurements in different CRS and ellipsoid modes, to check that the calculated lengths and units are always consistent """ da = QgsDistanceArea() da.setSourceCrs(3452) da.setEllipsoidalMode(False) da.setEllipsoid("NONE") daCRS = QgsCoordinateReferenceSystem() daCRS.createFromSrsId(da.sourceCrs()) # We check both the measured length AND the units, in case the logic regarding # ellipsoids and units changes in future distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) assert ((abs(distance - 2.23606797) < 0.00000001 and units == QGis.Degrees) or (abs(distance - 248.52) < 0.01 and units == QGis.Meters)) da.setEllipsoid("WGS84") distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) assert ((abs(distance - 2.23606797) < 0.00000001 and units == QGis.Degrees) or (abs(distance - 248.52) < 0.01 and units == QGis.Meters)) da.setEllipsoidalMode(True) distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) # should always be in Meters self.assertAlmostEqual(distance, 247555.57, delta=0.01) self.assertEqual(units, QGis.Meters) # now try with a source CRS which is in feet da.setSourceCrs(27469) da.setEllipsoidalMode(False) # measurement should be in feet distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) self.assertAlmostEqual(distance, 2.23606797, delta=0.000001) self.assertEqual(units, QGis.Feet) da.setEllipsoidalMode(True) # now should be in Meters again distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) self.assertAlmostEqual(distance, 0.67953772, delta=0.000001) self.assertEqual(units, QGis.Meters)
def nearest_neighbour_analysis(self, vlayer): vprovider = vlayer.dataProvider() sumDist = 0.00 distance = QgsDistanceArea() A = vlayer.extent() A = float(A.width() * A.height()) index = ftools_utils.createIndex(vprovider) nFeat = vprovider.featureCount() nElement = 0 if nFeat > 0: self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0) self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, nFeat)) feat = QgsFeature() neighbour = QgsFeature() fit = vprovider.getFeatures() while fit.nextFeature(feat): neighbourID = index.nearestNeighbor(feat.geometry().asPoint(), 2)[1] vprovider.getFeatures(QgsFeatureRequest().setFilterFid(neighbourID).setSubsetOfAttributes([])).nextFeature(neighbour) nearDist = distance.measureLine(neighbour.geometry().asPoint(), feat.geometry().asPoint()) sumDist += nearDist nElement += 1 self.emit(SIGNAL("runStatus(PyQt_PyObject)"), nElement) nVal = vprovider.featureCount() do = float(sumDist) / nVal de = float(0.5 / math.sqrt(nVal / A)) d = float(do / de) SE = float(0.26136 / math.sqrt((nVal * nVal) / A)) zscore = float((do - de) / SE) lstStats = [] lstStats.append(self.tr("Observed mean distance:") + unicode(do)) lstStats.append(self.tr("Expected mean distance:") + unicode(de)) lstStats.append(self.tr("Nearest neighbour index:") + unicode(d)) lstStats.append(self.tr("N:") + unicode(nVal)) lstStats.append(self.tr("Z-Score:") + unicode(zscore)) return (lstStats, [])
def processAlgorithm(self, parameters, context, feedback): if parameters[self.INPUT] == parameters[self.HUBS]: raise QgsProcessingException( self.tr('Same layer given for both hubs and spokes')) point_source = self.parameterAsSource(parameters, self.INPUT, context) hub_source = self.parameterAsSource(parameters, self.HUBS, context) fieldName = self.parameterAsString(parameters, self.FIELD, context) units = self.UNITS[self.parameterAsEnum(parameters, self.UNIT, context)] fields = point_source.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, point_source.sourceCrs()) index = QgsSpatialIndex(hub_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) distance = QgsDistanceArea() distance.setSourceCrs(point_source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.project().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = point_source.getFeatures() total = 100.0 / point_source.featureCount() if point_source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): sink.addFeature(f, QgsFeatureSink.FastInsert) continue src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next(hub_source.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0]).setSubsetOfAttributes([fieldName], hub_source.fields()).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) if units != self.LAYER_UNITS: hub_dist_in_desired_units = distance.convertLengthMeasurement(hubDist, units) else: hub_dist_in_desired_units = hubDist attributes = f.attributes() attributes.append(ft[fieldName]) attributes.append(hub_dist_in_desired_units) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPointXY(src)) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def regularMatrix(self, inLayer, inField, targetLayer, targetField, nPoints, progress): index = vector.spatialindex(targetLayer) inIdx = inLayer.fields().lookupField(inField) distArea = QgsDistanceArea() first = True features = vector.features(inLayer) total = 100.0 / len(features) for current, inFeat in enumerate(features): inGeom = inFeat.geometry() inID = str(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) if first: first = False data = ['ID'] for i in range(len(featList)): data.append('DIST_{0}'.format(i + 1)) self.writer.addRecord(data) data = [inID] for i in featList: request = QgsFeatureRequest().setFilterFid(i) outFeat = next(targetLayer.getFeatures(request)) outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) data.append(str(float(dist))) self.writer.addRecord(data) progress.setPercentage(int(current * total))
def measurePerimeter(geom): measure = QgsDistanceArea() value = 0.00 polygon = geom.asPolygon() for line in polygon: value += measure.measureLine(line) return value
def regularMatrix(self, inLayer, inField, targetLayer, targetField, nPoints, progress): features = vector.features(inLayer) total = 100.0 / len(features) if len(features) > 0 else 1 targetIdx = targetLayer.fieldNameIndex(targetField) first = True distArea = QgsDistanceArea() index = vector.spatialindex(targetLayer) for current, inFeat in enumerate(features): inGeom = inFeat.geometry() if first: featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) first = False data = ['ID'] request = QgsFeatureRequest().setFilterFids(featList).setSubsetOfAttributes([targetIdx]) for f in targetLayer.getFeatures(request): data.append(unicode(f[targetField])) self.writer.addRecord(data) data = [unicode(inFeat[inField])] for f in targetLayer.getFeatures(request): outGeom = f.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) data.append(unicode(float(dist))) self.writer.addRecord(data) progress.setPercentage(int(current * total))
def processAlgorithm(self, parameters, context, feedback): layerPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POINTS), context) layerHubs = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.HUBS), context) fieldName = self.getParameterValue(self.FIELD) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) fields = layerPoints.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, layerPoints.crs(), context) index = QgsProcessingUtils.createSpatialIndex(layerHubs, context) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs()) distance.setEllipsoid(QgsProject.instance().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = QgsProcessingUtils.getFeatures(layerPoints, context) total = 100.0 / layerPoints.featureCount() if layerPoints.featureCount() else 0 for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next(layerHubs.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0]).setSubsetOfAttributes([fieldName], layerHubs.fields()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append(sqrt( pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPolyline([src, closest])) writer.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) del writer
def calculateDistance(self, p1, p2): distance = QgsDistanceArea() distance.setSourceCrs(self.iface.activeLayer().crs()) distance.setEllipsoidalMode(True) # Sirgas 2000 distance.setEllipsoid('GRS1980') m = distance.measureLine(p1, p2) return m
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) spatialIndex = QgsSpatialIndex(source, feedback) distance = QgsDistanceArea() distance.setSourceCrs(source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.project().ellipsoid()) sumDist = 0.00 A = source.sourceExtent() A = float(A.width() * A.height()) features = source.getFeatures() count = source.featureCount() total = 100.0 / count if count else 1 for current, feat in enumerate(features): if feedback.isCanceled(): break neighbourID = spatialIndex.nearestNeighbor( feat.geometry().asPoint(), 2)[1] request = QgsFeatureRequest().setFilterFid(neighbourID).setSubsetOfAttributes([]) neighbour = next(source.getFeatures(request)) sumDist += distance.measureLine(neighbour.geometry().asPoint(), feat.geometry().asPoint()) feedback.setProgress(int(current * total)) do = float(sumDist) / count de = float(0.5 / math.sqrt(count / A)) d = float(do / de) SE = float(0.26136 / math.sqrt(count ** 2 / A)) zscore = float((do - de) / SE) results = {} results[self.OBSERVED_MD] = do results[self.EXPECTED_MD] = de results[self.NN_INDEX] = d results[self.POINT_COUNT] = count results[self.Z_SCORE] = zscore if output_file: data = [] data.append('Observed mean distance: ' + str(do)) data.append('Expected mean distance: ' + str(de)) data.append('Nearest neighbour index: ' + str(d)) data.append('Number of points: ' + str(count)) data.append('Z-Score: ' + str(zscore)) self.createHTML(output_file, data) results[self.OUTPUT_HTML_FILE] = output_file return results
def regularMatrix(self, parameters, context, source, inField, target_source, targetField, nPoints, feedback): distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) inIdx = source.fields().lookupField(inField) targetIdx = target_source.fields().lookupField(targetField) index = QgsSpatialIndex(target_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) first = True sink = None dest_id = None features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() if first: featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) first = False fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('ID') fields.append(input_id_field) for f in target_source.getFeatures(QgsFeatureRequest().setFilterFids(featList).setSubsetOfAttributes([targetIdx]).setDestinationCrs(source.sourceCrs(), context.transformContext())): fields.append(QgsField(str(f[targetField]), QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) data = [inFeat[inField]] for target in target_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setFilterFids(featList).setDestinationCrs(source.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break outGeom = target.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) data.append(dist) out_feature = QgsFeature() out_feature.setGeometry(inGeom) out_feature.setAttributes(data) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def linearMatrix(self, inLayer, inField, targetLayer, targetField, matType, nPoints, progress): if matType == 0: self.writer.addRecord(['InputID', 'TargetID', 'Distance']) else: self.writer.addRecord(['InputID', 'MEAN', 'STDDEV', 'MIN', 'MAX']) index = vector.spatialindex(targetLayer) inIdx = inLayer.fieldNameIndex(inField) outIdx = targetLayer.fieldNameIndex(targetField) outFeat = QgsFeature() inGeom = QgsGeometry() outGeom = QgsGeometry() distArea = QgsDistanceArea() features = vector.features(inLayer) current = 0 total = 100.0 / float(len(features)) for inFeat in features: inGeom = inFeat.geometry() inID = unicode(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 for i in featList: request = QgsFeatureRequest().setFilterFid(i) outFeat = targetLayer.getFeatures(request).next() outID = outFeat.attributes()[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: self.writer.addRecord([inID, unicode(outID), unicode(dist)]) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) self.writer.addRecord([inID, unicode(mean), unicode(vari), unicode(min(distList)), unicode(max(distList))]) current += 1 progress.setPercentage(int(current * total))
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri(self.getParameterValue(self.POINTS)) output = self.getOutputValue(self.OUTPUT) spatialIndex = vector.spatialindex(layer) neighbour = QgsFeature() distance = QgsDistanceArea() sumDist = 0.00 A = layer.extent() A = float(A.width() * A.height()) current = 0 features = vector.features(layer) count = len(features) total = 100.0 / float(len(features)) for feat in features: neighbourID = spatialIndex.nearestNeighbor( feat.geometry().asPoint(), 2)[1] request = QgsFeatureRequest().setFilterFid(neighbourID) neighbour = layer.getFeatures(request).next() sumDist += distance.measureLine(neighbour.geometry().asPoint(), feat.geometry().asPoint()) current += 1 progress.setPercentage(int(current * total)) do = float(sumDist) / count de = float(0.5 / math.sqrt(count / A)) d = float(do / de) SE = float(0.26136 / math.sqrt(count ** 2 / A)) zscore = float((do - de) / SE) data = [] data.append('Observed mean distance: ' + unicode(do)) data.append('Expected mean distance: ' + unicode(de)) data.append('Nearest neighbour index: ' + unicode(d)) data.append('Number of points: ' + unicode(count)) data.append('Z-Score: ' + unicode(zscore)) self.createHTML(output, data) self.setOutputValue(self.OBSERVED_MD, float(data[0].split(': ')[1])) self.setOutputValue(self.EXPECTED_MD, float(data[1].split(': ')[1])) self.setOutputValue(self.NN_INDEX, float(data[2].split(': ')[1])) self.setOutputValue(self.POINT_COUNT, float(data[3].split(': ')[1])) self.setOutputValue(self.Z_SCORE, float(data[4].split(': ')[1]))
def linearMatrix(self, inLayer, inField, targetLayer, targetField, matType, nPoints, feedback): if matType == 0: self.writer.addRecord(['InputID', 'TargetID', 'Distance']) else: self.writer.addRecord(['InputID', 'MEAN', 'STDDEV', 'MIN', 'MAX']) index = vector.spatialindex(targetLayer) inIdx = inLayer.fields().lookupField(inField) outIdx = targetLayer.fields().lookupField(targetField) distArea = QgsDistanceArea() features = vector.features(inLayer) total = 100.0 / len(features) for current, inFeat in enumerate(features): inGeom = inFeat.geometry() inID = str(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 request = QgsFeatureRequest().setFilterFids( featList).setSubsetOfAttributes([outIdx]) for outFeat in targetLayer.getFeatures(request): outID = outFeat.attributes()[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: self.writer.addRecord([inID, str(outID), str(dist)]) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) self.writer.addRecord([ inID, str(mean), str(vari), str(min(distList)), str(max(distList)) ]) feedback.setProgress(int(current * total))
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.POINTS), context) output = self.getOutputValue(self.OUTPUT) spatialIndex = QgsProcessingUtils.createSpatialIndex(layer, context) neighbour = QgsFeature() distance = QgsDistanceArea() sumDist = 0.00 A = layer.extent() A = float(A.width() * A.height()) features = QgsProcessingUtils.getFeatures(layer, context) count = QgsProcessingUtils.featureCount(layer, context) total = 100.0 / count for current, feat in enumerate(features): neighbourID = spatialIndex.nearestNeighbor( feat.geometry().asPoint(), 2)[1] request = QgsFeatureRequest().setFilterFid(neighbourID).setSubsetOfAttributes([]) neighbour = next(layer.getFeatures(request)) sumDist += distance.measureLine(neighbour.geometry().asPoint(), feat.geometry().asPoint()) feedback.setProgress(int(current * total)) do = float(sumDist) / count de = float(0.5 / math.sqrt(count / A)) d = float(do / de) SE = float(0.26136 / math.sqrt(count ** 2 / A)) zscore = float((do - de) / SE) data = [] data.append('Observed mean distance: ' + str(do)) data.append('Expected mean distance: ' + str(de)) data.append('Nearest neighbour index: ' + str(d)) data.append('Number of points: ' + str(count)) data.append('Z-Score: ' + str(zscore)) self.createHTML(output, data) self.setOutputValue(self.OBSERVED_MD, float(data[0].split(': ')[1])) self.setOutputValue(self.EXPECTED_MD, float(data[1].split(': ')[1])) self.setOutputValue(self.NN_INDEX, float(data[2].split(': ')[1])) self.setOutputValue(self.POINT_COUNT, float(data[3].split(': ')[1])) self.setOutputValue(self.Z_SCORE, float(data[4].split(': ')[1]))
def calculate_accuracy(self, polygon_layer, point_layer): """ Calculate the distance of each centroid on a point-layer to their surrounding polygons :param polygon_layer: A layer containing polygons :type polygon_layer: QgsVectorLayer :param point_layer: A layer containing the (supposed to be) centroids of that polygon :type point_layer: QgsVectorLayer :return: :rtype: """ point_provider = point_layer.dataProvider() add_attributes_if_not_exists(point_layer, [QgsField('DIST', QtCore.QVariant.Double)]) distance_area = QgsDistanceArea() poly_iterator = polygon_layer.dataProvider().getFeatures() point_iterator = point_provider.getFeatures() poly_feature = QgsFeature() point_feature = QgsFeature() field_index = point_provider.fieldNameIndex('DIST') while (poly_iterator.nextFeature(poly_feature) and point_iterator.nextFeature(point_feature)): geom= poly_feature.geometry() if geom is not None: try: poly_point = geom.asPolygon()[0] centroid = geom.asPoint() except IndexError: continue distances = {} for i, point in enumerate(poly_point): end = poly_point[(i+1) % len(poly_point)] try: intersect = self.intersect_point_to_line(centroid, point, end) if intersect != centroid: dist = distance_area.measureLine(centroid, intersect) distances[intersect] = dist except ZeroDivisionError as InvalidMath: continue values = {field_index: min(distances.values())} point_provider.changeAttributeValues({point_feature.id(): values})
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()) 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}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) spatialIndex = QgsSpatialIndex(source, feedback) distance = QgsDistanceArea() distance.setSourceCrs(source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.project().ellipsoid()) sumDist = 0.00 A = source.sourceExtent() A = float(A.width() * A.height()) features = source.getFeatures() count = source.featureCount() total = 100.0 / count if count else 1 for current, feat in enumerate(features): if feedback.isCanceled(): break neighbourID = spatialIndex.nearestNeighbor( feat.geometry().asPoint(), 2)[1] request = QgsFeatureRequest().setFilterFid( neighbourID).setSubsetOfAttributes([]) neighbour = next(source.getFeatures(request)) sumDist += distance.measureLine(neighbour.geometry().asPoint(), feat.geometry().asPoint()) feedback.setProgress(int(current * total)) do = float(sumDist) / count de = float(0.5 / math.sqrt(count / A)) d = float(do / de) SE = float(0.26136 / math.sqrt(count**2 / A)) zscore = float((do - de) / SE) results = {} results[self.OBSERVED_MD] = do results[self.EXPECTED_MD] = de results[self.NN_INDEX] = d results[self.POINT_COUNT] = count results[self.Z_SCORE] = zscore if output_file: data = [] data.append('Observed mean distance: ' + str(do)) data.append('Expected mean distance: ' + str(de)) data.append('Nearest neighbour index: ' + str(d)) data.append('Number of points: ' + str(count)) data.append('Z-Score: ' + str(zscore)) self.createHTML(output_file, data) results[self.OUTPUT_HTML_FILE] = output_file return results
def draw_scalebar(self, composer_map, top_offset): """Add a numeric scale to the bottom left of the map. We draw the scale bar manually because QGIS does not yet support rendering a scale bar for a geographic map in km. .. seealso:: :meth:`drawNativeScaleBar` :param composer_map: Composer map on which to draw the scalebar. :type composer_map: QgsComposerMap :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ LOGGER.debug('InaSAFE Map drawScaleBar called') myCanvas = self.iface.mapCanvas() myRenderer = myCanvas.mapRenderer() # # Add a linear map scale # myDistanceArea = QgsDistanceArea() myDistanceArea.setSourceCrs(myRenderer.destinationCrs().srsid()) myDistanceArea.setEllipsoidalMode(True) # Determine how wide our map is in km/m # Starting point at BL corner myComposerExtent = composer_map.extent() myStartPoint = QgsPoint(myComposerExtent.xMinimum(), myComposerExtent.yMinimum()) # Ending point at BR corner myEndPoint = QgsPoint(myComposerExtent.xMaximum(), myComposerExtent.yMinimum()) myGroundDistance = myDistanceArea.measureLine(myStartPoint, myEndPoint) # Get the equivalent map distance per page mm myMapWidth = self.mapWidth # How far is 1mm on map on the ground in meters? myMMToGroundDistance = myGroundDistance / myMapWidth #print 'MM:', myMMDistance # How long we want the scale bar to be in relation to the map myScaleBarToMapRatio = 0.5 # How many divisions the scale bar should have myTickCount = 5 myScaleBarWidthMM = myMapWidth * myScaleBarToMapRatio myPrintSegmentWidthMM = myScaleBarWidthMM / myTickCount # Segment width in real world (m) # We apply some logic here so that segments are displayed in meters # if each segment is less that 1000m otherwise km. Also the segment # lengths are rounded down to human looking numbers e.g. 1km not 1.1km myUnits = '' myGroundSegmentWidth = myPrintSegmentWidthMM * myMMToGroundDistance if myGroundSegmentWidth < 1000: myUnits = 'm' myGroundSegmentWidth = round(myGroundSegmentWidth) # adjust the segment width now to account for rounding myPrintSegmentWidthMM = myGroundSegmentWidth / myMMToGroundDistance else: myUnits = 'km' # Segment with in real world (km) myGroundSegmentWidth = round(myGroundSegmentWidth / 1000) myPrintSegmentWidthMM = ((myGroundSegmentWidth * 1000) / myMMToGroundDistance) # Now adjust the scalebar width to account for rounding myScaleBarWidthMM = myTickCount * myPrintSegmentWidthMM #print "SBWMM:", myScaleBarWidthMM #print "SWMM:", myPrintSegmentWidthMM #print "SWM:", myGroundSegmentWidthM #print "SWKM:", myGroundSegmentWidthKM # start drawing in line segments myScaleBarHeight = 5 # mm myLineWidth = 0.3 # mm myInsetDistance = 7 # how much to inset the scalebar into the map by myScaleBarX = self.pageMargin + myInsetDistance myScaleBarY = ( top_offset + self.mapHeight - myInsetDistance - myScaleBarHeight) # mm # Draw an outer background box - shamelessly hardcoded buffer myRect = QgsComposerShape(myScaleBarX - 4, # left edge myScaleBarY - 3, # top edge myScaleBarWidthMM + 13, # right edge myScaleBarHeight + 6, # bottom edge self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setColor(QtGui.QColor(255, 255, 255)) myPen.setWidthF(myLineWidth) myRect.setPen(myPen) #myRect.setLineWidth(myLineWidth) myRect.setFrameEnabled(False) myBrush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) # workaround for missing setTransparentFill missing from python api myRect.setBrush(myBrush) self.composition.addItem(myRect) # Set up the tick label font myFontWeight = QtGui.QFont.Normal myFontSize = 6 myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) # Draw the bottom line myUpshift = 0.3 # shift the bottom line up for better rendering myRect = QgsComposerShape(myScaleBarX, myScaleBarY + myScaleBarHeight - myUpshift, myScaleBarWidthMM, 0.1, self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setColor(QtGui.QColor(255, 255, 255)) myPen.setWidthF(myLineWidth) myRect.setPen(myPen) #myRect.setLineWidth(myLineWidth) myRect.setFrameEnabled(False) self.composition.addItem(myRect) # Now draw the scalebar ticks for myTickCountIterator in range(0, myTickCount + 1): myDistanceSuffix = '' if myTickCountIterator == myTickCount: myDistanceSuffix = ' ' + myUnits myRealWorldDistance = ('%.0f%s' % (myTickCountIterator * myGroundSegmentWidth, myDistanceSuffix)) #print 'RW:', myRealWorldDistance myMMOffset = myScaleBarX + (myTickCountIterator * myPrintSegmentWidthMM) #print 'MM:', myMMOffset myTickHeight = myScaleBarHeight / 2 # Lines are not exposed by the api yet so we # bodge drawing lines using rectangles with 1px height or width myTickWidth = 0.1 # width or rectangle to be drawn myUpTickLine = QgsComposerShape( myMMOffset, myScaleBarY + myScaleBarHeight - myTickHeight, myTickWidth, myTickHeight, self.composition) myUpTickLine.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setWidthF(myLineWidth) myUpTickLine.setPen(myPen) #myUpTickLine.setLineWidth(myLineWidth) myUpTickLine.setFrameEnabled(False) self.composition.addItem(myUpTickLine) # # Add a tick label # myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(myRealWorldDistance) myLabel.adjustSizeToText() myLabel.setItemPosition( myMMOffset - 3, myScaleBarY - myTickHeight) myLabel.setFrameEnabled(self.showFramesFlag) self.composition.addItem(myLabel)
class PositionMarker(QgsMapCanvasItem): ''' MapCanvasItem for showing the MobileItem on the MapCanvas Can have different appearences, fixed ones like corss, x or box or a userdefined shape. Can display also a label on the canvas ''' MIN_SIZE = 30 CIRLCE_SIZE = 30 def __init__(self, canvas, params={}): ''' Constructor :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface :param params: A dictionary defining all the properties of the position marker :type params: dictionary ''' self.canvas = canvas self.type = params.get('type', 'BOX').upper() self.size = int(params.get('size', 16)) self.showLabel = bool(params.get('showLabel', True)) s = (self.size - 1) / 2 self.length = float(params.get('length', 98.0)) self.width = float(params.get('width', 17.0)) self.offsetX = float(params.get('offsetX', 0.0)) self.offsetY = float(params.get('offsetY', 0.0)) self.shape = params.get('shape', ((0.0, -0.5), (0.5, -0.3), (0.5, 0.5), (-0.5, 0.50), (-0.5, -0.3))) self.paintShape = QPolygonF( [QPointF(-s, -s), QPointF(s, -s), QPointF(s, s), QPointF(-s, s)]) self.color = self.getColor(params.get('color', 'black')) self.fillColor = self.getColor(params.get('fillColor', 'lime')) self.defaultIcon = bool(params.get('defaultIcon', True)) self.defaultIconFilled = bool(params.get('defaultIconFilled', False)) self.paintCircle = False self.penWidth = int(params.get('penWidth', 1)) spw = s + self.penWidth + 1 self.bounding = QRectF(-spw, -spw, spw * 2, spw * 2) if self.type in ('CROSS', 'X'): self.penWidth = 5 self.trackLen = int(params.get('trackLength', 100)) self.trackColor = self.getColor( params.get('trackColor', self.fillColor)) self.track = deque() self.position = None self.heading = 0 self.northAlign = 0.0 super(PositionMarker, self).__init__(canvas) self.setZValue(int(params.get('zValue', 100))) self.distArea = QgsDistanceArea() self.distArea.setEllipsoid(u'WGS84') if self.showLabel: self.label = MarkerLabel(self.canvas, params) self.label.setZValue(self.zValue() + 0.1) self.updateSize() def properties(self): return { 'type': self.type, 'size': self.size, 'length': self.length, 'width': self.width, 'defaultIcon': self.defaultIcon, 'defaultIconFilled': self.defaultIconFilled, 'offsetX': self.offsetX, 'offsetY': self.offsetY, 'shape': self.shape, 'color': self.color.rgba(), 'fillColor': self.fillColor.rgba(), 'penWidth': self.penWidth, 'trackLength': self.trackLen, 'trackColor': self.trackColor.rgba(), 'zValue': self.zValue(), 'showLabel': self.showLabel } def setMapPosition(self, pos): if self.position != pos: self.updateTrack() self.position = pos self.setPos(self.toCanvasCoordinates(self.position)) if self.showLabel: self.label.setMapPosition(pos) self.update() def newHeading(self, heading): if self.heading != heading: self.heading = heading self.setRotation(self.canvas.rotation() + self.heading - self.northAlign) self.update() def resetPosition(self): self.position = None if self.showLabel: self.label.resetPosition() def updatePosition(self): if self.position: self.prepareGeometryChange() self.updateSize() self.setPos(self.toCanvasCoordinates(self.position)) self.setRotation(self.canvas.rotation() + self.heading - self.northAlign) def updateMapMagnification(self): self.updatePosition() if self.showLabel: self.label.updatePosition() for tp in self.track: tp[0].updatePosition() def updateSize(self): if self.type != 'SHAPE': return s = self.canvas.mapSettings() self.distArea.setSourceCrs(s.destinationCrs(), QgsProject.instance().transformContext()) try: if self.position: p1 = self.position p = self.toCanvasCoordinates(self.position) p2 = self.toMapCoordinates(QPoint(p.x(), p.y() + 100.0)) else: p = self.canvas.viewport().rect().center() p1 = self.toMapCoordinates(p) p2 = self.toMapCoordinates(QPoint(p.x(), p.y() + 100)) lngth = self.distArea.measureLine(p1, p2) f = 100.0 / lngth self.northAlign = fmod( self.distArea.bearing(p2, p1) * 180.0 / pi + self.canvas.rotation(), 360.0) except Exception: f = s.outputDpi() / 0.0254 / s.scale() paintLength = max(self.length * f, self.MIN_SIZE) paintWidth = paintLength * self.width / self.length offsY = self.offsetX / self.length * paintLength offsX = self.offsetY / self.width * paintWidth self.paintShape.clear() if (self.length * f) < self.MIN_SIZE and self.defaultIcon: self.paintCircle = True self.bounding = QRectF(-self.CIRLCE_SIZE * 0.5, -self.CIRLCE_SIZE - 2, self.CIRLCE_SIZE, self.CIRLCE_SIZE * 1.5) else: self.paintCircle = False for v in self.shape: self.paintShape << QPointF(v[0] * paintWidth - offsX, v[1] * paintLength + offsY) self.bounding = self.paintShape.boundingRect() self.size = max(paintLength, paintWidth) def newTrackPoint(self, pos): tp = QgsVertexMarker(self.canvas) tp.setCenter(pos) tp.setIconType(QgsVertexMarker.ICON_CROSS) tp.setColor(self.trackColor) tp.setZValue(self.zValue() - 0.1) tp.setIconSize(3) tp.setPenWidth(3) return tp def updateTrack(self): if self.position and self.trackLen: if len(self.track) >= self.trackLen: tpr = self.track.popleft() self.canvas.scene().removeItem(tpr[0]) del (tpr) tp = self.newTrackPoint(self.position) self.track.append((tp, self.position)) def setVisible(self, visible): for tp in self.track: tp[0].setVisible(visible) QgsMapCanvasItem.setVisible(self, visible) if self.showLabel: self.label.setVisible(visible) def deleteTrack(self): for tp in self.track: self.canvas.scene().removeItem(tp[0]) self.track.clear() def setTrack(self, track): self.track.clear() for tp in track: tpn = self.newTrackPoint(tp) self.track.append((tpn, tp)) def paint(self, painter, xxx, xxx2): if not self.position: return s = (self.size - 1) / 2 pen = QPen(self.color) pen.setWidth(self.penWidth) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing, True) if self.type == 'CROSS': painter.drawLine(QLineF(-s, 0, s, 0)) painter.drawLine(QLineF(0, -s, 0, s)) elif self.type == 'X': painter.drawLine(QLineF(-s, -s, s, s)) painter.drawLine(QLineF(-s, s, s, -s)) elif self.type == 'BOX': brush = QBrush(self.fillColor) painter.setBrush(brush) painter.drawConvexPolygon(self.paintShape) elif self.type == 'SHAPE': if self.paintCircle: pen.setWidth(self.penWidth * 2) if self.defaultIconFilled: brush = QBrush(self.fillColor) painter.setBrush(brush) painter.setPen(pen) painter.drawEllipse(QPointF(0, 0), self.CIRLCE_SIZE * 0.4, self.CIRLCE_SIZE * 0.4) painter.drawLine( QLineF(0, -self.CIRLCE_SIZE * 0.4, 0, -self.CIRLCE_SIZE)) else: brush = QBrush(self.fillColor) painter.setBrush(brush) painter.drawConvexPolygon(self.paintShape) def boundingRect(self): return self.bounding def getColor(self, value): try: return QColor.fromRgba(int(value)) except ValueError: return QColor(value) def removeFromCanvas(self): self.deleteTrack() if self.showLabel: self.canvas.scene().removeItem(self.label) self.canvas.scene().removeItem(self)
def processAlgorithm(self, progress): layerPoints = dataobjects.getObjectFromUri(self.getParameterValue(self.POINTS)) layerHubs = dataobjects.getObjectFromUri(self.getParameterValue(self.HUBS)) fieldName = self.getParameterValue(self.FIELD) addLines = self.getParameterValue(self.GEOMETRY) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException(self.tr("Same layer given for both hubs and spokes")) fields = layerPoints.fields() fields.append(QgsField("HubName", QVariant.String)) fields.append(QgsField("HubDist", QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Point, layerPoints.crs()) index = vector.spatialindex(layerHubs) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs().srsid()) distance.setEllipsoidalMode(True) # Scan source points, find nearest hub, and write to output file features = vector.features(layerPoints) total = 100.0 / len(features) for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next( layerHubs.getFeatures( QgsFeatureRequest() .setFilterFid(neighbors[0]) .setSubsetOfAttributes([fieldName], layerHubs.fields()) ) ) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == "Feet": attributes.append(hubDist * 3.2808399) elif units == "Miles": attributes.append(hubDist * 0.000621371192) elif units == "Kilometers": attributes.append(hubDist / 1000.0) elif units != "Meters": attributes.append(sqrt(pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPoint(src)) writer.addFeature(feat) progress.setPercentage(int(current * total)) del writer
class ExportGeometryInfo(QgisAlgorithm): INPUT = 'INPUT' METHOD = 'CALC_METHOD' OUTPUT = 'OUTPUT' def icon(self): return QgsApplication.getThemeIcon("/algorithms/mAlgorithmAddGeometryAttributes.svg") def svgIconPath(self): return QgsApplication.iconPath("/algorithms/mAlgorithmAddGeometryAttributes.svg") def tags(self): return self.tr('export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons,sinuosity,fields').split(',') def group(self): return self.tr('Vector geometry') def groupId(self): return 'vectorgeometry' def __init__(self): super().__init__() self.export_z = False self.export_m = False self.distance_area = None self.calc_methods = [self.tr('Layer CRS'), self.tr('Project CRS'), self.tr('Ellipsoidal')] def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterEnum(self.METHOD, self.tr('Calculate using'), options=self.calc_methods, defaultValue=0)) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Added geom info'))) def name(self): return 'exportaddgeometrycolumns' def displayName(self): return self.tr('Add geometry attributes') def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) method = self.parameterAsEnum(parameters, self.METHOD, context) wkb_type = source.wkbType() fields = source.fields() new_fields = QgsFields() if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry: new_fields.append(QgsField('area', QVariant.Double)) new_fields.append(QgsField('perimeter', QVariant.Double)) elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry: new_fields.append(QgsField('length', QVariant.Double)) if not QgsWkbTypes.isMultiType(source.wkbType()): new_fields.append(QgsField('straightdis', QVariant.Double)) new_fields.append(QgsField('sinuosity', QVariant.Double)) else: new_fields.append(QgsField('xcoord', QVariant.Double)) new_fields.append(QgsField('ycoord', QVariant.Double)) if QgsWkbTypes.hasZ(source.wkbType()): self.export_z = True new_fields.append(QgsField('zcoord', QVariant.Double)) if QgsWkbTypes.hasM(source.wkbType()): self.export_m = True new_fields.append(QgsField('mvalue', QVariant.Double)) fields = QgsProcessingUtils.combineFields(fields, new_fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, wkb_type, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) coordTransform = None # Calculate with: # 0 - layer CRS # 1 - project CRS # 2 - ellipsoidal self.distance_area = QgsDistanceArea() if method == 2: self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext()) self.distance_area.setEllipsoid(context.project().ellipsoid()) elif method == 1: coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project()) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break outFeat = f attrs = f.attributes() inGeom = f.geometry() if inGeom: if coordTransform is not None: inGeom.transform(coordTransform) if inGeom.type() == QgsWkbTypes.PointGeometry: attrs.extend(self.point_attributes(inGeom)) elif inGeom.type() == QgsWkbTypes.PolygonGeometry: attrs.extend(self.polygon_attributes(inGeom)) else: attrs.extend(self.line_attributes(inGeom)) # ensure consistent count of attributes - otherwise null # geometry features will have incorrect attribute length # and provider may reject them if len(attrs) < len(fields): attrs += [NULL] * (len(fields) - len(attrs)) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id} def point_attributes(self, geometry): pt = None if not geometry.isMultipart(): pt = geometry.constGet() else: if geometry.numGeometries() > 0: pt = geometry.geometryN(0) attrs = [] if pt: attrs.append(pt.x()) attrs.append(pt.y()) # add point z/m if self.export_z: attrs.append(pt.z()) if self.export_m: attrs.append(pt.m()) return attrs def line_attributes(self, geometry): if geometry.isMultipart(): return [self.distance_area.measureLength(geometry)] else: curve = geometry.constGet() p1 = curve.startPoint() p2 = curve.endPoint() straight_distance = self.distance_area.measureLine(QgsPointXY(p1), QgsPointXY(p2)) sinuosity = curve.sinuosity() if math.isnan(sinuosity): sinuosity = NULL return [self.distance_area.measureLength(geometry), straight_distance, sinuosity] def polygon_attributes(self, geometry): area = self.distance_area.measureArea(geometry) perimeter = self.distance_area.measurePerimeter(geometry) return [area, perimeter]
def testLengthMeasureAndUnits(self): """Test a variety of length measurements in different CRS and ellipsoid modes, to check that the calculated lengths and units are always consistent """ da = QgsDistanceArea() da.setSourceCrs(3452) da.setEllipsoidalMode(False) da.setEllipsoid("NONE") daCRS = QgsCoordinateReferenceSystem() daCRS.createFromSrsId(da.sourceCrs()) # We check both the measured length AND the units, in case the logic regarding # ellipsoids and units changes in future distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) assert ((abs(distance - 2.23606797) < 0.00000001 and units == QGis.Degrees) or (abs(distance - 248.52) < 0.01 and units == QGis.Meters)) da.setEllipsoid("WGS84") distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) assert ((abs(distance - 2.23606797) < 0.00000001 and units == QGis.Degrees) or (abs(distance - 248.52) < 0.01 and units == QGis.Meters)) da.setEllipsoidalMode(True) distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) # should always be in Meters self.assertAlmostEqual(distance, 247555.57, delta=0.01) self.assertEqual(units, QGis.Meters) # test converting the resultant length distance = da.convertLengthMeasurement(distance, QGis.NauticalMiles) self.assertAlmostEqual(distance, 133.669, delta=0.01) # now try with a source CRS which is in feet da.setSourceCrs(27469) da.setEllipsoidalMode(False) # measurement should be in feet distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) self.assertAlmostEqual(distance, 2.23606797, delta=0.000001) self.assertEqual(units, QGis.Feet) # test converting the resultant length distance = da.convertLengthMeasurement(distance, QGis.Meters) self.assertAlmostEqual(distance, 0.6815, delta=0.001) da.setEllipsoidalMode(True) # now should be in Meters again distance = da.measureLine(QgsPoint(1, 1), QgsPoint(2, 3)) units = da.lengthUnits() print "measured {} in {}".format(distance, QgsUnitTypes.toString(units)) self.assertAlmostEqual(distance, 0.67953772, delta=0.000001) self.assertEqual(units, QGis.Meters) # test converting the resultant length distance = da.convertLengthMeasurement(distance, QGis.Feet) self.assertAlmostEqual(distance, 2.2294, delta=0.001)
def processAlgorithm(self, progress): layerPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.POINTS)) layerHubs = dataobjects.getObjectFromUri( self.getParameterValue(self.HUBS)) fieldName = self.getParameterValue(self.FIELD) addLines = self.getParameterValue(self.GEOMETRY) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) geomType = QGis.WKBPoint if addLines: geomType = QGis.WKBLineString fields = layerPoints.pendingFields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, geomType, layerPoints.crs()) index = vector.spatialindex(layerHubs) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs().srsid()) distance.setEllipsoidalMode(True) project_ellipsoid = QgsProject.instance().readEntry('Measure', '/Ellipsoid', 'NONE')[0] distance.setEllipsoid(project_ellipsoid) # Scan source points, find nearest hub, and write to output file features = vector.features(layerPoints) total = 100.0 / len(features) if len(features) > 0 else 1 for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = layerHubs.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0])).next() closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append(sqrt( pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) if geomType == QGis.WKBPoint: feat.setGeometry(QgsGeometry.fromPoint(src)) else: feat.setGeometry(QgsGeometry.fromPolyline([src, closest])) writer.addFeature(feat) progress.setPercentage(int(current * total)) del writer
def linearMatrix(self, parameters, context, source, inField, target_source, targetField, same_source_and_target, matType, nPoints, feedback): if same_source_and_target: # need to fetch an extra point from the index, since the closest match will always be the same # as the input feature nPoints += 1 inIdx = source.fields().lookupField(inField) outIdx = target_source.fields().lookupField(targetField) fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('InputID') fields.append(input_id_field) if matType == 0: target_id_field = target_source.fields()[outIdx] target_id_field.setName('TargetID') fields.append(target_id_field) fields.append(QgsField('Distance', QVariant.Double)) else: fields.append(QgsField('MEAN', QVariant.Double)) fields.append(QgsField('STDDEV', QVariant.Double)) fields.append(QgsField('MIN', QVariant.Double)) fields.append(QgsField('MAX', QVariant.Double)) out_wkb = QgsWkbTypes.multiType( source.wkbType()) if matType == 0 else source.wkbType() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, out_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex( target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() inID = str(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 request = QgsFeatureRequest().setFilterFids( featList).setSubsetOfAttributes([outIdx]).setDestinationCrs( source.sourceCrs(), context.transformContext()) for outFeat in target_source.getFeatures(request): if feedback.isCanceled(): break if same_source_and_target and inFeat.id() == outFeat.id(): continue outID = outFeat.attributes()[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: out_feature = QgsFeature() out_geom = QgsGeometry.unaryUnion( [inFeat.geometry(), outFeat.geometry()]) out_feature.setGeometry(out_geom) out_feature.setAttributes([inID, outID, dist]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) out_feature = QgsFeature() out_feature.setGeometry(inFeat.geometry()) out_feature.setAttributes( [inID, mean, vari, min(distList), max(distList)]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, feedback): layer = dataobjects.getLayerFromString( self.getParameterValue(self.VECTOR)) pointCount = float(self.getParameterValue(self.POINT_NUMBER)) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, layer.crs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = layer.featureCount() total = 100.0 / pointCount index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: # pick random feature fid = random.randint(0, featureCount - 1) f = next( layer.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 pnt = QgsPoint(rx, ry) geom = QgsGeometry.fromPoint(pnt) if vector.checkMinDistance(pnt, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) writer.addFeature(f) index.insertFeature(f) points[nPoints] = pnt nPoints += 1 feedback.setProgress(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: ProcessingLog.addToLog( ProcessingLog.LOG_INFO, self.tr('Can not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) del writer
def processAlgorithm(self, progress): layerPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.POINTS)) layerHubs = dataobjects.getObjectFromUri( self.getParameterValue(self.HUBS)) fieldName = self.getParameterValue(self.FIELD) addLines = self.getParameterValue(self.GEOMETRY) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) fields = layerPoints.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.LineString, layerPoints.crs()) index = vector.spatialindex(layerHubs) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs().srsid()) distance.setEllipsoidalMode(True) # Scan source points, find nearest hub, and write to output file features = vector.features(layerPoints) total = 100.0 / len(features) for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next( layerHubs.getFeatures(QgsFeatureRequest().setFilterFid( neighbors[0]).setSubsetOfAttributes([fieldName], layerHubs.fields()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append( sqrt( pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPolyline([src, closest])) writer.addFeature(feat) progress.setPercentage(int(current * total)) del writer
def processAlgorithm(self, parameters, context, feedback): if parameters[self.INPUT] == parameters[self.HUBS]: raise QgsProcessingException( self.tr('Same layer given for both hubs and spokes')) point_source = self.parameterAsSource(parameters, self.INPUT, context) if point_source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) hub_source = self.parameterAsSource(parameters, self.HUBS, context) if hub_source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.HUBS)) fieldName = self.parameterAsString(parameters, self.FIELD, context) units = self.UNITS[self.parameterAsEnum(parameters, self.UNIT, context)] fields = point_source.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, point_source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex(hub_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) distance = QgsDistanceArea() distance.setSourceCrs(point_source.sourceCrs(), context.transformContext()) distance.setEllipsoid(context.project().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = point_source.getFeatures() total = 100.0 / point_source.featureCount() if point_source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): sink.addFeature(f, QgsFeatureSink.FastInsert) continue src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next(hub_source.getFeatures(QgsFeatureRequest().setFilterFid(neighbors[0]).setSubsetOfAttributes([fieldName], hub_source.fields()).setDestinationCrs(point_source.sourceCrs(), context.transformContext()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) if units != self.LAYER_UNITS: hub_dist_in_desired_units = distance.convertLengthMeasurement(hubDist, units) else: hub_dist_in_desired_units = hubDist attributes = f.attributes() attributes.append(ft[fieldName]) attributes.append(hub_dist_in_desired_units) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPointXY(src)) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * 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)) 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()) 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}
class ExportGeometryInfo(QgisAlgorithm): INPUT = 'INPUT' METHOD = 'CALC_METHOD' OUTPUT = 'OUTPUT' def icon(self): return QgsApplication.getThemeIcon( "/algorithms/mAlgorithmAddGeometryAttributes.svg") def svgIconPath(self): return QgsApplication.iconPath( "/algorithms/mAlgorithmAddGeometryAttributes.svg") def tags(self): return self.tr( 'export,add,information,measurements,areas,lengths,perimeters,latitudes,longitudes,x,y,z,extract,points,lines,polygons,sinuosity,fields' ).split(',') def group(self): return self.tr('Vector geometry') def groupId(self): return 'vectorgeometry' def __init__(self): super().__init__() self.export_z = False self.export_m = False self.distance_area = None self.calc_methods = [ self.tr('Layer CRS'), self.tr('Project CRS'), self.tr('Ellipsoidal') ] def initAlgorithm(self, config=None): self.addParameter( QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'))) self.addParameter( QgsProcessingParameterEnum(self.METHOD, self.tr('Calculate using'), options=self.calc_methods, defaultValue=0)) self.addParameter( QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Added geom info'))) def name(self): return 'exportaddgeometrycolumns' def displayName(self): return self.tr('Add geometry attributes') def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) method = self.parameterAsEnum(parameters, self.METHOD, context) wkb_type = source.wkbType() fields = source.fields() new_fields = QgsFields() if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry: new_fields.append(QgsField('area', QVariant.Double)) new_fields.append(QgsField('perimeter', QVariant.Double)) elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry: new_fields.append(QgsField('length', QVariant.Double)) if not QgsWkbTypes.isMultiType(source.wkbType()): new_fields.append(QgsField('straightdis', QVariant.Double)) new_fields.append(QgsField('sinuosity', QVariant.Double)) else: new_fields.append(QgsField('xcoord', QVariant.Double)) new_fields.append(QgsField('ycoord', QVariant.Double)) if QgsWkbTypes.hasZ(source.wkbType()): self.export_z = True new_fields.append(QgsField('zcoord', QVariant.Double)) if QgsWkbTypes.hasM(source.wkbType()): self.export_m = True new_fields.append(QgsField('mvalue', QVariant.Double)) fields = QgsProcessingUtils.combineFields(fields, new_fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, wkb_type, source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) coordTransform = None # Calculate with: # 0 - layer CRS # 1 - project CRS # 2 - ellipsoidal self.distance_area = QgsDistanceArea() if method == 2: self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext()) self.distance_area.setEllipsoid(context.project().ellipsoid()) elif method == 1: coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project()) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break outFeat = f attrs = f.attributes() inGeom = f.geometry() if inGeom: if coordTransform is not None: inGeom.transform(coordTransform) if inGeom.type() == QgsWkbTypes.PointGeometry: attrs.extend(self.point_attributes(inGeom)) elif inGeom.type() == QgsWkbTypes.PolygonGeometry: attrs.extend(self.polygon_attributes(inGeom)) else: attrs.extend(self.line_attributes(inGeom)) # ensure consistent count of attributes - otherwise null # geometry features will have incorrect attribute length # and provider may reject them if len(attrs) < len(fields): attrs += [NULL] * (len(fields) - len(attrs)) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id} def point_attributes(self, geometry): pt = None if not geometry.isMultipart(): pt = geometry.constGet() else: if geometry.numGeometries() > 0: pt = geometry.geometryN(0) attrs = [] if pt: attrs.append(pt.x()) attrs.append(pt.y()) # add point z/m if self.export_z: attrs.append(pt.z()) if self.export_m: attrs.append(pt.m()) return attrs def line_attributes(self, geometry): if geometry.isMultipart(): return [self.distance_area.measureLength(geometry)] else: curve = geometry.constGet() p1 = curve.startPoint() p2 = curve.endPoint() straight_distance = self.distance_area.measureLine( QgsPointXY(p1), QgsPointXY(p2)) sinuosity = curve.sinuosity() if math.isnan(sinuosity): sinuosity = NULL return [ self.distance_area.measureLength(geometry), straight_distance, sinuosity ] def polygon_attributes(self, geometry): area = self.distance_area.measureArea(geometry) perimeter = self.distance_area.measurePerimeter(geometry) return [area, perimeter]
def regularMatrix(self, parameters, context, source, inField, target_source, targetField, nPoints, feedback): distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) inIdx = source.fields().lookupField(inField) targetIdx = target_source.fields().lookupField(targetField) index = QgsSpatialIndex( target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) first = True sink = None dest_id = None features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() if first: featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) first = False fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('ID') fields.append(input_id_field) for f in target_source.getFeatures( QgsFeatureRequest().setFilterFids( featList).setSubsetOfAttributes([ targetIdx ]).setDestinationCrs(source.sourceCrs(), context.transformContext())): fields.append( QgsField(str(f[targetField]), QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) data = [inFeat[inField]] for target in target_source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( []).setFilterFids(featList).setDestinationCrs( source.sourceCrs(), context.transformContext())): if feedback.isCanceled(): break outGeom = target.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) data.append(dist) out_feature = QgsFeature() out_feature.setGeometry(inGeom) out_feature.setAttributes(data) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.VECTOR)) groupField = self.getParameterValue(self.GROUP_FIELD) orderField = self.getParameterValue(self.ORDER_FIELD) dateFormat = str(self.getParameterValue(self.DATE_FORMAT)) #gap = int(self.getParameterValue(self.GAP_PERIOD)) dirName = self.getOutputValue(self.OUTPUT_TEXT) fields = QgsFields() fields.append(QgsField('group', QVariant.String, '', 254, 0)) fields.append(QgsField('begin', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) writer = self.getOutputFromName(self.OUTPUT_LINES).getVectorWriter( fields, QgsWkbTypes.LineString, layer.crs()) points = dict() features = vector.features(layer) total = 100.0 / len(features) for current, f in enumerate(features): point = f.geometry().asPoint() group = f[groupField] order = f[orderField] if dateFormat != '': order = datetime.strptime(str(order), dateFormat) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] progress.setPercentage(int(current * total)) progress.setPercentage(0) da = QgsDistanceArea() current = 0 total = 100.0 / len(points) for group, vertices in list(points.items()): vertices.sort() f = QgsFeature() f.initAttributes(len(fields)) f.setFields(fields) f['group'] = group f['begin'] = vertices[0][0] f['end'] = vertices[-1][0] fileName = os.path.join(dirName, '%s.txt' % group) fl = open(fileName, 'w') fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') line = [] i = 0 for node in vertices: line.append(node[1]) if i == 0: fl.write('startAt=%f;%f;90\n' % (node[1].x(), node[1].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(line[i - 1], line[i]) fl.write('%f;%f;90\n' % (angle, distance)) i += 1 f.setGeometry(QgsGeometry.fromPolyline(line)) writer.addFeature(f) current += 1 progress.setPercentage(int(current * total)) del writer fl.close()
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 drawScaleBar(self, theComposerMap, theTopOffset): """Add a numeric scale to the bottom left of the map We draw the scale bar manually because QGIS does not yet support rendering a scalebar for a geographic map in km. .. seealso:: :meth:`drawNativeScaleBar` Args: * theComposerMap - QgsComposerMap instance used as the basis scale calculations. * theTopOffset - vertical offset at which the map should be drawn Returns: None Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map drawScaleBar called') myCanvas = self.iface.mapCanvas() myRenderer = myCanvas.mapRenderer() # # Add a linear map scale # myDistanceArea = QgsDistanceArea() myDistanceArea.setSourceCrs(myRenderer.destinationCrs().srsid()) myDistanceArea.setProjectionsEnabled(True) # Determine how wide our map is in km/m # Starting point at BL corner myComposerExtent = theComposerMap.extent() myStartPoint = QgsPoint(myComposerExtent.xMinimum(), myComposerExtent.yMinimum()) # Ending point at BR corner myEndPoint = QgsPoint(myComposerExtent.xMaximum(), myComposerExtent.yMinimum()) myGroundDistance = myDistanceArea.measureLine(myStartPoint, myEndPoint) # Get the equivalent map distance per page mm myMapWidth = self.mapWidth # How far is 1mm on map on the ground in meters? myMMToGroundDistance = myGroundDistance / myMapWidth #print 'MM:', myMMDistance # How long we want the scale bar to be in relation to the map myScaleBarToMapRatio = 0.5 # How many divisions the scale bar should have myTickCount = 5 myScaleBarWidthMM = myMapWidth * myScaleBarToMapRatio myPrintSegmentWidthMM = myScaleBarWidthMM / myTickCount # Segment width in real world (m) # We apply some logic here so that segments are displayed in meters # if each segment is less that 1000m otherwise km. Also the segment # lengths are rounded down to human looking numbers e.g. 1km not 1.1km myUnits = '' myGroundSegmentWidth = myPrintSegmentWidthMM * myMMToGroundDistance if myGroundSegmentWidth < 1000: myUnits = 'm' myGroundSegmentWidth = round(myGroundSegmentWidth) # adjust the segment width now to account for rounding myPrintSegmentWidthMM = myGroundSegmentWidth / myMMToGroundDistance else: myUnits = 'km' # Segment with in real world (km) myGroundSegmentWidth = round(myGroundSegmentWidth / 1000) myPrintSegmentWidthMM = ((myGroundSegmentWidth * 1000) / myMMToGroundDistance) # Now adjust the scalebar width to account for rounding myScaleBarWidthMM = myTickCount * myPrintSegmentWidthMM #print "SBWMM:", myScaleBarWidthMM #print "SWMM:", myPrintSegmentWidthMM #print "SWM:", myGroundSegmentWidthM #print "SWKM:", myGroundSegmentWidthKM # start drawing in line segments myScaleBarHeight = 5 # mm myLineWidth = 0.3 # mm myInsetDistance = 7 # how much to inset the scalebar into the map by myScaleBarX = self.pageMargin + myInsetDistance myScaleBarY = (theTopOffset + self.mapHeight - myInsetDistance - myScaleBarHeight) # mm # Draw an outer background box - shamelessly hardcoded buffer myRect = QgsComposerShape( myScaleBarX - 4, # left edge myScaleBarY - 3, # top edge myScaleBarWidthMM + 13, # right edge myScaleBarHeight + 6, # bottom edge self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myRect.setLineWidth(myLineWidth) myRect.setFrame(False) myBrush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) # workaround for missing setTransparentFill missing from python api myRect.setBrush(myBrush) self.composition.addItem(myRect) # Set up the tick label font myFontWeight = QtGui.QFont.Normal myFontSize = 6 myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) # Draw the bottom line myUpshift = 0.3 # shift the bottom line up for better rendering myRect = QgsComposerShape(myScaleBarX, myScaleBarY + myScaleBarHeight - myUpshift, myScaleBarWidthMM, 0.1, self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myRect.setLineWidth(myLineWidth) myRect.setFrame(False) self.composition.addItem(myRect) # Now draw the scalebar ticks for myTickCountIterator in range(0, myTickCount + 1): myDistanceSuffix = '' if myTickCountIterator == myTickCount: myDistanceSuffix = ' ' + myUnits myRealWorldDistance = ( '%.0f%s' % (myTickCountIterator * myGroundSegmentWidth, myDistanceSuffix)) #print 'RW:', myRealWorldDistance myMMOffset = myScaleBarX + (myTickCountIterator * myPrintSegmentWidthMM) #print 'MM:', myMMOffset myTickHeight = myScaleBarHeight / 2 # Lines are not exposed by the api yet so we # bodge drawing lines using rectangles with 1px height or width myTickWidth = 0.1 # width or rectangle to be drawn myUpTickLine = QgsComposerShape( myMMOffset, myScaleBarY + myScaleBarHeight - myTickHeight, myTickWidth, myTickHeight, self.composition) myUpTickLine.setShapeType(QgsComposerShape.Rectangle) myUpTickLine.setLineWidth(myLineWidth) myUpTickLine.setFrame(False) self.composition.addItem(myUpTickLine) # # Add a tick label # myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(myRealWorldDistance) myLabel.adjustSizeToText() myLabel.setItemPosition(myMMOffset - 3, myScaleBarY - myTickHeight) myLabel.setFrame(self.showFramesFlag) self.composition.addItem(myLabel)
def processAlgorithm(self, progress): layerPoints = dataobjects.getObjectFromUri( self.getParameterValue(self.POINTS)) layerHubs = dataobjects.getObjectFromUri( self.getParameterValue(self.HUBS)) fieldName = self.getParameterValue(self.FIELD) addLines = self.getParameterValue(self.GEOMETRY) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) geomType = QGis.WKBPoint if addLines: geomType = QGis.WKBLineString fields = layerPoints.pendingFields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, geomType, layerPoints.crs()) # Create array of hubs in memory hubs = [] features = vector.features(layerHubs) for f in features: hubs.append( Hub(f.geometry().boundingBox().center(), unicode(f[fieldName]))) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs().srsid()) distance.setEllipsoidalMode(True) # Scan source points, find nearest hub, and write to output file features = vector.features(layerPoints) total = 100.0 / len(features) for current, f in enumerate(features): src = f.geometry().boundingBox().center() closest = hubs[0] hubDist = distance.measureLine(src, closest.point) for hub in hubs: dist = distance.measureLine(src, hub.point) if dist < hubDist: closest = hub hubDist = dist attributes = f.attributes() attributes.append(closest.name) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append( sqrt( pow(src.x() - closest.point.x(), 2.0) + pow(src.y() - closest.point.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) if geomType == QGis.WKBPoint: feat.setGeometry(QgsGeometry.fromPoint(src)) else: feat.setGeometry(QgsGeometry.fromPolyline([src, closest.point])) writer.addFeature(feat) progress.setPercentage(int(current * total)) del writer
def getDistance(self, geomOne, geomTwo): distance = QgsDistanceArea() distance.setEllipsoid('WGS84') m = round(distance.measureLine(geomOne, geomTwo), 2) return m
class PositionMarker(QgsMapCanvasItem): ''' classdocs ''' def __init__(self, canvas, params={}): ''' Constructor :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface :param params: A dictionary defining all the properties of the position marker :type params: dictionary ''' self.canvas = canvas self.type = params.get('type', 'BOX').upper() self.size = int(params.get('size', 16)) self.bounding = 1.414213562 * self.size self.length = float(params.get('length', 98.0)) self.width = float(params.get('width', 17.0)) self.shape = params.get('shape', ((0.0, -0.5), (0.5, -0.3), (0.5, 0.5), (-0.5, 0.50), (-0.5, -0.3))) s = (self.size - 1) / 2 self.paintShape = QPolygonF([QPointF(-s, -s), QPointF(s, -s), QPointF(s, s), QPointF(-s, s)]) self.color = self.getColor(params.get('color', 'black')) self.fillColor = self.getColor(params.get('fillColor', 'lime')) self.penWidth = int(params.get('penWidth', 1)) if self.type in ('CROSS', 'X'): self.penWidth = 5 self.trackLen = int(params.get('trackLength', 100)) self.trackColor = self.getColor(params.get('trackColor', self.fillColor)) self.track = deque() self.pos = None self.heading = 0 super(PositionMarker, self).__init__(canvas) self.setZValue(int(params.get('zValue', 100))) self.distArea = QgsDistanceArea() self.distArea.setEllipsoid(u'WGS84') self.distArea.setEllipsoidalMode(True) self.updateSize() def properties(self): return {'type': self.type, 'size': self.size, 'length': self.length, 'width': self.width, 'shape': self.shape, 'color': self.color.rgba(), 'fillColor': self.fillColor.rgba(), 'penWidth': self.penWidth, 'trackLength': self.trackLen, 'trackColor' : self.trackColor.rgba(), 'zValue': self.zValue()} def setMapPosition(self, pos): if self.pos != pos: self.updateTrack() self.pos = pos self.setPos(self.toCanvasCoordinates(self.pos)) self.update() def newHeading(self, heading): if self.heading != heading: self.heading = heading self.setRotation(self.canvas.rotation() + self.heading) self.update() def resetPosition(self): self.pos = None def updatePosition(self): if self.pos: self.prepareGeometryChange() self.updateSize() self.setPos(self.toCanvasCoordinates(self.pos)) self.setRotation(self.canvas.rotation() + self.heading) self.update() def updateSize(self): if self.type != 'SHAPE': return s = self.canvas.mapSettings() self.distArea.setSourceCrs(s.destinationCrs()) try: p1 = self.toMapCoordinates(QPoint(0, 0)) p2 = self.toMapCoordinates(QPoint(0, 100)) l = self.distArea.measureLine(p1, p2) f = 100 / l except: f = s.outputDpi() / 0.0254 / s.scale() paintLength = max(self.length * f, 50) paintWidth = paintLength * self.width / self.length self.paintShape.clear() for v in self.shape: self.paintShape << QPointF(v[0] * paintWidth, v[1] * paintLength) self.size = max(paintLength, paintWidth) self.bounding = sqrt(pow(paintLength, 2) + pow(paintLength, 2)) def updateTrack(self): if self.pos and self.trackLen: if len(self.track) >= self.trackLen: tpr = self.track.popleft() self.canvas.scene().removeItem(tpr) del(tpr) tp = QgsVertexMarker(self.canvas) tp.setCenter(self.pos) tp.setIconType(QgsVertexMarker.ICON_CROSS) tp.setColor(self.trackColor) tp.setZValue(self.zValue() - 0.1) tp.setIconSize(3) tp.setPenWidth(3) self.track.append(tp) def setVisible(self, visible): for tp in self.track: tp.setVisible(visible) QgsMapCanvasItem.setVisible(self, visible) def deleteTrack(self): for tp in self.track: self.canvas.scene().removeItem(tp) self.track.clear() def paint(self, painter, xxx, xxx2): if not self.pos: return s = (self.size - 1) / 2 pen = QPen(self.color) pen.setWidth(self.penWidth) painter.setPen(pen) if self.type == 'CROSS': painter.drawLine(QLineF(-s, 0, s, 0)) painter.drawLine(QLineF(0, -s, 0, s)) elif self.type == 'X': painter.drawLine(QLineF(-s, -s, s, s)) painter.drawLine(QLineF(-s, s, s, -s)) elif self.type == 'BOX': brush = QBrush(self.fillColor) painter.setBrush(brush) painter.drawConvexPolygon(self.paintShape) elif self.type == 'SHAPE': painter.setRenderHint(QPainter.Antialiasing, True) brush = QBrush(self.fillColor) painter.setBrush(brush) painter.drawConvexPolygon(self.paintShape) def boundingRect(self): s = self.bounding / 2 return QRectF(QPointF(-s, -s), QPointF(s, s)) def getColor(self, value): try: return QColor.fromRgba(int(value)) except ValueError: return QColor(value) def removeFromCanvas(self): self.deleteTrack() self.canvas.scene().removeItem(self)
def processAlgorithm(self, feedback): layer = dataobjects.getLayerFromString( self.getParameterValue(self.VECTOR)) groupField = self.getParameterValue(self.GROUP_FIELD) orderField = self.getParameterValue(self.ORDER_FIELD) dateFormat = str(self.getParameterValue(self.DATE_FORMAT)) #gap = int(self.getParameterValue(self.GAP_PERIOD)) dirName = self.getOutputValue(self.OUTPUT_TEXT) fields = QgsFields() fields.append(QgsField('group', QVariant.String, '', 254, 0)) fields.append(QgsField('begin', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) writer = self.getOutputFromName(self.OUTPUT_LINES).getVectorWriter( fields, QgsWkbTypes.LineString, layer.crs()) points = dict() features = vector.features(layer) total = 100.0 / len(features) for current, f in enumerate(features): point = f.geometry().asPoint() group = f[groupField] order = f[orderField] if dateFormat != '': order = datetime.strptime(str(order), dateFormat) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() current = 0 total = 100.0 / len(points) for group, vertices in list(points.items()): vertices.sort() f = QgsFeature() f.initAttributes(len(fields)) f.setFields(fields) f['group'] = group f['begin'] = vertices[0][0] f['end'] = vertices[-1][0] fileName = os.path.join(dirName, '%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') line = [] i = 0 for node in vertices: line.append(node[1]) if i == 0: fl.write('startAt=%f;%f;90\n' % (node[1].x(), node[1].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(line[i - 1], line[i]) fl.write('%f;%f;90\n' % (angle, distance)) i += 1 f.setGeometry(QgsGeometry.fromPolyline(line)) writer.addFeature(f) current += 1 feedback.setProgress(int(current * total)) del writer
def processAlgorithm(self, context, feedback): layerPoints = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.POINTS), context) layerHubs = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.HUBS), context) fieldName = self.getParameterValue(self.FIELD) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException( self.tr('Same layer given for both hubs and spokes')) fields = layerPoints.fields() fields.append(QgsField('HubName', QVariant.String)) fields.append(QgsField('HubDist', QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, layerPoints.crs(), context) index = QgsProcessingUtils.createSpatialIndex(layerHubs, context) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs()) distance.setEllipsoid(QgsProject.instance().ellipsoid()) # Scan source points, find nearest hub, and write to output file features = QgsProcessingUtils.getFeatures(layerPoints, context) total = 100.0 / QgsProcessingUtils.featureCount(layerPoints, context) for current, f in enumerate(features): src = f.geometry().boundingBox().center() neighbors = index.nearestNeighbor(src, 1) ft = next( layerHubs.getFeatures(QgsFeatureRequest().setFilterFid( neighbors[0]).setSubsetOfAttributes([fieldName], layerHubs.fields()))) closest = ft.geometry().boundingBox().center() hubDist = distance.measureLine(src, closest) attributes = f.attributes() attributes.append(ft[fieldName]) if units == 'Feet': attributes.append(hubDist * 3.2808399) elif units == 'Miles': attributes.append(hubDist * 0.000621371192) elif units == 'Kilometers': attributes.append(hubDist / 1000.0) elif units != 'Meters': attributes.append( sqrt( pow(src.x() - closest.x(), 2.0) + pow(src.y() - closest.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) feat.setGeometry(QgsGeometry.fromPoint(src)) writer.addFeature(feat) feedback.setProgress(int(current * total)) del writer
class MeasureDistanceTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs( crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): self.msglog.logMessage("") self.start_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point: self.end_point = point self.rubber_band.addPoint(self.end_point) self.rubber_band_points.addPoint(self.end_point) distance = self.distance_calc.measureLine( [self.start_point, self.end_point]) bearing = self.distance_calc.bearing(self.start_point, point) distancemsg = QMessageBox(self.parent()) distancemsg.finished.connect(self.deactivate) distancemsg.setWindowTitle("Measure tool") distancemsg.setText("Distance: {:.3F} m. Bearing: {:.3F} º".format( distance, degrees(bearing))) distancemsg.exec() self.finish() else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) distance = self.distance_calc.measureLine( [self.start_point, point]) bearing = self.distance_calc.bearing(self.start_point, point) self.msglog.logMessage("") self.msglog.logMessage( "Current distance: {:.3F} m. Bearing: {:.3F} º".format( distance, degrees(bearing)), "Measure distance:", 0) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
def point2nb(self): lst = [] # geoms = [geom.geometry() for geom in self.lyr.getFeatures() # feats = self.lyr.getFeatures() # for f1, f2 in itertools.product(feats, repeat=2): # if f1!=f2: # d = f1.geometry().asPoint().distance(f2.geometry().asPoint()) # self.plainTextEdit.insertPlainText("%s %s: %s\n" % (f1.id(), f2.id(), d)) featA = QgsFeature() featsA = self.lyr.getFeatures() trh = float(self.lineEdit.text()) if self.comboBox_6.currentText() == 'km': # psrid = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() prv = self.lyr.dataProvider() psrid = prv.crs().srsid() dist = QgsDistanceArea() dist.setEllipsoid('WGS84') dist.setEllipsoidalMode(True) # self.plainTextEdit.insertPlainText("%s\n" % psrid) if psrid != 3452: trafo = QgsCoordinateTransform(psrid, 3452) while featsA.nextFeature(featA): featB = QgsFeature() featsB = self.lyr.getFeatures() sor = [] while featsB.nextFeature(featB): if featA.id() != featB.id(): tav = dist.measureLine(trafo.transform(featA.geometry().asPoint()), trafo.transform(featB.geometry().asPoint())) # self.plainTextEdit.insertPlainText("%s %s %s\n" % (featA.id(), featB.id(), tav)) if (tav / 1000.0) <= trh: sor.append(featB.id()) lst.append(sor) else: while featsA.nextFeature(featA): featB = QgsFeature() featsB = self.lyr.getFeatures() sor = [] while featsB.nextFeature(featB): if featA.id() != featB.id(): tav = dist.measureLine(featA.geometry().asPoint(), featB.geometry().asPoint()) # self.plainTextEdit.insertPlainText("%s %s %s\n" % (featA.id(), featB.id(), tav)) if (tav / 1000.0) <= trh: sor.append(featB.id()) lst.append(sor) else: while featsA.nextFeature(featA): featB = QgsFeature() featsB = self.lyr.getFeatures() sor = [] while featsB.nextFeature(featB): if featA.id() != featB.id(): tav = featA.geometry().asPoint().distance(featB.geometry().asPoint()) # self.plainTextEdit.insertPlainText("%s %s %s\n" % (featA.id(), featB.id(), tav)) if tav <= trh: sor.append(featB.id()) lst.append(sor) # self.plainTextEdit.insertPlainText("%s\n" % lst) return lst
def linearMatrix(self, parameters, context, source, inField, target_source, targetField, same_source_and_target, matType, nPoints, feedback): if same_source_and_target: # need to fetch an extra point from the index, since the closest match will always be the same # as the input feature nPoints += 1 inIdx = source.fields().lookupField(inField) outIdx = target_source.fields().lookupField(targetField) fields = QgsFields() input_id_field = source.fields()[inIdx] input_id_field.setName('InputID') fields.append(input_id_field) if matType == 0: target_id_field = target_source.fields()[outIdx] target_id_field.setName('TargetID') fields.append(target_id_field) fields.append(QgsField('Distance', QVariant.Double)) else: fields.append(QgsField('MEAN', QVariant.Double)) fields.append(QgsField('STDDEV', QVariant.Double)) fields.append(QgsField('MIN', QVariant.Double)) fields.append(QgsField('MAX', QVariant.Double)) out_wkb = QgsWkbTypes.multiType(source.wkbType()) if matType == 0 else source.wkbType() (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, out_wkb, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) index = QgsSpatialIndex(target_source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(source.sourceCrs(), context.transformContext())), feedback) distArea = QgsDistanceArea() distArea.setSourceCrs(source.sourceCrs(), context.transformContext()) distArea.setEllipsoid(context.project().ellipsoid()) features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([inIdx])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break inGeom = inFeat.geometry() inID = str(inFeat.attributes()[inIdx]) featList = index.nearestNeighbor(inGeom.asPoint(), nPoints) distList = [] vari = 0.0 request = QgsFeatureRequest().setFilterFids(featList).setSubsetOfAttributes([outIdx]).setDestinationCrs(source.sourceCrs(), context.transformContext()) for outFeat in target_source.getFeatures(request): if feedback.isCanceled(): break if same_source_and_target and inFeat.id() == outFeat.id(): continue outID = outFeat.attributes()[outIdx] outGeom = outFeat.geometry() dist = distArea.measureLine(inGeom.asPoint(), outGeom.asPoint()) if matType == 0: out_feature = QgsFeature() out_geom = QgsGeometry.unaryUnion([inFeat.geometry(), outFeat.geometry()]) out_feature.setGeometry(out_geom) out_feature.setAttributes([inID, outID, dist]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) else: distList.append(float(dist)) if matType != 0: mean = sum(distList) / len(distList) for i in distList: vari += (i - mean) * (i - mean) vari = math.sqrt(vari / len(distList)) out_feature = QgsFeature() out_feature.setGeometry(inFeat.geometry()) out_feature.setAttributes([inID, mean, vari, min(distList), max(distList)]) sink.addFeature(out_feature, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
class GuidanceDock(QtGui.QDockWidget, FORM_CLASS): ''' classdocs ''' def __init__(self, parent=None): ''' Constructor ''' super(GuidanceDock, self).__init__(parent) self.setupUi(self) self.compass = CompassWidget() self.compass.setMinimumHeight(80) self.verticalLayout.addWidget(self.compass) self.verticalLayout.setStretch(5, 8) self.distArea = QgsDistanceArea() self.distArea.setEllipsoid(u'WGS84') self.distArea.setEllipsoidalMode(True) self.distArea.setSourceCrs(3452L) self.fontSize = 11 self.source = None self.target = None self.srcPos = [None, 0.0] self.trgPos = [None, 0.0] self.srcHeading = 0.0 self.trgHeading = 0.0 s = QSettings() self.format = s.value('PosiView/Guidance/Format', defaultValue=1, type=int) self.showUtc = s.value('PosiView/Misc/ShowUtcClock', defaultValue=False, type=bool) self.timer = 0 self.setUtcClock() def setUtcClock(self): if self.showUtc: if not self.timer: self.timer = self.startTimer(1000) self.frameUtcClock.show() else: self.frameUtcClock.hide() self.killTimer(self.timer) self.timer = 0 def setMobiles(self, mobiles): self.reset() self.mobiles = mobiles self.comboBoxSource.blockSignals(True) self.comboBoxTarget.blockSignals(True) self.comboBoxSource.clear() self.comboBoxSource.addItems(sorted(mobiles.keys())) self.comboBoxSource.setCurrentIndex(-1) self.comboBoxTarget.clear() self.comboBoxTarget.addItems(sorted(mobiles.keys())) self.comboBoxTarget.setCurrentIndex(-1) self.comboBoxSource.blockSignals(False) self.comboBoxTarget.blockSignals(False) s = QSettings() m = s.value('PosiView/Guidance/Source') if m in self.mobiles: self.comboBoxSource.setCurrentIndex(self.comboBoxSource.findText(m)) m = s.value('PosiView/Guidance/Target') if m in self.mobiles: self.comboBoxTarget.setCurrentIndex(self.comboBoxTarget.findText(m)) self.showUtc = s.value('PosiView/Misc/ShowUtcClock', defaultValue=False, type=bool) self.setUtcClock() @pyqtSlot(name='on_pushButtonFormat_clicked') def switchCoordinateFormat(self): self.format = (self.format + 1) % 3 s = QSettings() s.setValue('PosiView/Guidance/Format', self.format) if self.trgPos[0]: lon, lat = self.posToStr(self.trgPos[0]) self.labelTargetLat.setText(lat) self.labelTargetLon.setText(lon) if self.srcPos[0]: lon, lat = self.posToStr(self.srcPos[0]) self.labelSourceLat.setText(lat) self.labelSourceLon.setText(lon) def posToStr(self, pos): if self.format == 0: return "{:.6f}".format(pos.x()), "{:.6f}".format(pos.y()) if self.format == 1: return pos.toDegreesMinutes(4, True, True).split(',') if self.format == 2: return pos.toDegreesMinutesSeconds(2, True, True).split(',') @pyqtSlot(str, name='on_comboBoxSource_currentIndexChanged') def sourceChanged(self, mob): if self.source is not None: try: self.source.newPosition.disconnect(self.onNewSourcePosition) self.source.newAttitude.disconnect(self.onNewSourceAttitude) except TypeError: pass try: self.source = self.mobiles[mob] self.source.newPosition.connect(self.onNewSourcePosition) self.source.newAttitude.connect(self.onNewSourceAttitude) s = QSettings() s.setValue('PosiView/Guidance/Source', mob) except KeyError: self.source = None self.resetSource() @pyqtSlot(str, name='on_comboBoxTarget_currentIndexChanged') def targetChanged(self, mob): if self.target is not None: try: self.target.newPosition.disconnect(self.onNewTargetPosition) self.target.newAttitude.disconnect(self.onNewTargetAttitude) except TypeError: pass try: self.target = self.mobiles[mob] self.target.newPosition.connect(self.onNewTargetPosition) self.target.newAttitude.connect(self.onNewTargetAttitude) s = QSettings() s.setValue('PosiView/Guidance/Target', mob) except KeyError: self.target = None self.resetTarget() @pyqtSlot(float, QgsPoint, float, float) def onNewSourcePosition(self, fix, pos, depth, altitude): if [pos, depth] != self.srcPos: lon, lat = self.posToStr(pos) self.labelSourceLat.setText(lat) self.labelSourceLon.setText(lon) self.labelSourceDepth.setText(str(depth)) if self.trgPos[0] is not None: self.labelVertDistance.setText(str(self.trgPos[1] - depth)) dist = self.distArea.measureLine(self.trgPos[0], pos) self.labelDistance.setText('{:.1f}'.format(dist)) if dist != 0: bearing = self.distArea.bearing(pos, self.trgPos[0]) * 180 / pi if bearing < 0: bearing += 360 else: bearing = 0.0 self.labelDirection.setText('{:.1f}'.format(bearing)) self.srcPos = [pos, depth] @pyqtSlot(float, QgsPoint, float, float) def onNewTargetPosition(self, fix, pos, depth, altitude): if [pos, depth] != self.trgPos: lon, lat = self.posToStr(pos) self.labelTargetLat.setText(lat) self.labelTargetLon.setText(lon) self.labelTargetDepth.setText(str(depth)) if self.srcPos[0] is not None: self.labelVertDistance.setText(str(depth - self.srcPos[1])) dist = self.distArea.measureLine(pos, self.srcPos[0]) self.labelDistance.setText('{:.1f}'.format(dist)) if dist != 0: bearing = self.distArea.bearing(self.srcPos[0], pos) * 180 / pi if bearing < 0: bearing += 360 else: bearing = 0.0 self.labelDirection.setText('{:.1f}'.format(bearing)) self.trgPos = [pos, depth] @pyqtSlot(float, float, float) def onNewTargetAttitude(self, heading, pitch, roll): if self.trgHeading != heading: self.trgHeading = heading self.labelTargetHeading.setText(str(heading)) self.compass.setAngle2(heading) @pyqtSlot(float, float, float) def onNewSourceAttitude(self, heading, pitch, roll): if self.srcHeading != heading: self.srcHeading = heading self.labelSourceHeading.setText(str(heading)) self.compass.setAngle(heading) def reset(self): try: if self.source is not None: self.source.newPosition.disconnect(self.onNewSourcePosition) self.source.newAttitude.disconnect(self.onNewSourceAttitude) if self.target is not None: self.target.newPosition.disconnect(self.onNewTargetPosition) self.target.newAttitude.disconnect(self.onNewTargetAttitude) except TypeError: pass self.source = None self.target = None self.resetSource() self.resetTarget() self.resetDistBearing() def resetSource(self): self.srcPos = [None, 0.0] self.srcHeading = -720.0 self.labelSourceLat.setText('---') self.labelSourceLon.setText('---') self.labelSourceHeading.setText('---') self.labelSourceDepth.setText('---') self.compass.reset(1) self.resetDistBearing() def resetTarget(self): self.trgPos = [None, 0.0] self.trgHeading = -720.0 self.labelTargetLat.setText('---') self.labelTargetLon.setText('---') self.labelTargetHeading.setText('---') self.labelTargetDepth.setText('---') self.compass.reset(2) self.resetDistBearing() def resetDistBearing(self): self.labelDirection.setText('---') self.labelDistance.setText('---') self.labelVertDistance.setText('---') def resizeEvent(self, event): fsize = event.size().width() / 40 if fsize != self.fontSize: self.fontSize = fsize self.dockWidgetContents.setStyleSheet("font-weight: bold; font-size: {}pt".format(self.fontSize)) return QtGui.QDockWidget.resizeEvent(self, event) def timerEvent(self, event): dt = QDateTime.currentDateTimeUtc() self.labelTimeUtc.setText(dt.time().toString(u'hh:mm:ss'))
class GuidanceDock(QDockWidget, FORM_CLASS): ''' classdocs ''' def __init__(self, parent=None): ''' Constructor ''' super(GuidanceDock, self).__init__(parent) self.setupUi(self) self.setStyleSheet("QLabel { padding-left: 5px; padding-right: 5px; }") self.compass = CompassWidget() self.compass.setMinimumHeight(80) self.verticalLayout.addWidget(self.compass) self.verticalLayout.setStretch(5, 8) self.distArea = QgsDistanceArea() self.distArea.setEllipsoid(u'WGS84') self.distArea.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:4326'), QgsProject.instance().transformContext()) self.fontSize = 11 self.source = None self.target = None self.srcPos = [None, 0.0] self.trgPos = [None, 0.0] self.srcHeading = 0.0 self.trgHeading = 0.0 s = QSettings() self.format = s.value('PosiView/Guidance/Format', defaultValue=1, type=int) self.showUtc = s.value('PosiView/Misc/ShowUtcClock', defaultValue=False, type=bool) self.timer = 0 self.setUtcClock() self.layer = None def setUtcClock(self): if self.showUtc: if not self.timer: self.timer = self.startTimer(1000) self.frameUtcClock.show() else: self.frameUtcClock.hide() self.killTimer(self.timer) self.timer = 0 def setMobiles(self, mobiles): self.reset() self.mobiles = mobiles self.comboBoxSource.blockSignals(True) self.comboBoxTarget.blockSignals(True) mobs = sorted(mobiles.keys()) self.comboBoxSource.clear() self.comboBoxSource.addItems(mobs) self.comboBoxSource.setCurrentIndex(-1) self.comboBoxTarget.clear() self.comboBoxTarget.addItems(mobs) self.comboBoxTarget.setCurrentIndex(-1) self.comboBoxSource.blockSignals(False) self.comboBoxTarget.blockSignals(False) s = QSettings() m = s.value('PosiView/Guidance/Source') if m in self.mobiles: self.comboBoxSource.setCurrentText(m) # Index(self.comboBoxSource.findText(m)) m = s.value('PosiView/Guidance/Target') if m in self.mobiles: self.comboBoxTarget.setCurrentText(m) # Index(self.comboBoxTarget.findText(m)) self.showUtc = s.value('PosiView/Misc/ShowUtcClock', defaultValue=False, type=bool) self.setUtcClock() @pyqtSlot(name='on_pushButtonFormat_clicked') def switchCoordinateFormat(self): self.format = (self.format + 1) % 3 s = QSettings() s.setValue('PosiView/Guidance/Format', self.format) if self.trgPos[0]: lon, lat = self.posToStr(self.trgPos[0]) self.labelTargetLat.setText(lat) self.labelTargetLon.setText(lon) if self.srcPos[0]: lon, lat = self.posToStr(self.srcPos[0]) self.labelSourceLat.setText(lat) self.labelSourceLon.setText(lon) def posToStr(self, pos): if self.format == 0: return "{:.6f}".format(pos.x()), "{:.6f}".format(pos.y()) if self.format == 1: return QgsCoordinateFormatter.format(pos, QgsCoordinateFormatter.FormatDegreesMinutes, 4).split(',') if self.format == 2: return QgsCoordinateFormatter.format(pos, QgsCoordinateFormatter.FormatDegreesMinutesSeconds, 2).split(',') @pyqtSlot(str, name='on_comboBoxSource_currentIndexChanged') def sourceChanged(self, mob): if self.source is not None: try: self.source.newPosition.disconnect(self.onNewSourcePosition) self.source.newAttitude.disconnect(self.onNewSourceAttitude) except TypeError: pass if mob in self.mobiles: try: self.source = self.mobiles[mob] self.source.newPosition.connect(self.onNewSourcePosition) self.source.newAttitude.connect(self.onNewSourceAttitude) s = QSettings() s.setValue('PosiView/Guidance/Source', mob) except KeyError: self.source = None self.resetSource() elif self.layer: for f in self.layer.getFeatures(): if f['name'] == mob[:-2]: pos = f.geometry().asPoint() self.resetSource() self.onNewSourcePosition(None, pos, -9999, -9999) @pyqtSlot(str, name='on_comboBoxTarget_currentIndexChanged') def targetChanged(self, mob): if self.target is not None: try: self.target.newPosition.disconnect(self.onNewTargetPosition) self.target.newAttitude.disconnect(self.onNewTargetAttitude) except TypeError: pass if mob in self.mobiles: try: self.target = self.mobiles[mob] self.target.newPosition.connect(self.onNewTargetPosition) self.target.newAttitude.connect(self.onNewTargetAttitude) s = QSettings() s.setValue('PosiView/Guidance/Target', mob) except KeyError: self.target = None self.resetTarget() elif self.layer: for f in self.layer.getFeatures(): if f['name'] == mob[:-2]: pos = f.geometry().asPoint() self.resetTarget() self.onNewTargetPosition(None, pos, -9999, -9999) @pyqtSlot(float, QgsPointXY, float, float) def onNewSourcePosition(self, fix, pos, depth, altitude): if [pos, depth] != self.srcPos: lon, lat = self.posToStr(pos) self.labelSourceLat.setText(lat) self.labelSourceLon.setText(lon) if depth > -9999: self.labelSourceDepth.setText('{:.1f}'.format(depth)) if self.trgPos[0] is not None: if depth > -9999 and self.trgPos[1] > -9999: self.labelVertDistance.setText('{:.1f}'.format(self.trgPos[1] - depth)) dist = self.distArea.measureLine(self.trgPos[0], pos) self.labelDistance.setText('{:.1f}'.format(dist)) if dist != 0: bearing = self.distArea.bearing(pos, self.trgPos[0]) * 180 / pi if bearing < 0: bearing += 360 else: bearing = 0.0 self.labelDirection.setText('{:.1f}'.format(bearing)) self.srcPos = [pos, depth] @pyqtSlot(float, QgsPointXY, float, float) def onNewTargetPosition(self, fix, pos, depth, altitude): if [pos, depth] != self.trgPos: lon, lat = self.posToStr(pos) self.labelTargetLat.setText(lat) self.labelTargetLon.setText(lon) if depth > -9999: self.labelTargetDepth.setText('{:.1f}'.format(depth)) if self.srcPos[0] is not None: if depth > -9999 and self.srcPos[1] > -9999: self.labelVertDistance.setText('{:.1f}'.format(depth - self.srcPos[1])) dist = self.distArea.measureLine(pos, self.srcPos[0]) self.labelDistance.setText('{:.1f}'.format(dist)) if dist != 0: bearing = self.distArea.bearing(self.srcPos[0], pos) * 180 / pi if bearing < 0: bearing += 360 else: bearing = 0.0 self.labelDirection.setText('{:.1f}'.format(bearing)) self.trgPos = [pos, depth] @pyqtSlot(float, float, float) def onNewTargetAttitude(self, heading, pitch, roll): if self.trgHeading != heading: self.trgHeading = heading self.labelTargetHeading.setText('{:.1f}'.format(heading)) self.compass.setAngle2(heading) @pyqtSlot(float, float, float) def onNewSourceAttitude(self, heading, pitch, roll): if self.srcHeading != heading: self.srcHeading = heading self.labelSourceHeading.setText('{:.1f}'.format(heading)) self.compass.setAngle(heading) @pyqtSlot(QgsMapLayer) def onActiveLayerChanged(self, layer): if self.cleanComboBox(self.comboBoxSource): self.resetSource() if self.cleanComboBox(self.comboBoxTarget): self.resetTarget() self.layer = None if not layer: return if layer.type() == QgsMapLayer.VectorLayer and layer.wkbType() == QgsWkbTypes.Point: if layer.fields().indexOf('name') != -1: self.layer = layer self.comboBoxSource.blockSignals(True) self.comboBoxTarget.blockSignals(True) items = sorted([f['name'] + ' ' for f in layer.getFeatures()]) self.comboBoxSource.addItems(items) self.comboBoxTarget.addItems(items) self.comboBoxSource.blockSignals(False) self.comboBoxTarget.blockSignals(False) def reset(self): try: if self.source is not None: self.source.newPosition.disconnect(self.onNewSourcePosition) self.source.newAttitude.disconnect(self.onNewSourceAttitude) if self.target is not None: self.target.newPosition.disconnect(self.onNewTargetPosition) self.target.newAttitude.disconnect(self.onNewTargetAttitude) except TypeError: pass self.source = None self.target = None self.resetSource() self.resetTarget() self.resetDistBearing() def resetSource(self): self.srcPos = [None, 0.0] self.srcHeading = -9999.0 self.labelSourceLat.setText('---') self.labelSourceLon.setText('---') self.labelSourceHeading.setText('---') self.labelSourceDepth.setText('---') self.compass.reset(1) self.resetDistBearing() def resetTarget(self): self.trgPos = [None, 0.0] self.trgHeading = -9999.0 self.labelTargetLat.setText('---') self.labelTargetLon.setText('---') self.labelTargetHeading.setText('---') self.labelTargetDepth.setText('---') self.compass.reset(2) self.resetDistBearing() def cleanComboBox(self, comboBox): comboBox.blockSignals(True) ct = comboBox.currentText() for _ in range(comboBox.count() - len(self.mobiles)): comboBox.removeItem(len(self.mobiles)) if ct not in self.mobiles: comboBox.setCurrentIndex(-1) res = True else: res = False comboBox.blockSignals(False) return res def resetDistBearing(self): self.labelDirection.setText('---') self.labelDistance.setText('---') self.labelVertDistance.setText('---') def resizeEvent(self, event): fsize = max(8, event.size().width() / 50) if fsize != self.fontSize: self.fontSize = fsize self.dockWidgetContents.setStyleSheet("font-weight: bold; font-size: {}pt;".format(self.fontSize)) return QDockWidget.resizeEvent(self, event) def timerEvent(self, event): dt = QDateTime.currentDateTimeUtc() self.labelTimeUtc.setText(dt.time().toString(u'hh:mm:ss'))
class SizeCalculator(): """Special object to handle size calculation with an output unit.""" def __init__( self, coordinate_reference_system, geometry_type, exposure_key): """Constructor for the size calculator. :param coordinate_reference_system: The Coordinate Reference System of the layer. :type coordinate_reference_system: QgsCoordinateReferenceSystem :param exposure_key: The geometry type of the layer. :type exposure_key: qgis.core.QgsWkbTypes.GeometryType """ self.calculator = QgsDistanceArea() self.calculator.setSourceCrs( coordinate_reference_system, QgsProject.instance().transformContext() ) self.calculator.setEllipsoid('WGS84') if geometry_type == QgsWkbTypes.LineGeometry: self.default_unit = unit_metres LOGGER.info('The size calculator is set to use {unit}'.format( unit=distance_unit[self.calculator.lengthUnits()])) else: self.default_unit = unit_square_metres LOGGER.info('The size calculator is set to use {unit}'.format( unit=distance_unit[self.calculator.areaUnits()])) self.geometry_type = geometry_type self.output_unit = None if exposure_key: exposure_definition = definition(exposure_key) self.output_unit = exposure_definition['size_unit'] def measure_distance(self, point_a, point_b): """Measure the distance between two points. This is added here since QgsDistanceArea object is already called here. :param point_a: First Point. :type point_a: QgsPoint :param point_b: Second Point. :type point_b: QgsPoint :return: The distance between input points. :rtype: float """ return self.calculator.measureLine(point_a, point_b) def measure(self, geometry): """Measure the length or the area of a geometry. :param geometry: The geometry. :type geometry: QgsGeometry :return: The geometric size in the expected exposure unit. :rtype: float """ message = 'Size with NaN value : geometry valid={valid}, WKT={wkt}' feature_size = 0 if geometry.isMultipart(): # Be careful, the size calculator is not working well on a # multipart. # So we compute the size part per part. See ticket #3812 for single in geometry.asGeometryCollection(): if self.geometry_type == QgsWkbTypes.LineGeometry: geometry_size = self.calculator.measureLength(single) else: geometry_size = self.calculator.measureArea(single) if not isnan(geometry_size): feature_size += geometry_size else: LOGGER.debug(message.format( valid=single.isGeosValid(), wkt=single.asWkt())) else: if self.geometry_type == QgsWkbTypes.LineGeometry: geometry_size = self.calculator.measureLength(geometry) else: geometry_size = self.calculator.measureArea(geometry) if not isnan(geometry_size): feature_size = geometry_size else: LOGGER.debug(message.format( valid=geometry.isGeosValid(), wkt=geometry.asWkt())) feature_size = round(feature_size) if self.output_unit: if self.output_unit != self.default_unit: feature_size = convert_unit( feature_size, self.default_unit, self.output_unit) return feature_size
def processAlgorithm(self, progress): layer = dataobjects.getObjectFromUri( self.getParameterValue(self.VECTOR)) pointCount = float(self.getParameterValue(self.POINT_NUMBER)) minDistance = float(self.getParameterValue(self.MIN_DISTANCE)) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, layer.crs()) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 featureCount = layer.featureCount() total = 100.0 / pointCount index = QgsSpatialIndex() points = dict() da = QgsDistanceArea() request = QgsFeatureRequest() random.seed() while nIterations < maxIterations and nPoints < pointCount: # pick random feature fid = random.randint(0, featureCount - 1) f = next(layer.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 pnt = QgsPoint(rx, ry) geom = QgsGeometry.fromPoint(pnt) if vector.checkMinDistance(pnt, index, minDistance, points): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) writer.addFeature(f) index.insertFeature(f) points[nPoints] = pnt nPoints += 1 progress.setPercentage(int(nPoints * total)) nIterations += 1 if nPoints < pointCount: ProcessingLog.addToLog(ProcessingLog.LOG_INFO, self.tr('Can not generate requested number of random points. ' 'Maximum number of attempts exceeded.')) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) 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()) points = dict() features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( [group_field_index, order_field_index])) 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().geometry().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()) 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() 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, progress): layerPoints = dataobjects.getObjectFromUri(self.getParameterValue(self.POINTS)) layerHubs = dataobjects.getObjectFromUri(self.getParameterValue(self.HUBS)) fieldName = self.getParameterValue(self.FIELD) addLines = self.getParameterValue(self.GEOMETRY) units = self.UNITS[self.getParameterValue(self.UNIT)] if layerPoints.source() == layerHubs.source(): raise GeoAlgorithmExecutionException(self.tr("Same layer given for both hubs and spokes")) geomType = QGis.WKBPoint if addLines: geomType = QGis.WKBLineString fields = layerPoints.pendingFields() fields.append(QgsField("HubName", QVariant.String)) fields.append(QgsField("HubDist", QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, geomType, layerPoints.crs()) # Create array of hubs in memory hubs = [] features = vector.features(layerHubs) for f in features: hubs.append(Hub(f.geometry().boundingBox().center(), unicode(f[fieldName]))) distance = QgsDistanceArea() distance.setSourceCrs(layerPoints.crs().srsid()) distance.setEllipsoidalMode(True) # Scan source points, find nearest hub, and write to output file features = vector.features(layerPoints) count = len(features) total = 100.0 / float(count) for count, f in enumerate(features): src = f.geometry().boundingBox().center() closest = hubs[0] hubDist = distance.measureLine(src, closest.point) for hub in hubs: dist = distance.measureLine(src, hub.point) if dist < hubDist: closest = hub hubDist = dist attributes = f.attributes() attributes.append(closest.name) if units == "Feet": attributes.append(hubDist * 3.2808399) elif units == "Miles": attributes.append(hubDist * 0.000621371192) elif units == "Kilometers": attributes.append(hubDist / 1000.0) elif units != "Meters": attributes.append(sqrt(pow(src.x() - closest.point.x(), 2.0) + pow(src.y() - closest.point.y(), 2.0))) else: attributes.append(hubDist) feat = QgsFeature() feat.setAttributes(attributes) if geomType == QGis.WKBPoint: feat.setGeometry(QgsGeometry.fromPoint(src)) else: feat.setGeometry(QgsGeometry.fromPolyline([src, closest.point])) writer.addFeature(feat) progress.setPercentage(int(count * total)) del writer
class ScaleBar(QgsMapCanvasItem): def __init__(self, proj, canvas): super(ScaleBar, self).__init__(canvas) self.proj = proj self.canvas = canvas # set font self.myfont = QFont("helvetica", 10) self.myfontmetrics = QFontMetrics(self.myfont) # set crs and ellipsoid crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs( crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) def paint(self, painter, xxx, xxx2): """Paint scalebar on painter.""" mymajorticksize = 8 mypreferredsize = 1 mytextoffsetx = 3 myscalebarunit = "m" mymapunits = QgsUnitTypes.DistanceMeters # get height and width canvasheight = painter.device().height() canvaswidth = painter.device().width() # set origins myoriginx = canvaswidth - 40 myoriginy = canvasheight - 20 # save previous painter painter.save() # set rotation painter.rotate(-self.canvas.rotation()) # set translation painter.translate(myoriginx, myoriginy) # calculate size of scale bar for preferred number of map units myscalebarwidth = mypreferredsize # if scale bar is very small reset to 1/4 of the canvs wide if myscalebarwidth < 30: # pixels myscalebarwidth = canvaswidth / 4.0 # if scale bar is more than half the cnavs wide keep halving until not while myscalebarwidth > canvaswidth / 3.0: myscalebarwidth = myscalebarwidth / 3.0 # get the distance between 2 points transform = self.canvas.getCoordinateTransform() start_point = transform.toMapCoordinates(0 - myscalebarwidth, 0) end_point = transform.toMapCoordinates(0, 0) distance = self.distance_calc.measureLine([start_point, end_point]) # change scale (km,m,cm,mm) if mymapunits == QgsUnitTypes.DistanceMeters: if distance > 1000.0: myscalebarunit = "km" distance = distance / 1000 rounddist = round(distance, 1) elif distance < 0.01: myscalebarunit = "mm" distance = distance * 1000 rounddist = round(distance, 4) elif distance < 0.1: myscalebarunit = "cm" distance = distance * 100 rounddist = round(distance, 2) else: myscalebarunit = "m" rounddist = round(distance, 1) # set new scalebarwidth myroundscalebarwidth = (rounddist * myscalebarwidth / distance) # set qpen mybackgroundpen = QPen(Qt.black, 4) # create bar mybararray = QPolygon(2) mybararray.putPoints(0, 0 - myroundscalebarwidth, 0 + mymajorticksize / 2, 0, 0 + mymajorticksize / 2) painter.setPen(mybackgroundpen) # draw line painter.drawPolyline(mybararray) # draw 0 painter.drawText( 0 - myroundscalebarwidth - (self.myfontmetrics.width("0") / 2), 0 - (self.myfontmetrics.height() / 4), "0") # draw max painter.drawText(0 - (self.myfontmetrics.width(str(rounddist)) / 2), 0 - (self.myfontmetrics.height() / 4), str(rounddist)) # draw unit label painter.drawText((0 + mytextoffsetx), (0 + mymajorticksize), str(myscalebarunit)) # restore painter painter.restore()