def testSubstitutionMap(self): """Test that we can use degree symbols in substitutions. """ # Create a point and convert it to text containing a degree symbol. myPoint = QgsPointXY(12.3, -33.33) myCoordinates = myPoint.toDegreesMinutesSeconds(2) myTokens = myCoordinates.split(',') myLongitude = myTokens[0] myLatitude = myTokens[1] myText = 'Latitude: %s, Longitude: %s' % (myLatitude, myLongitude) # Load the composition with the substitutions myComposition = QgsComposition(QgsProject.instance()) mySubstitutionMap = {'replace-me': myText} myFile = os.path.join(TEST_DATA_DIR, 'template-for-substitution.qpt') with open(myFile) as f: myTemplateContent = f.read() myDocument = QDomDocument() myDocument.setContent(myTemplateContent) myComposition.loadFromTemplate(myDocument, mySubstitutionMap) # We should be able to get map0 myMap = myComposition.getComposerMapById(0) myMessage = ('Map 0 could not be found in template %s', myFile) assert myMap is not None, myMessage
class TestQgsPointXY(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) def setUp(self): self.mPoint = QgsPointXY(10.0, 10.0) def test_Point(self): myExpectedValue = 10.0 myActualValue = self.mPoint.x() myMessage = 'Expected: %s Got: %s' % (myExpectedValue, myActualValue) assert myExpectedValue == myActualValue, myMessage def test_pointToString(self): myExpectedValue = '10, 10' myActualValue = self.mPoint.toString() myMessage = 'Expected: %s Got: %s' % (myExpectedValue, myActualValue) assert myExpectedValue == myActualValue, myMessage def test_hash(self): a = QgsPointXY(2.0, 1.0) b = QgsPointXY(2.0, 2.0) c = QgsPointXY(1.0, 2.0) d = QgsPointXY(1.0, 1.0) e = QgsPointXY(2.0, 1.0) assert a.__hash__() != b.__hash__() assert e.__hash__() == a.__hash__() mySet = set([a, b, c, d, e]) assert len(mySet) == 4
def testRenderMetersInMapUnits(self): crs_wsg84 = QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326') rt_extent = QgsRectangle(13.37768985634235, 52.51625705830762, 13.37771931686235, 52.51628651882762) point_berlin_wsg84 = QgsPointXY(13.37770458660236, 52.51627178856762) length_wsg84_mapunits = 0.00001473026350140572 meters_test = 2.40 da_wsg84 = QgsDistanceArea() da_wsg84.setSourceCrs(crs_wsg84, QgsProject.instance().transformContext()) if (da_wsg84.sourceCrs().isGeographic()): da_wsg84.setEllipsoid(da_wsg84.sourceCrs().ellipsoidAcronym()) length_meter_mapunits = da_wsg84.measureLineProjected(point_berlin_wsg84, 1.0, (math.pi / 2)) meters_test_mapunits = meters_test * length_wsg84_mapunits meters_test_pixel = meters_test * length_wsg84_mapunits ms = QgsMapSettings() ms.setDestinationCrs(crs_wsg84) ms.setExtent(rt_extent) r = QgsRenderContext.fromMapSettings(ms) r.setExtent(rt_extent) self.assertEqual(r.extent().center().toString(7), point_berlin_wsg84.toString(7)) c = QgsMapUnitScale() r.setDistanceArea(da_wsg84) result_test_painterunits = r.convertToPainterUnits(meters_test, QgsUnitTypes.RenderMetersInMapUnits, c) self.assertEqual(QgsDistanceArea.formatDistance(result_test_painterunits, 7, QgsUnitTypes.DistanceUnknownUnit, True), QgsDistanceArea.formatDistance(meters_test_mapunits, 7, QgsUnitTypes.DistanceUnknownUnit, True)) result_test_mapunits = r.convertToMapUnits(meters_test, QgsUnitTypes.RenderMetersInMapUnits, c) self.assertEqual(QgsDistanceArea.formatDistance(result_test_mapunits, 7, QgsUnitTypes.DistanceDegrees, True), QgsDistanceArea.formatDistance(meters_test_mapunits, 7, QgsUnitTypes.DistanceDegrees, True)) result_test_meters = r.convertFromMapUnits(meters_test_mapunits, QgsUnitTypes.RenderMetersInMapUnits) self.assertEqual(QgsDistanceArea.formatDistance(result_test_meters, 1, QgsUnitTypes.DistanceMeters, True), QgsDistanceArea.formatDistance(meters_test, 1, QgsUnitTypes.DistanceMeters, True))
def __init__(self, plugin, filepath, title, screenExtent): QgsPluginLayer.__init__( self, FreehandRasterGeoreferencerLayer.LAYER_TYPE, title) self.plugin = plugin self.iface = plugin.iface self.title = title self.filepath = filepath self.screenExtent = screenExtent self.history = [] # set custom properties self.setCustomProperty("title", title) self.setCustomProperty("filepath", self.filepath) self.setValid(True) self.setTransparency(LayerDefaultSettings.TRANSPARENCY) self.setBlendModeByName(LayerDefaultSettings.BLEND_MODE) # dummy data: real init is done in intializeLayer self.center = QgsPointXY(0, 0) self.rotation = 0.0 self.xScale = 1.0 self.yScale = 1.0 self.error = False self.initializing = False self.initialized = False self.initializeLayer(screenExtent) self._extent = None self.provider = FreehandRasterGeoreferencerLayerProvider(self)
def testPoint(self): point = QgsReferencedPointXY(QgsPointXY(1.0, 2.0), QgsCoordinateReferenceSystem('epsg:3111')) self.assertEqual(point.x(), 1.0) self.assertEqual(point.y(), 2.0) self.assertEqual(point.crs().authid(), 'EPSG:3111') point.setCrs(QgsCoordinateReferenceSystem('epsg:28356')) self.assertEqual(point.crs().authid(), 'EPSG:28356') # in variant v = QVariant(QgsReferencedPointXY(QgsPointXY(3.0, 4.0), QgsCoordinateReferenceSystem('epsg:3111'))) self.assertEqual(v.value().x(), 3.0) self.assertEqual(v.value().y(), 4.0) self.assertEqual(v.value().crs().authid(), 'EPSG:3111') # to QgsPointXY p = QgsPointXY(point) self.assertEqual(p.x(), 1.0) self.assertEqual(p.y(), 2.0)
def test_hash(self): a = QgsPointXY(2.0, 1.0) b = QgsPointXY(2.0, 2.0) c = QgsPointXY(1.0, 2.0) d = QgsPointXY(1.0, 1.0) e = QgsPointXY(2.0, 1.0) assert a.__hash__() != b.__hash__() assert e.__hash__() == a.__hash__() mySet = set([a, b, c, d, e]) assert len(mySet) == 4
def checkPoint(self, point): state = 'yellow' if type(point) != QgsPointXY: try: if type(point) == list and len(point) == 2: point = QgsPointXY(point[0], point[1]) else: point = QgsPointXY(point) except TypeError: return state, None if self.dhm != {}: extent = self.dhm['extent'] [extLx, extHy, extHx, extLy] = extent if extLx <= float(point.x()) <= extHx \ and extLy <= float(point.y()) <= extHy: state = 'green' else: state = 'red' return state, point
def readXml(self, node, context): self.readCustomProperties(node) self.title = self.customProperty("title", "") self.filepath = self.customProperty("filepath", "") self.xScale = float(self.customProperty("xScale", 1.0)) self.yScale = float(self.customProperty("yScale", 1.0)) self.rotation = float(self.customProperty("rotation", 0.0)) xCenter = float(self.customProperty("xCenter", 0.0)) yCenter = float(self.customProperty("yCenter", 0.0)) self.center = QgsPointXY(xCenter, yCenter) self.setTransparency(int(self.customProperty( "transparency", LayerDefaultSettings.TRANSPARENCY))) self.setBlendModeByName(self.customProperty( "blendMode", LayerDefaultSettings.BLEND_MODE)) return True
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context) startPoints = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.START_POINTS), context) endPoint = self.getParameterValue(self.END_POINT) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.LineString, layer.crs(), context) tmp = endPoint.split(',') endPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Loading start points...')) request = QgsFeatureRequest() request.setFlags(request.flags() ^ QgsFeatureRequest.SubsetOfAttributes) features = QgsProcessingUtils.getFeatures(startPoints, context, request) count = QgsProcessingUtils.featureCount(startPoints, context) points = [endPoint] for f in features: points.append(f.geometry().asPoint()) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, points) feedback.pushInfo(self.tr('Calculating shortest paths...')) graph = builder.graph() idxEnd = graph.findVertex(snappedPoints[0]) route = [] total = 100.0 / count if count else 1 for i in range(1, count + 1): idxStart = graph.findVertex(snappedPoints[i]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: msg = self.tr('There is no route from start point ({}) to end point ({}).'.format(points[i].toString(), endPoint.toString())) feedback.setProgressText(msg) QgsMessageLog.logMessage(msg, self.tr('Processing'), QgsMessageLog.WARNING) continue cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append(graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[i]) route.reverse() geom = QgsGeometry.fromPolyline(route) feat.setGeometry(geom) feat['start'] = points[i].toString() feat['end'] = endPoint.toString() feat['cost'] = cost / multiplier writer.addFeature(feat, QgsFeatureSink.FastInsert) route[:] = [] feedback.setProgress(int(i * total)) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) proximity = self.parameterAsDouble(parameters, self.PROXIMITY, context) radius = self.parameterAsDouble(parameters, self.DISTANCE, context) horizontal = self.parameterAsBool(parameters, self.HORIZONTAL, context) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, source.fields(), source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 def searchRect(p): return QgsRectangle(p.x() - proximity, p.y() - proximity, p.x() + proximity, p.y() + proximity) index = QgsSpatialIndex() # NOTE: this is a Python port of QgsPointDistanceRenderer::renderFeature. If refining this algorithm, # please port the changes to QgsPointDistanceRenderer::renderFeature also! clustered_groups = [] group_index = {} group_locations = {} for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().asPoint() other_features_within_radius = index.intersects(searchRect(point)) if not other_features_within_radius: index.insertFeature(f) group = [f] clustered_groups.append(group) group_index[f.id()] = len(clustered_groups) - 1 group_locations[f.id()] = point else: # find group with closest location to this point (may be more than one within search tolerance) min_dist_feature_id = other_features_within_radius[0] min_dist = group_locations[min_dist_feature_id].distance(point) for i in range(1, len(other_features_within_radius)): candidate_id = other_features_within_radius[i] new_dist = group_locations[candidate_id].distance(point) if new_dist < min_dist: min_dist = new_dist min_dist_feature_id = candidate_id group_index_pos = group_index[min_dist_feature_id] group = clustered_groups[group_index_pos] # calculate new centroid of group old_center = group_locations[min_dist_feature_id] group_locations[min_dist_feature_id] = QgsPointXY((old_center.x() * len(group) + point.x()) / (len(group) + 1.0), (old_center.y() * len(group) + point.y()) / (len(group) + 1.0)) # add to a group clustered_groups[group_index_pos].append(f) group_index[f.id()] = group_index_pos feedback.setProgress(int(current * total)) current = 0 total = 100.0 / len(clustered_groups) if clustered_groups else 1 feedback.setProgress(0) fullPerimeter = 2 * math.pi for group in clustered_groups: if feedback.isCanceled(): break count = len(group) if count == 1: sink.addFeature(group[0], QgsFeatureSink.FastInsert) else: angleStep = fullPerimeter / count if count == 2 and horizontal: currentAngle = math.pi / 2 else: currentAngle = 0 old_point = group_locations[group[0].id()] for f in group: if feedback.isCanceled(): break sinusCurrentAngle = math.sin(currentAngle) cosinusCurrentAngle = math.cos(currentAngle) dx = radius * sinusCurrentAngle dy = radius * cosinusCurrentAngle # we want to keep any existing m/z values point = f.geometry().constGet().clone() point.setX(old_point.x() + dx) point.setY(old_point.y() + dy) f.setGeometry(QgsGeometry(point)) sink.addFeature(f, QgsFeatureSink.FastInsert) currentAngle += angleStep current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
class FreehandRasterGeoreferencerLayer(QgsPluginLayer): LAYER_TYPE = "FreehandRasterGeoreferencerLayer" transformParametersChanged = pyqtSignal(tuple) def __init__(self, plugin, filepath, title, screenExtent): QgsPluginLayer.__init__( self, FreehandRasterGeoreferencerLayer.LAYER_TYPE, title) self.plugin = plugin self.iface = plugin.iface self.title = title self.filepath = filepath self.screenExtent = screenExtent self.history = [] # set custom properties self.setCustomProperty("title", title) self.setCustomProperty("filepath", self.filepath) self.setValid(True) self.setTransparency(LayerDefaultSettings.TRANSPARENCY) self.setBlendModeByName(LayerDefaultSettings.BLEND_MODE) # dummy data: real init is done in intializeLayer self.center = QgsPointXY(0, 0) self.rotation = 0.0 self.xScale = 1.0 self.yScale = 1.0 self.error = False self.initializing = False self.initialized = False self.initializeLayer(screenExtent) self._extent = None self.provider = FreehandRasterGeoreferencerLayerProvider(self) def dataProvider(self): # issue with DBManager if the dataProvider of the QgsLayerPlugin # returns None return self.provider def setScale(self, xScale, yScale): self.xScale = xScale self.yScale = yScale def setRotation(self, rotation): rotation = round(rotation, 1) # keep in -180,180 interval if rotation < -180: rotation += 360 if rotation > 180: rotation -= 360 self.rotation = rotation def setCenter(self, center): self.center = center def commitTransformParameters(self): QgsProject.instance().setDirty(True) self._extent = None self.setCustomProperty("xScale", self.xScale) self.setCustomProperty("yScale", self.yScale) self.setCustomProperty("rotation", self.rotation) self.setCustomProperty("xCenter", self.center.x()) self.setCustomProperty("yCenter", self.center.y()) self.transformParametersChanged.emit( (self.xScale, self.yScale, self.rotation, self.center)) def reprojectTransformParameters(self, oldCrs, newCrs): transform = QgsCoordinateTransform(oldCrs, newCrs, QgsProject.instance()) newCenter = transform.transform(self.center) newExtent = transform.transform(self.extent()) # transform the parameters except rotation # TODO rotation could be better handled (maybe check rotation between # old and new extent) # but not really worth the effort ? self.setCrs(newCrs) self.setCenter(newCenter) self.resetScale(newExtent.width(), newExtent.height()) def resetTransformParametersToNewCrs(self): """ Attempts to keep the layer on the same region of the map when the map CRS is changed """ oldCrs = self.crs() newCrs = self.iface.mapCanvas().mapSettings().destinationCrs() self.reprojectTransformParameters(oldCrs, newCrs) self.commitTransformParameters() def setupCrsEvents(self): layerId = self.id() def removeCrsChangeHandler(layerIds): if layerId in layerIds: try: self.iface.mapCanvas().destinationCrsChanged.disconnect( self.resetTransformParametersToNewCrs) except Exception: pass try: QgsProject.instance().disconnect( removeCrsChangeHandler) except Exception: pass self.iface.mapCanvas().destinationCrsChanged.connect( self.resetTransformParametersToNewCrs) QgsProject.instance().layersRemoved.connect( removeCrsChangeHandler) def setupCrs(self): mapCrs = self.iface.mapCanvas().mapSettings().destinationCrs() self.setCrs(mapCrs) self.setupCrsEvents() def repaint(self): self.repaintRequested.emit() def transformParameters(self): return (self.center, self.rotation, self.xScale, self.yScale) def initializeLayer(self, screenExtent=None): if self.error or self.initialized or self.initializing: return if self.filepath is not None: # not safe... self.initializing = True filepath = self.getAbsoluteFilepath() if not os.path.exists(filepath): # TODO integrate with BadLayerHandler ? loadErrorDialog = LoadErrorDialog(filepath) result = loadErrorDialog.exec_() if result == 1: # absolute filepath = loadErrorDialog.lineEditImagePath.text() # to relative if needed self.filepath = utils.toRelativeToQGS(filepath) self.setCustomProperty("filepath", self.filepath) QgsProject.instance().setDirty(True) else: self.error = True del loadErrorDialog fileInfo = QFileInfo(filepath) ext = fileInfo.suffix() if ext == "pdf": s = QSettings() oldValidation = s.value("/Projections/defaultBehavior") s.setValue("/Projections/defaultBehavior", "useGlobal") # for not asking about crs path = fileInfo.filePath() baseName = fileInfo.baseName() layer = QgsRasterLayer(path, baseName) self.image = layer.previewAsImage(QSize(layer.width(),layer.height())) s.setValue("/Projections/defaultBehavior", oldValidation) else: reader = QImageReader(filepath) self.image = reader.read() self.initialized = True self.initializing = False self.setupCrs() if screenExtent: # constructor called from AddLayer action # if not, layer loaded from QGS project file # check if image already has georef info # use GDAL dataset = gdal.Open(filepath, gdal.GA_ReadOnly) georef = None if dataset: georef = dataset.GetGeoTransform() if georef and not self.is_default_geotransform(georef): self.initializeExistingGeoreferencing(dataset, georef) else: # init to default params self.setCenter(screenExtent.center()) self.setRotation(0.0) sw = screenExtent.width() sh = screenExtent.height() self.resetScale(sw, sh) self.commitTransformParameters() def initializeExistingGeoreferencing(self, dataset, georef): # georef can have scaling, rotation or translation rotation = 180 / math.pi * -math.atan2(georef[4], georef[1]) sx = math.sqrt(georef[1] ** 2 + georef[4] ** 2) sy = math.sqrt(georef[2] ** 2 + georef[5] ** 2) i_center_x = self.image.width() / 2 i_center_y = self.image.height() / 2 center = QgsPointXY(georef[0] + georef[1] * i_center_x + georef[2] * i_center_y, georef[3] + georef[4] * i_center_x + georef[5] * i_center_y) qDebug(repr(rotation) + " " + repr((sx, sy)) + " " + repr(center)) self.setRotation(rotation) self.setCenter(center) # keep yScale positive self.setScale(sx, sy) self.commitTransformParameters() crs_wkt = dataset.GetProjection() message_shown = False if crs_wkt: qcrs = QgsCoordinateReferenceSystem(crs_wkt) if qcrs != self.crs(): # reproject try: self.reprojectTransformParameters(qcrs, self.crs()) self.commitTransformParameters() self.showBarMessage( "Transform parameters changed", "Found existing georeferencing in raster but " "its CRS does not match the CRS of the map. " "Reprojected the extent.", Qgis.Warning, 5) message_shown = True except Exception as ex: QgsMessageLog.logMessage(repr(ex)) self.showBarMessage( "CRS does not match", "Found existing georeferencing in raster but " "its CRS does not match the CRS of the map. " "Unable to reproject.", Qgis.Warning, 5) message_shown = True # if no projection info, assume it is the same CRS # as the map and no warning if not message_shown: self.showBarMessage( "Georeferencing loaded", "Found existing georeferencing in raster", Qgis.Info, 3) # zoom (assume the user wants to work on the image) self.iface.mapCanvas().setExtent(self.extent()) def is_default_geotransform(self, georef): """ Check if there is really a transform or if it is just the default made up by GDAL """ return (georef[0] == 0 and georef[3] == 0 and georef[1] == 1 and georef[5] == 1) def resetScale(self, sw, sh): iw = self.image.width() ih = self.image.height() wratio = sw / iw hratio = sh / ih if wratio > hratio: # takes all height of current extent self.setScale(hratio, hratio) else: # all width self.setScale(wratio, wratio) def replaceImage(self, filepath, title): self.title = title self.filepath = filepath # set custom properties self.setCustomProperty("title", title) self.setCustomProperty("filepath", self.filepath) self.setName(title) fileInfo = QFileInfo(filepath) ext = fileInfo.suffix() if ext == "pdf": s = QSettings() oldValidation = s.value("/Projections/defaultBehavior") s.setValue("/Projections/defaultBehavior", "useGlobal") # for not asking about crs path = fileInfo.filePath() baseName = fileInfo.baseName() layer = QgsRasterLayer(path, baseName) self.image = layer.previewAsImage(QSize(layer.width(), layer.height())) s.setValue("/Projections/defaultBehavior", oldValidation) else: reader = QImageReader(filepath) self.image = reader.read() self.repaint() def clone(self): layer = FreehandRasterGeoreferencerLayer(self.plugin, self.filepath, self.title, self.screenExtent) layer.center = self.center layer.rotation = self.rotation layer.xScale = self.xScale layer.yScale = self.yScale layer.commitTransformParameters() return layer def getAbsoluteFilepath(self): if not os.path.isabs(self.filepath): # relative to QGS file qgsPath = QgsProject.instance().fileName() qgsFolder, _ = os.path.split(qgsPath) filepath = os.path.join(qgsFolder, self.filepath) else: filepath = self.filepath return filepath def extent(self): self.initializeLayer() if not self.initialized: qDebug("Not Initialized") return QgsRectangle(0, 0, 1, 1) if self._extent: return self._extent topLeft, topRight, bottomRight, bottomLeft = self.cornerCoordinates() left = min(topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x()) right = max(topLeft.x(), topRight.x(), bottomRight.x(), bottomLeft.x()) top = max(topLeft.y(), topRight.y(), bottomRight.y(), bottomLeft.y()) bottom = min(topLeft.y(), topRight.y(), bottomRight.y(), bottomLeft.y()) # recenter + create rectangle self._extent = QgsRectangle(left, bottom, right, top) return self._extent def cornerCoordinates(self): return self.transformedCornerCoordinates(self.center, self.rotation, self.xScale, self.yScale) def transformedCornerCoordinates(self, center, rotation, xScale, yScale): # scale topLeft = QgsPointXY(-self.image.width() / 2.0 * xScale, self.image.height() / 2.0 * yScale) topRight = QgsPointXY(self.image.width() / 2.0 * xScale, self.image.height() / 2.0 * yScale) bottomLeft = QgsPointXY(-self.image.width() / 2.0 * xScale, - self.image.height() / 2.0 * yScale) bottomRight = QgsPointXY(self.image.width() / 2.0 * xScale, - self.image.height() / 2.0 * yScale) # rotate # minus sign because rotation is CW in this class and Qt) rotationRad = -rotation * math.pi / 180 cosRot = math.cos(rotationRad) sinRot = math.sin(rotationRad) topLeft = self._rotate(topLeft, cosRot, sinRot) topRight = self._rotate(topRight, cosRot, sinRot) bottomRight = self._rotate(bottomRight, cosRot, sinRot) bottomLeft = self._rotate(bottomLeft, cosRot, sinRot) topLeft.set(topLeft.x() + center.x(), topLeft.y() + center.y()) topRight.set(topRight.x() + center.x(), topRight.y() + center.y()) bottomRight.set(bottomRight.x() + center.x(), bottomRight.y() + center.y()) bottomLeft.set(bottomLeft.x() + center.x(), bottomLeft.y() + center.y()) return (topLeft, topRight, bottomRight, bottomLeft) def transformedCornerCoordinatesFromPoint(self, startPoint, rotation, xScale, yScale): # startPoint is a fixed point for this new movement (rotation and # scale) # rotation is the global rotation of the image # xScale is the new xScale factor to be multiplied by self.xScale # idem for yScale # Calculate the coordinate of the center in a startPoint origin # coordinate system and apply scales dX = (self.center.x() - startPoint.x()) * xScale dY = (self.center.y() - startPoint.y()) * yScale # Half width and half height in the current transformation hW = (self.image.width() / 2.0) * self.xScale * xScale hH = (self.image.height() / 2.0) * self.yScale * yScale # Actual rectangle coordinates : pt1 = QgsPointXY(-hW, hH) pt2 = QgsPointXY(hW, hH) pt3 = QgsPointXY(hW, -hH) pt4 = QgsPointXY(-hW, -hH) # Actual rotation from the center # minus sign because rotation is CW in this class and Qt) rotationRad = -self.rotation * math.pi / 180 cosRot = math.cos(rotationRad) sinRot = math.sin(rotationRad) pt1 = self._rotate(pt1, cosRot, sinRot) pt2 = self._rotate(pt2, cosRot, sinRot) pt3 = self._rotate(pt3, cosRot, sinRot) pt4 = self._rotate(pt4, cosRot, sinRot) # Second transformation # displacement of the origin pt1 = QgsPointXY(pt1.x() + dX, pt1.y() + dY) pt2 = QgsPointXY(pt2.x() + dX, pt2.y() + dY) pt3 = QgsPointXY(pt3.x() + dX, pt3.y() + dY) pt4 = QgsPointXY(pt4.x() + dX, pt4.y() + dY) # Rotation # minus sign because rotation is CW in this class and Qt) rotationRad = -rotation * math.pi / 180 cosRot = math.cos(rotationRad) sinRot = math.sin(rotationRad) pt1 = self._rotate(pt1, cosRot, sinRot) pt2 = self._rotate(pt2, cosRot, sinRot) pt3 = self._rotate(pt3, cosRot, sinRot) pt4 = self._rotate(pt4, cosRot, sinRot) # translate to startPoint pt1 = QgsPointXY(pt1.x() + startPoint.x(), pt1.y() + startPoint.y()) pt2 = QgsPointXY(pt2.x() + startPoint.x(), pt2.y() + startPoint.y()) pt3 = QgsPointXY(pt3.x() + startPoint.x(), pt3.y() + startPoint.y()) pt4 = QgsPointXY(pt4.x() + startPoint.x(), pt4.y() + startPoint.y()) return (pt1, pt2, pt3, pt4) def moveCenterFromPointRotate(self, startPoint, rotation, xScale, yScale): cornerPoints = self.transformedCornerCoordinatesFromPoint( startPoint, rotation, xScale, yScale) self.center = QgsPointXY((cornerPoints[0].x( ) + cornerPoints[2].x()) / 2, (cornerPoints[0].y() + cornerPoints[2].y()) / 2) def _rotate(self, point, cosRot, sinRot): return QgsPointXY(point.x() * cosRot - point.y() * sinRot, point.x() * sinRot + point.y() * cosRot) def createMapRenderer(self, rendererContext): return FreehandRasterGeoreferencerLayerRenderer(self, rendererContext) def setBlendModeByName(self, modeName): self.blendModeName = modeName blendMode = getattr(QPainter, "CompositionMode_" + modeName, 0) self.setBlendMode(blendMode) self.setCustomProperty("blendMode", modeName) def setTransparency(self, transparency): self.transparency = transparency self.setCustomProperty("transparency", transparency) def draw(self, renderContext): if renderContext.extent().isEmpty(): qDebug("Drawing is skipped because map extent is empty.") return True self.initializeLayer() if not self.initialized: qDebug("Drawing is skipped because nothing to draw.") return True painter = renderContext.painter() painter.save() self.prepareStyle(painter) self.drawRaster(renderContext) painter.restore() return True def drawRaster(self, renderContext): painter = renderContext.painter() self.map2pixel = renderContext.mapToPixel() scaleX = self.xScale / self.map2pixel.mapUnitsPerPixel() scaleY = self.yScale / self.map2pixel.mapUnitsPerPixel() rect = QRectF(QPointF(-self.image.width() / 2.0, - self.image.height() / 2.0), QPointF(self.image.width() / 2.0, self.image.height() / 2.0)) mapCenter = self.map2pixel.transform(self.center) # draw the image on the map canvas painter.translate(QPoint(round(mapCenter.x()), round(mapCenter.y()))) painter.rotate(self.rotation) painter.scale(scaleX, scaleY) painter.drawImage(rect, self.image) painter.setBrush(Qt.NoBrush) painter.setPen(QColor(0, 0, 0)) painter.drawRect(rect) def prepareStyle(self, painter): painter.setOpacity(1.0 - self.transparency / 100.0) def readXml(self, node, context): self.readCustomProperties(node) self.title = self.customProperty("title", "") self.filepath = self.customProperty("filepath", "") self.xScale = float(self.customProperty("xScale", 1.0)) self.yScale = float(self.customProperty("yScale", 1.0)) self.rotation = float(self.customProperty("rotation", 0.0)) xCenter = float(self.customProperty("xCenter", 0.0)) yCenter = float(self.customProperty("yCenter", 0.0)) self.center = QgsPointXY(xCenter, yCenter) self.setTransparency(int(self.customProperty( "transparency", LayerDefaultSettings.TRANSPARENCY))) self.setBlendModeByName(self.customProperty( "blendMode", LayerDefaultSettings.BLEND_MODE)) return True def writeXml(self, node, doc, context): element = node.toElement() self.writeCustomProperties(node, doc) element.setAttribute("type", "plugin") element.setAttribute( "name", FreehandRasterGeoreferencerLayer.LAYER_TYPE) return True def metadata(self): lines = [] fmt = "%s:\t%s" lines.append(fmt % (self.tr("Title"), self.title)) filepath = self.getAbsoluteFilepath() filepath = os.path.normpath(filepath) lines.append(fmt % (self.tr("Path"), filepath)) lines.append(fmt % (self.tr("Image Width"), str(self.image.width()))) lines.append(fmt % (self.tr("Image Height"), str(self.image.height()))) lines.append(fmt % (self.tr("Rotation (CW)"), str(self.rotation))) lines.append(fmt % (self.tr("X center"), str(self.center.x()))) lines.append(fmt % (self.tr("Y center"), str(self.center.y()))) lines.append(fmt % (self.tr("X scale"), str(self.xScale))) lines.append(fmt % (self.tr("Y scale"), str(self.yScale))) return "\n".join(lines) def log(self, msg): qDebug(msg) def dump(self, detail=False, bbox=None): pass def showStatusMessage(self, msg, timeout): self.iface.mainWindow().statusBar().showMessage(msg, timeout) def showBarMessage(self, title, text, level, duration): self.iface.messageBar().pushMessage(title, text, level, duration) def transparencyChanged(self, val): QgsProject.instance().setDirty(True) self.setTransparency(val) self.repaintRequested.emit()
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.PrmInputLayer, context) measureTotal = self.parameterAsBool(parameters, self.PrmMeasureTotalLength, context) units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure, context) autoStyle = self.parameterAsBool(parameters, self.PrmAutomaticStyline, context) srcCRS = source.sourceCrs() f = QgsFields() f.append(QgsField("label", QVariant.String)) f.append(QgsField("distance", QVariant.Double)) f.append(QgsField("units", QVariant.String)) if not measureTotal: f.append(QgsField("heading_to", QVariant.Double)) f.append(QgsField("total_distance", QVariant.Double)) (sink, dest_id) = self.parameterAsSink( parameters, self.PrmOutputLayer, context, f, QgsWkbTypes.LineString, srcCRS) if srcCRS != epsg4326: geomTo4326 = QgsCoordinateTransform(srcCRS, epsg4326, QgsProject.instance()) toSinkCrs = QgsCoordinateTransform(epsg4326, srcCRS, QgsProject.instance()) wkbtype = source.wkbType() geomtype = QgsWkbTypes.geometryType(wkbtype) featureCount = source.featureCount() total = 100.0 / featureCount if featureCount else 0 iterator = source.getFeatures() for cnt, feature in enumerate(iterator): if feedback.isCanceled(): break if geomtype == QgsWkbTypes.LineGeometry: if feature.geometry().isMultipart(): ptdata = [feature.geometry().asMultiPolyline()] else: ptdata = [[feature.geometry().asPolyline()]] else: #polygon if feature.geometry().isMultipart(): ptdata = feature.geometry().asMultiPolygon() else: ptdata = [feature.geometry().asPolygon()] if len(ptdata) < 1: continue for seg in ptdata: if len(seg) < 1: continue if measureTotal: for pts in seg: numpoints = len(pts) if numpoints < 2: continue f = QgsFeature() f.setGeometry(QgsGeometry.fromPolylineXY(pts)) ptStart = QgsPointXY(pts[0].x(), pts[0].y()) if srcCRS != epsg4326: # Convert to 4326 ptStart = geomTo4326.transform(ptStart) # Calculate the total distance of this line segment distance = 0.0 for x in range(1,numpoints): ptEnd = QgsPointXY(pts[x].x(), pts[x].y()) if srcCRS != epsg4326: # Convert to 4326 ptEnd = geomTo4326.transform(ptEnd) l = geod.Inverse(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) distance += l['s12'] ptStart = ptEnd distance = self.unitDistance(units, distance) # Distance converted to the selected unit of measure attr = ["{:.2f} {}".format(distance, unitsAbbr[units]), distance, unitsAbbr[units] ] f.setAttributes(attr) sink.addFeature(f) else: for pts in seg: numpoints = len(pts) if numpoints < 2: continue ptStart = QgsPointXY(pts[0].x(), pts[0].y()) if srcCRS != epsg4326: # Convert to 4326 ptStart = geomTo4326.transform(ptStart) # Calculate the total distance of this line segment totalDistance = 0.0 for x in range(1,numpoints): ptEnd = QgsPointXY(pts[x].x(), pts[x].y()) if srcCRS != epsg4326: # Convert to 4326 ptEnd = geomTo4326.transform(ptEnd) l = geod.Inverse(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) totalDistance += l['s12'] ptStart = ptEnd totalDistance = self.unitDistance(units, totalDistance) # Distance converted to the selected unit of measure ptStart = QgsPointXY(pts[0].x(), pts[0].y()) if srcCRS != epsg4326: # Convert to 4326 pt1 = geomTo4326.transform(ptStart) else: pt1 = ptStart for x in range(1,numpoints): ptEnd = QgsPointXY(pts[x].x(), pts[x].y()) f = QgsFeature() f.setGeometry(QgsGeometry.fromPolylineXY([ptStart, ptEnd])) if srcCRS != epsg4326: # Convert to 4326 pt2 = geomTo4326.transform(ptEnd) else: pt2 = ptEnd l = geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x()) ptStart = ptEnd pt1 = pt2 distance = self.unitDistance(units, l['s12']) attr = ["{:.2f} {}".format(distance, unitsAbbr[units]), distance, unitsAbbr[units], l['azi1'],totalDistance ] f.setAttributes(attr) sink.addFeature(f) if cnt % 100 == 0: feedback.setProgress(int(cnt * total)) if autoStyle and context.willLoadLayerOnCompletion(dest_id): context.layerToLoadOnCompletionDetails(dest_id).setPostProcessor(StylePostProcessor.create()) return {self.PrmOutputLayer: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) fields = QgsFields() fields.append(QgsField('POINTA', QVariant.Double, '', 24, 15)) fields.append(QgsField('POINTB', QVariant.Double, '', 24, 15)) fields.append(QgsField('POINTC', QVariant.Double, '', 24, 15)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) pts = [] ptDict = {} ptNdx = -1 c = voronoi.Context() features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, inFeat in enumerate(features): if feedback.isCanceled(): break geom = QgsGeometry(inFeat.geometry()) if geom.isNull(): continue if geom.isMultipart(): points = geom.asMultiPoint() else: points = [geom.asPoint()] for n, point in enumerate(points): x = point.x() y = point.y() pts.append((x, y)) ptNdx += 1 ptDict[ptNdx] = (inFeat.id(), n) feedback.setProgress(int(current * total)) if len(pts) < 3: raise QgsProcessingException( self.tr('Input file should contain at least 3 points. Choose ' 'another file and try again.')) uniqueSet = set(item for item in pts) ids = [pts.index(item) for item in uniqueSet] sl = voronoi.SiteList([voronoi.Site(*i) for i in uniqueSet]) c.triangulate = True voronoi.voronoi(sl, c) triangles = c.triangles feat = QgsFeature() total = 100.0 / len(triangles) if triangles else 1 for current, triangle in enumerate(triangles): if feedback.isCanceled(): break indices = list(triangle) indices.append(indices[0]) polygon = [] attrs = [] step = 0 for index in indices: fid, n = ptDict[ids[index]] request = QgsFeatureRequest().setFilterFid(fid) inFeat = next(source.getFeatures(request)) geom = QgsGeometry(inFeat.geometry()) if geom.isMultipart(): point = QgsPointXY(geom.asMultiPoint()[n]) else: point = QgsPointXY(geom.asPoint()) polygon.append(point) if step <= 3: attrs.append(ids[index]) step += 1 feat.setAttributes(attrs) geometry = QgsGeometry().fromPolygonXY([polygon]) feat.setGeometry(geometry) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) strategy = self.getParameterValue(self.STRATEGY) travelCost = self.getParameterValue(self.TRAVEL_COST) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint]) feedback.pushInfo(self.tr('Calculating service area...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = [] for i, v in enumerate(cost): if v > travelCost and tree[i] != -1: vertexId = graph.edge(tree[i]).outVertex() if cost[vertexId] <= travelCost: vertices.append(i) upperBoundary = [] lowerBoundary = [] for i in vertices: upperBoundary.append(graph.vertex(graph.edge(tree[i]).inVertex()).point()) lowerBoundary.append(graph.vertex(graph.edge(tree[i]).outVertex()).point()) feedback.pushInfo(self.tr('Writing results...')) fields = QgsFields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName( self.OUTPUT_POINTS).getVectorWriter(fields, QgsWkbTypes.MultiPoint, layer.crs(), context) feat.setGeometry(geomUpper) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) feat.setGeometry(geomLower) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer upperBoundary.append(startPoint) lowerBoundary.append(startPoint) geomUpper = QgsGeometry.fromMultiPoint(upperBoundary) geomLower = QgsGeometry.fromMultiPoint(lowerBoundary) writer = self.getOutputFromName( self.OUTPUT_POLYGON).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context) geom = geomUpper.convexHull() feat.setGeometry(geom) feat['type'] = 'upper' feat['start'] = startPoint.toString() writer.addFeature(feat) geom = geomLower.convexHull() feat.setGeometry(geom) feat['type'] = 'lower' feat['start'] = startPoint.toString() writer.addFeature(feat) del writer
def testChangeGeometry(self): # test changing geometries values from an edit buffer # make a layer with two features layer = createEmptyLayer() self.assertTrue(layer.startEditing()) # add two features f1 = QgsFeature(layer.fields(), 1) f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2))) f1.setAttributes(["test", 123]) self.assertTrue(layer.addFeature(f1)) f2 = QgsFeature(layer.fields(), 2) f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 4))) f2.setAttributes(["test2", 246]) self.assertTrue(layer.addFeature(f2)) layer.commitChanges() layer.startEditing() self.assertEqual(layer.editBuffer().changedGeometries(), {}) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) # change geometry layer.changeGeometry(1, QgsGeometry.fromPointXY(QgsPointXY(10, 20))) # test contents of buffer self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 10) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.undoStack().count(), 1) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 10) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) # apply second change to same feature layer.beginEditCommand( 'second change' ) # need to use an edit command to avoid the two geometry changes being merged layer.changeGeometry(1, QgsGeometry.fromPointXY(QgsPointXY(100, 200))) layer.endEditCommand() # test contents of buffer self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 100) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.undoStack().count(), 2) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 100) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) layer.changeGeometry(2, QgsGeometry.fromPointXY(QgsPointXY(20, 40))) # test contents of buffer self.assertEqual(set(layer.editBuffer().changedGeometries().keys()), set([1, 2])) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 100) self.assertEqual( layer.editBuffer().changedGeometries()[2].constGet().x(), 20) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.undoStack().count(), 3) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 100) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 20) layer.undoStack().undo() self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertEqual( layer.editBuffer().changedGeometries()[1].constGet().x(), 100) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 100) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) layer.undoStack().undo() self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), [1]) self.assertTrue(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 10) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2) layer.undoStack().undo() self.assertEqual(list(layer.editBuffer().changedGeometries().keys()), []) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(1)) self.assertFalse(layer.editBuffer().isFeatureGeometryChanged(2)) self.assertEqual(layer.getFeature(1).geometry().constGet().x(), 1) self.assertEqual(layer.getFeature(2).geometry().constGet().x(), 2)
def mousePressEvent(self, event): """ :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return if event.button() == Qt.LeftButton: self.snapped = True self.pressPos = self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(100, self) if (not vut.IsPointOnScreen(event.x(), event.y(), self.surface)): self.UpdateSurface() return # point drawer if self.gt is not None and self._interaction.pointDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) pointIndex = len(self.drawPtPos) + 1 AddDrawPointOnMap(pointIndex, Longitude, Latitude, Altitude) self.drawPtPos.append([Longitude, Latitude, Altitude]) # polygon drawer if self.gt is not None and self._interaction.polygonDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.poly_RubberBand.addPoint(QgsPointXY(Longitude, Latitude)) self.poly_coordinates.extend(QgsPointXY(Longitude, Latitude)) self.drawPolygon.append([Longitude, Latitude, Altitude]) # line drawer if self.gt is not None and self._interaction.lineDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawLines.append([Longitude, Latitude, Altitude]) AddDrawLineOnMap(self.drawLines) if self._interaction.objectTracking: self.origin = event.pos() self.Tracking_RubberBand.setGeometry( QRect(self.origin, QSize())) self.Tracking_RubberBand.show() if self._interaction.censure: self.origin = event.pos() self.Censure_RubberBand.setGeometry(QRect( self.origin, QSize())) self.Censure_RubberBand.show() # Ruler drawer if self.gt is not None and self._interaction.ruler: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawRuler.append([Longitude, Latitude, Altitude]) # if not called, the paint event is not triggered. self.UpdateSurface()
def import_gpx_file(self, file_path, output_directory, attribute_select="Last", use_wgs84=True, calculate_motion_attributes=False, overwrite=False): """ Imports the data from the GPX file and create the vector layer """ if len(self.attribute_definitions) == 0: self.get_table_data(file_path) self.error_message = '' if calculate_motion_attributes: self.attribute_definitions.append( DataTypeDefinition('_distance', DataTypes.Double, True, '')) self.attribute_definitions.append( DataTypeDefinition('_duration', DataTypes.Double, True, '')) self.attribute_definitions.append( DataTypeDefinition('_speed', DataTypes.Double, True, '')) tree = ElementTree.parse(file_path) root = tree.getroot() crs = QgsCoordinateReferenceSystem('EPSG:4326') if use_wgs84 else None vector_layer_builder = GpxFeatureBuilder(os.path.basename(file_path), self.attribute_definitions, attribute_select, crs) prev_track_point = None self.equal_coordintes = 0 for track in root.findall('gpx:trk', self.namespace): track_segment = track.find('gpx:trkseg', self.namespace) for track_point in track_segment.findall('gpx:trkpt', self.namespace): if prev_track_point is not None: previous_point = QgsPointXY( float(prev_track_point.get('lon')), float(prev_track_point.get('lat'))) new_point = QgsPointXY(float(track_point.get('lon')), float(track_point.get('lat'))) if GeomTools.is_equal_coordinate(previous_point, new_point): self.equal_coordintes += 1 continue # add a feature with first/last/both attributes attributes = dict() if attribute_select == 'First': self.add_attributes(attributes, prev_track_point, '') elif attribute_select == 'Last': self.add_attributes(attributes, track_point, '') elif attribute_select == 'Both': self.add_attributes(attributes, prev_track_point, 'a_') self.add_attributes(attributes, track_point, 'b_') if calculate_motion_attributes: time_a = DataTypes.create_date( prev_track_point.find('gpx:time', self.namespace).text) time_b = DataTypes.create_date( track_point.find('gpx:time', self.namespace).text) attributes['_distance'] = GeomTools.distance( previous_point, new_point, crs) if time_a is not None or time_b is not None: attributes[ '_duration'] = GeomTools.calculate_duration( time_a, time_b) attributes['_speed'] = GeomTools.calculate_speed( time_a, time_b, previous_point, new_point, crs) vector_layer_builder.add_feature( [previous_point, new_point], attributes) prev_track_point = track_point vector_layer = vector_layer_builder.save_layer(output_directory, overwrite) if vector_layer_builder.error_message != '': self.error_message = vector_layer_builder.error_message print(self.error_message) return vector_layer
def create_point(self, centroid, radian, radius): dx = math.cos(radian) * radius dy = math.sin(radian) * radius return QgsPointXY(centroid.x() + dx, centroid.y() + dy)
def ovals(self, sink, source, width, height, rotation, segments, feedback): features = source.getFeatures() ft = QgsFeature() total = 100.0 / source.featureCount() if source.featureCount() else 0 if rotation >= 0: for current, feat in enumerate(features): if feedback.isCanceled(): break if not feat.hasGeometry(): continue w = feat[width] h = feat[height] angle = feat[rotation] # block 0/NULL width or height, but allow 0 as angle value if not w or not h: feedback.pushInfo( QCoreApplication.translate( 'RectanglesOvalsDiamondsVariable', 'Feature {} has empty ' 'width or height. ' 'Skipping…').format(feat.id())) continue if angle == NULL: feedback.pushInfo( QCoreApplication.translate( 'RectanglesOvalsDiamondsVariable', 'Feature {} has empty ' 'angle. ' 'Skipping…').format(feat.id())) continue xOffset = w / 2.0 yOffset = h / 2.0 phi = angle * math.pi / 180 point = feat.geometry().asPoint() x = point.x() y = point.y() points = [] for t in [(2 * math.pi) / segments * i for i in range(segments)]: points.append( (xOffset * math.cos(t), yOffset * math.sin(t))) polygon = [[ QgsPointXY( i[0] * math.cos(phi) + i[1] * math.sin(phi) + x, -i[0] * math.sin(phi) + i[1] * math.cos(phi) + y) for i in points ]] ft.setGeometry(QgsGeometry.fromPolygonXY(polygon)) ft.setAttributes(feat.attributes()) sink.addFeature(ft, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) else: for current, feat in enumerate(features): if feedback.isCanceled(): break if not feat.hasGeometry(): continue w = feat[width] h = feat[height] if not w or not h: feedback.pushInfo( QCoreApplication.translate( 'RectanglesOvalsDiamondsVariable', 'Feature {} has empty ' 'width or height. ' 'Skipping…').format(feat.id())) continue xOffset = w / 2.0 yOffset = h / 2.0 point = feat.geometry().asPoint() x = point.x() y = point.y() points = [] for t in [(2 * math.pi) / segments * i for i in range(segments)]: points.append( (xOffset * math.cos(t), yOffset * math.sin(t))) polygon = [[QgsPointXY(i[0] + x, i[1] + y) for i in points]] ft.setGeometry(QgsGeometry.fromPolygonXY(polygon)) ft.setAttributes(feat.attributes()) sink.addFeature(ft, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total))
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context) startPoint = self.getParameterValue(self.START_POINT) endPoint = self.getParameterValue(self.END_POINT) strategy = self.getParameterValue(self.STRATEGY) directionFieldName = self.getParameterValue(self.DIRECTION_FIELD) forwardValue = self.getParameterValue(self.VALUE_FORWARD) backwardValue = self.getParameterValue(self.VALUE_BACKWARD) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) bothValue = self.getParameterValue(self.VALUE_BOTH) defaultDirection = self.getParameterValue(self.DEFAULT_DIRECTION) speedFieldName = self.getParameterValue(self.SPEED_FIELD) defaultSpeed = self.getParameterValue(self.DEFAULT_SPEED) tolerance = self.getParameterValue(self.TOLERANCE) fields = QgsFields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) writer = self.getOutputFromName( self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.LineString, layer.crs(), context) tmp = startPoint.split(',') startPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) tmp = endPoint.split(',') endPoint = QgsPointXY(float(tmp[0]), float(tmp[1])) directionField = -1 if directionFieldName is not None: directionField = layer.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName is not None: speedField = layer.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(layer, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = iface.mapCanvas().mapSettings().destinationCrs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(iface.mapCanvas().mapSettings().destinationCrs(), True, tolerance) feedback.pushInfo(self.tr('Building graph...')) snappedPoints = director.makeGraph(builder, [startPoint, endPoint]) feedback.pushInfo(self.tr('Calculating shortest path...')) graph = builder.graph() idxStart = graph.findVertex(snappedPoints[0]) idxEnd = graph.findVertex(snappedPoints[1]) tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: raise GeoAlgorithmExecutionException( self.tr('There is no route from start point to end point.')) route = [] cost = 0.0 current = idxEnd while current != idxStart: cost += graph.edge(tree[current]).cost(0) route.append(graph.vertex(graph.edge(tree[current]).inVertex()).point()) current = graph.edge(tree[current]).outVertex() route.append(snappedPoints[0]) route.reverse() self.setOutputValue(self.TRAVEL_COST, cost / multiplier) feedback.pushInfo(self.tr('Writing results...')) geom = QgsGeometry.fromPolyline(route) feat = QgsFeature() feat.setFields(fields) feat['start'] = startPoint.toString() feat['end'] = endPoint.toString() feat['cost'] = cost / multiplier feat.setGeometry(geom) writer.addFeature(feat) del writer
def TestQgsPointXYRepr(self): p = QgsPointXY(123.456, 987.654) self.assertTrue(p.__repr__().startswith('<QgsPointXY: POINT(123.456'))
def TestQgsGeometryRepr(self): p = QgsPointXY(123.456, 987.654) g = QgsGeometry.fromPointXY(p) self.assertTrue( g.__repr__().startswith('<QgsGeometry: Point (123.456)'))
def moveCenterFromPointRotate(self, startPoint, rotation, xScale, yScale): cornerPoints = self.transformedCornerCoordinatesFromPoint( startPoint, rotation, xScale, yScale) self.center = QgsPointXY((cornerPoints[0].x( ) + cornerPoints[2].x()) / 2, (cornerPoints[0].y() + cornerPoints[2].y()) / 2)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) pointCount = self.parameterAsDouble(parameters, self.POINTS_NUMBER, context) minDistance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) bbox = source.sourceExtent() sourceIndex = QgsSpatialIndex(source, feedback) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, source.sourceCrs(), QgsFeatureSink.RegeneratePrimaryKey) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) nPoints = 0 nIterations = 0 maxIterations = pointCount * 200 total = 100.0 / pointCount if pointCount else 1 index = QgsSpatialIndex() points = dict() random.seed() while nIterations < maxIterations and nPoints < pointCount: if feedback.isCanceled(): break rx = bbox.xMinimum() + bbox.width() * random.random() ry = bbox.yMinimum() + bbox.height() * random.random() p = QgsPointXY(rx, ry) geom = QgsGeometry.fromPointXY(p) ids = sourceIndex.intersects(geom.buffer(5, 5).boundingBox()) if len(ids) > 0 and \ vector.checkMinDistance(p, index, minDistance, points): request = QgsFeatureRequest().setFilterFids( ids).setSubsetOfAttributes([]) for f in source.getFeatures(request): if feedback.isCanceled(): break tmpGeom = f.geometry() if geom.within(tmpGeom): f = QgsFeature(nPoints) f.initAttributes(1) f.setFields(fields) f.setAttribute('id', nPoints) f.setGeometry(geom) sink.addFeature(f, QgsFeatureSink.FastInsert) index.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 testDateTimeWriteShapefile(self): """Check writing date and time fields to an ESRI shapefile.""" ml = QgsVectorLayer( ('Point?crs=epsg:4326&field=id:int&' 'field=date_f:date&field=time_f:time&field=dt_f:datetime'), 'test', 'memory') self.assertTrue(ml.isValid()) provider = ml.dataProvider() self.assertIsNotNone(provider) ft = QgsFeature() ft.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 10))) ft.setAttributes([ 1, QDate(2014, 3, 5), QTime(13, 45, 22), QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22)) ]) res, features = provider.addFeatures([ft]) self.assertTrue(res) self.assertTrue(features) dest_file_name = os.path.join(str(QDir.tempPath()), 'datetime.shp') crs = QgsCoordinateReferenceSystem() crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, dest_file_name, 'utf-8', crs, 'ESRI Shapefile') self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') fields = created_layer.dataProvider().fields() self.assertEqual( fields.at(fields.indexFromName('date_f')).type(), QVariant.Date) # shapefiles do not support time types, result should be string self.assertEqual( fields.at(fields.indexFromName('time_f')).type(), QVariant.String) # shapefiles do not support datetime types, result should be string self.assertEqual( fields.at(fields.indexFromName('dt_f')).type(), QVariant.String) f = next(created_layer.getFeatures(QgsFeatureRequest())) date_idx = created_layer.fields().lookupField('date_f') self.assertIsInstance(f.attributes()[date_idx], QDate) self.assertEqual(f.attributes()[date_idx], QDate(2014, 3, 5)) time_idx = created_layer.fields().lookupField('time_f') # shapefiles do not support time types self.assertIsInstance(f.attributes()[time_idx], str) self.assertEqual(f.attributes()[time_idx], '13:45:22') # shapefiles do not support datetime types datetime_idx = created_layer.fields().lookupField('dt_f') self.assertIsInstance(f.attributes()[datetime_idx], str) self.assertEqual( f.attributes()[datetime_idx], QDateTime(QDate(2014, 3, 5), QTime(13, 45, 22)).toString("yyyy/MM/dd hh:mm:ss.zzz"))
def legend_test(self): self.atlas_map.setAtlasDriven(True) self.atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto) self.atlas_map.setAtlasMargin(0.10) # add a point layer ptLayer = QgsVectorLayer( "Point?crs=epsg:4326&field=attr:int(1)&field=label:string(20)", "points", "memory") pr = ptLayer.dataProvider() f1 = QgsFeature(1) f1.initAttributes(2) f1.setAttribute(0, 1) f1.setAttribute(1, "Test label 1") f1.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-0.638, 48.954))) f2 = QgsFeature(2) f2.initAttributes(2) f2.setAttribute(0, 2) f2.setAttribute(1, "Test label 2") f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(-1.682, 48.550))) pr.addFeatures([f1, f2]) # categorized symbology r = QgsCategorizedSymbolRenderer("attr", [ QgsRendererCategory( 1, QgsMarkerSymbol.createSimple({ "color": "255,0,0", 'outline_color': 'black' }), "red"), QgsRendererCategory( 2, QgsMarkerSymbol.createSimple({ "color": "0,0,255", 'outline_color': 'black' }), "blue") ]) ptLayer.setRenderer(r) QgsProject.instance().addMapLayer(ptLayer) # add the point layer to the map settings layers = self.layers layers = [ptLayer] + layers self.atlas_map.setLayers(layers) self.overview.setLayers(layers) # add a legend legend = QgsLayoutItemLegend(self.layout) legend.setTitle("Legend") legend.attemptMove(QgsLayoutPoint(200, 100)) # sets the legend filter parameter legend.setLinkedMap(self.atlas_map) legend.setLegendFilterOutAtlas(True) self.layout.addLayoutItem(legend) self.atlas.beginRender() self.atlas.seekTo(0) self.mLabel1.adjustSizeToText() checker = QgsLayoutChecker('atlas_legend', self.layout) myTestResult, myMessage = checker.testLayout() self.report += checker.report() self.assertTrue(myTestResult, myMessage) self.atlas.endRender() # restore state self.atlas_map.setLayers([layers[1]]) self.layout.removeLayoutItem(legend) QgsProject.instance().removeMapLayer(ptLayer.id())
def _hexagonGrid(self, sink, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback): ft = QgsFeature() # To preserve symmetry, hspacing is fixed relative to vspacing xVertexLo = 0.288675134594813 * vSpacing xVertexHi = 0.577350269189626 * vSpacing hSpacing = xVertexLo + xVertexHi hOverlay = hSpacing - hOverlay if hOverlay < 0: raise QgsProcessingException( self.tr('To preserve symmetry, hspacing is fixed relative to vspacing\n \ hspacing is fixed at: {0} and hoverlay is fixed at: {1}\n \ hoverlay cannot be negative. Increase hoverlay.').format(hSpacing, hOverlay) ) halfVSpacing = vSpacing / 2.0 columns = int(math.ceil(float(width) / hOverlay)) rows = int(math.ceil(float(height) / (vSpacing - vOverlay))) cells = rows * columns count_update = cells * 0.05 id = 1 count = 0 for col in range(columns): if feedback.isCanceled(): break # (column + 1) and (row + 1) calculation is used to maintain # topology between adjacent shapes and avoid overlaps/holes # due to rounding errors x1 = originX + (col * hOverlay) # far left x2 = x1 + (xVertexHi - xVertexLo) # left x3 = originX + (col * hOverlay) + hSpacing # right x4 = x3 + (xVertexHi - xVertexLo) # far right for row in range(rows): if (col % 2) == 0: y1 = originY + (row * vOverlay) - (((row * 2) + 0) * halfVSpacing) # hi y2 = originY + (row * vOverlay) - (((row * 2) + 1) * halfVSpacing) # mid y3 = originY + (row * vOverlay) - (((row * 2) + 2) * halfVSpacing) # lo else: y1 = originY + (row * vOverlay) - (((row * 2) + 1) * halfVSpacing) # hi y2 = originY + (row * vOverlay) - (((row * 2) + 2) * halfVSpacing) # mid y3 = originY + (row * vOverlay) - (((row * 2) + 3) * halfVSpacing) # lo polyline = [] polyline.append(QgsPointXY(x1, y2)) polyline.append(QgsPointXY(x2, y1)) polyline.append(QgsPointXY(x3, y1)) polyline.append(QgsPointXY(x4, y2)) polyline.append(QgsPointXY(x3, y3)) polyline.append(QgsPointXY(x2, y3)) polyline.append(QgsPointXY(x1, y2)) ft.setGeometry(QgsGeometry.fromPolygonXY([polyline])) ft.setAttributes([x1, y1, x4, y3, id]) sink.addFeature(ft, QgsFeatureSink.FastInsert) id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / cells * 100))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) weight_field = self.parameterAsString(parameters, self.WEIGHT, context) unique_field = self.parameterAsString(parameters, self.UID, context) attributes = [] if not weight_field: weight_index = -1 else: weight_index = source.fields().lookupField(weight_field) if weight_index >= 0: attributes.append(weight_index) if not unique_field: unique_index = -1 else: unique_index = source.fields().lookupField(unique_field) if unique_index >= 0: attributes.append(unique_index) field_list = QgsFields() field_list.append(QgsField('MEAN_X', QVariant.Double, '', 24, 15)) field_list.append(QgsField('MEAN_Y', QVariant.Double, '', 24, 15)) if unique_index >= 0: field_list.append(QgsField('UID', QVariant.String, '', 255)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, field_list, QgsWkbTypes.Point, source.sourceCrs()) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(attributes)) total = 100.0 / source.featureCount() if source.featureCount() else 0 means = {} for current, feat in enumerate(features): if feedback.isCanceled(): break feedback.setProgress(int(current * total)) if unique_index == -1: clazz = "Single class" else: clazz = str(feat.attributes()[unique_index]).strip() if weight_index == -1: weight = 1.00 else: try: weight = float(feat.attributes()[weight_index]) except: weight = 1.00 if weight < 0: raise QgsProcessingException( self. tr('Negative weight value found. Please fix your data and try again.' )) if clazz not in means: means[clazz] = (0, 0, 0) (cx, cy, totalweight) = means[clazz] geom = QgsGeometry(feat.geometry()) geom = vector.extractPoints(geom) for i in geom: cx += i.x() * weight cy += i.y() * weight totalweight += weight means[clazz] = (cx, cy, totalweight) current = 0 total = 100.0 / len(means) if means else 1 for (clazz, values) in list(means.items()): if feedback.isCanceled(): break outFeat = QgsFeature() cx = values[0] / values[2] cy = values[1] / values[2] meanPoint = QgsPointXY(cx, cy) outFeat.setGeometry(QgsGeometry.fromPoint(meanPoint)) attributes = [cx, cy] if unique_index >= 0: attributes.append(clazz) outFeat.setAttributes(attributes) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def balanced(features, graph, feedback, balance=0, min_colors=4): feature_colors = {} # start with minimum number of colors in pool color_pool = set(range(1, min_colors + 1)) # calculate count of neighbours neighbour_count = defaultdict(int) for feature_id, neighbours in graph.node_edge.items(): neighbour_count[feature_id] += len(neighbours) # sort features by neighbour count - we want to handle those with more neighbours first sorted_by_count = [feature_id for feature_id in sorted(neighbour_count.items(), key=operator.itemgetter(1), reverse=True)] # counts for each color already assigned color_counts = defaultdict(int) color_areas = defaultdict(float) for c in color_pool: color_counts[c] = 0 color_areas[c] = 0 total = 10.0 / len(sorted_by_count) i = 0 for (feature_id, n) in sorted_by_count: # first work out which already assigned colors are adjacent to this feature adjacent_colors = set() for neighbour in graph.node_edge[feature_id]: if neighbour in feature_colors: adjacent_colors.add(feature_colors[neighbour]) # from the existing colors, work out which are available (ie non-adjacent) available_colors = color_pool.difference(adjacent_colors) feature_color = -1 if len(available_colors) == 0: # no existing colors available for this feature, so add new color to pool and repeat min_colors += 1 return ColoringAlgorithm.balanced(features, graph, feedback, balance, min_colors) else: if balance == 0: # choose least used available color counts = [(c, v) for c, v in color_counts.items() if c in available_colors] feature_color = sorted(counts, key=operator.itemgetter(1))[0][0] color_counts[feature_color] += 1 elif balance == 1: areas = [(c, v) for c, v in color_areas.items() if c in available_colors] feature_color = sorted(areas, key=operator.itemgetter(1))[0][0] color_areas[feature_color] += features[feature_id].geometry().area() elif balance == 2: min_distances = {c: sys.float_info.max for c in available_colors} this_feature_centroid = QgsPointXY(features[feature_id].geometry().centroid().geometry()) # find features for all available colors other_features = {f_id: c for (f_id, c) in feature_colors.items() if c in available_colors} # loop through these, and calculate the minimum distance from this feature to the nearest # feature with each assigned color for other_feature_id, c in other_features.items(): other_geometry = features[other_feature_id].geometry() other_centroid = QgsPointXY(other_geometry.centroid().geometry()) distance = this_feature_centroid.distanceSquared(other_centroid) if distance < min_distances[c]: min_distances[c] = distance # choose color such that minimum distance is maximised! ie we want MAXIMAL separation between # features with the same color feature_color = sorted(min_distances, key=min_distances.__getitem__, reverse=True)[0] feature_colors[feature_id] = feature_color i += 1 feedback.setProgress(70 + int(i * total)) return feature_colors
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs()) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) fields = startPoints.fields() fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 20, 7)) feat = QgsFeature() feat.setFields(fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) multiplier = 3600 director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo( QCoreApplication.translate('ShortestPathLayerToPoint', 'Loading start points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount( ) else 0 points = [endPoint] source_attributes = {} i = 1 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo( QCoreApplication.translate('ShortestPathLayerToPoint', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo( QCoreApplication.translate('ShortestPathLayerToPoint', 'Calculating shortest paths…')) graph = builder.graph() idxEnd = graph.findVertex(snappedPoints[0]) nPoints = len(snappedPoints) total = 100.0 / nPoints if nPoints else 1 for i in range(1, nPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) tree, costs = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) if tree[idxEnd] == -1: msg = self.tr( 'There is no route from start point ({}) to end point ({}).' .format(points[i].toString(), endPoint.toString())) feedback.reportError(msg) # add feature with no geometry feat.clearGeometry() attrs = source_attributes[i] attrs.append(points[i].toString()) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) continue route = [graph.vertex(idxEnd).point()] cost = costs[idxEnd] current = idxEnd while current != idxStart: current = graph.edge(tree[current]).fromVertex() route.append(graph.vertex(current).point()) route.reverse() geom = QgsGeometry.fromPolylineXY(route) feat.setGeometry(geom) attrs = source_attributes[i] attrs.extend( [points[i].toString(), endPoint.toString(), cost / multiplier]) feat.setAttributes(attrs) sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): self.parameters = parameters self.context = context self.feedback = feedback load_geotiffs = self.parameterAsInt(parameters, self.PrmLoadGeoTiffs, context) out_folder = self.parameterAsFile(parameters, self.PrmGroundOverlayFolder, context) input_file = self.parameterAsFile(parameters, self.PrmInput, context) f, extension = os.path.splitext(input_file) dirname = os.path.dirname(input_file) extension = extension.lower() try: if extension == '.kmz': kmz = ZipFile(input_file, 'r') kml = kmz.open('doc.kml', 'r') elif extension == '.kml': kml = open(input_file, encoding="utf-8", errors="backslashreplace") else: msg = "Invalid extension: Should be kml or kmz" raise QgsProcessingException(msg) except Exception: msg = "Failed to open file" raise QgsProcessingException(msg) parser = xml.sax.make_parser() self.overlays = [] # Set up the handler for doing the main processing handler = GroundOverlayHandler(feedback) handler.groundoverlay.connect(self.groundoverlay) parser.setContentHandler(handler) try: input_source = xml.sax.xmlreader.InputSource() input_source.setByteStream(kml) input_source.setEncoding('utf-8') parser.parse(input_source) except Exception: '''s = traceback.format_exc() feedback.pushInfo(s)''' feedback.reportError( tr('Failure in kml extraction - May return partial results.')) handler.endDocument() # Iterate through each found overlay images for overlay in self.overlays: north = overlay[0] south = overlay[1] east = overlay[2] west = overlay[3] rotation = overlay[4] href = overlay[5] if href.startswith('http:') or href.startswith('https:'): feedback.reportError( 'Cannot process network images: {}'.format(href)) continue if extension == '.kmz': try: image = kmz.read(href) output_file = os.path.basename(href) file_name, ext = os.path.splitext(output_file) # Write out a temporary image temp_file_name = os.path.join( out_folder, '{}_temp{}'.format(file_name, ext)) fp = open(temp_file_name, "wb") fp.write(image) fp.close() raster = QgsRasterLayer(temp_file_name, "temp") except Exception: feedback.reportError( 'Image does not exist: {}'.format(href)) continue else: # Check to see if it is a valid file name in_path = os.path.join(dirname, href) if not os.path.isfile(in_path): # The path was not valid feedback.reportError( 'Image file does not exist: {}'.format(in_path)) continue raster = QgsRasterLayer(in_path, "temp") output_file = os.path.basename(in_path) file_name, ext = os.path.splitext(output_file) if not raster.isValid(): feedback.reportError('Invalid raster image: {}'.format(href)) continue out_path = os.path.join(out_folder, file_name + ".tif") if rotation == 0: status = processing.run( "gdal:translate", { 'INPUT': raster, 'EXTRA': '-a_srs EPSG:4326 -a_ullr {} {} {} {}'.format( west, north, east, south), 'DATA_TYPE': 0, 'OUTPUT': out_path }) else: rwidth = raster.width() rheight = raster.height() center_x = (east + west) / 2.0 center_y = (north + south) / 2.0 center_pt = QgsPointXY(center_x, center_y) ul_pt = QgsPointXY(west, north) ur_pt = QgsPointXY(east, north) lr_pt = QgsPointXY(east, south) ll_pt = QgsPointXY(west, south) distance = center_pt.distance(ul_pt) az = center_pt.azimuth(ul_pt) - rotation pt1 = center_pt.project(distance, az) az = center_pt.azimuth(ur_pt) - rotation pt2 = center_pt.project(distance, az) az = center_pt.azimuth(lr_pt) - rotation pt3 = center_pt.project(distance, az) az = center_pt.azimuth(ll_pt) - rotation pt4 = center_pt.project(distance, az) gcp1 = '-gcp {} {} {} {}'.format(0, 0, pt1.x(), pt1.y()) gcp2 = '-gcp {} {} {} {}'.format(rwidth, 0, pt2.x(), pt2.y()) gcp3 = '-gcp {} {} {} {}'.format(rwidth, rheight, pt3.x(), pt3.y()) gcp4 = '-gcp {} {} {} {}'.format(0, rheight, pt4.x(), pt4.y()) status = processing.run( "gdal:translate", { 'INPUT': raster, 'EXTRA': '-a_srs EPSG:4326 -a_nodata 0,0,0 {} {} {} {}'.format( gcp1, gcp2, gcp3, gcp4), 'DATA_TYPE': 0, 'OUTPUT': out_path }) if load_geotiffs: context.addLayerToLoadOnCompletion( out_path, context.LayerDetails(file_name, project=context.project())) del raster if extension == '.kmz': try: os.remove(temp_file_name) os.remove(temp_file_name + '.aux.xml') except Exception: pass if extension == '.kmz': kmz.close() else: kml.close() # self.feedback.pushInfo('Number of overlays: {}'.format(len(self.overlays))) return ({})
def setUp(self): self.mPoint = QgsPointXY(10.0, 10.0)
def testCreateFeature(self): """ test creating a feature respecting defaults and constraints """ layer = QgsVectorLayer( "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # add a bunch of features f = QgsFeature() f.setAttributes(["test", 123, 1.0]) f1 = QgsFeature(2) f1.setAttributes(["test_1", 124, 1.1]) f2 = QgsFeature(3) f2.setAttributes(["test_2", 125, 2.4]) f3 = QgsFeature(4) f3.setAttributes(["test_3", 126, 1.7]) f4 = QgsFeature(5) f4.setAttributes(["superpig", 127, 0.8]) self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4])) # no layer self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid()) # basic tests f = QgsVectorLayerUtils.createFeature(layer) self.assertTrue(f.isValid()) self.assertEqual(f.fields(), layer.fields()) self.assertFalse(f.hasGeometry()) self.assertEqual(f.attributes(), [NULL, NULL, NULL]) # set geometry g = QgsGeometry.fromPointXY(QgsPointXY(100, 200)) f = QgsVectorLayerUtils.createFeature(layer, g) self.assertTrue(f.hasGeometry()) self.assertEqual(f.geometry().asWkt(), g.asWkt()) # using attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'a', 2: 6.0 }) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4')) f = QgsVectorLayerUtils.createFeature(layer) self.assertEqual(f.attributes(), [NULL, NULL, 12]) # we do not expect the default value expression to take precedence over the attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'a', 2: 6.0 }) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression based on geometry layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x')) f = QgsVectorLayerUtils.createFeature(layer, g) #adjusted so that input value and output feature are the same self.assertEqual(f.attributes(), [NULL, NULL, 300.0]) layer.setDefaultValueDefinition(2, QgsDefaultValue(None)) # test with violated unique constraints layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique) f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128 self.assertEqual(f.attributes(), ['test_1', 128, NULL]) layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique) # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) self.assertEqual(f.attributes(), ['test_4', 128, NULL]) # test with violated unique constraints and default value expression providing unique value layer.setDefaultValueDefinition(1, QgsDefaultValue('130')) f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value self.assertEqual(f.attributes(), ['test_4', 130, NULL]) # fallback: test with violated unique constraints and default value expression providing already existing value # add the feature with the default value: self.assertTrue(layer.dataProvider().addFeatures([f])) f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value # and since the default value providing an already existing value (130) it generates a unique value (next int: 131) self.assertEqual(f.attributes(), ['test_5', 131, NULL]) layer.setDefaultValueDefinition(1, QgsDefaultValue(None)) # test with manually correct unique constraint f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 132 }) self.assertEqual(f.attributes(), ['test_5', 132, NULL]) """ test creating a feature respecting unique values of postgres provider """ layer = QgsVectorLayer( "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # init connection string dbconn = 'dbname=\'qgis_test\'' if 'QGIS_PGTEST_DB' in os.environ: dbconn = os.environ['QGIS_PGTEST_DB'] # create a vector layer pg_layer = QgsVectorLayer( '{} table="qgis_test"."authors" sql='.format(dbconn), "authors", "postgres") self.assertTrue(pg_layer.isValid()) # check the default clause default_clause = 'nextval(\'qgis_test.authors_pk_seq\'::regclass)' self.assertEqual(pg_layer.dataProvider().defaultValueClause(0), default_clause) # though default_clause is after the first create not unique (until save), it should fill up all the features with it pg_layer.startEditing() f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) self.assertTrue( QgsVectorLayerUtils.valueExists(pg_layer, 0, default_clause)) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) # if a unique value is passed, use it f = QgsVectorLayerUtils.createFeature(pg_layer, attributes={ 0: 40, 1: NULL }) self.assertEqual(f.attributes(), [40, NULL]) # and if a default value is configured use it as well pg_layer.setDefaultValueDefinition(0, QgsDefaultValue('11*4')) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [44, NULL]) pg_layer.rollBack()
def testMeasureLineProjectedWorldPoints(self): # +-+ # | | # +-+ + # checking returned length_mapunits/projected_points of diffferent world points with results from SpatiaLite ST_Project da_3068 = QgsDistanceArea() da_3068.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3068'), QgsProject.instance().transformContext()) if (da_3068.sourceCrs().isGeographic()): da_3068.setEllipsoid(da_3068.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_3068.sourceCrs().authid(), 'EPSG:3068') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:3068', da_3068.sourceCrs().authid(), da_3068.sourceCrs().description(), da_3068.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_3068.lengthUnits()), da_3068.sourceCrs().projectionAcronym(), da_3068.sourceCrs().ellipsoidAcronym()))) da_wsg84 = QgsDistanceArea() da_wsg84.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326'), QgsProject.instance().transformContext()) if (da_wsg84.sourceCrs().isGeographic()): da_wsg84.setEllipsoid(da_wsg84.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_wsg84.sourceCrs().authid(), 'EPSG:4326') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}] ellipsoid[{}]".format(u'EPSG:4326', da_wsg84.sourceCrs().authid(), da_wsg84.sourceCrs().description(), da_wsg84.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_wsg84.lengthUnits()), da_wsg84.sourceCrs().projectionAcronym(), da_wsg84.sourceCrs().ellipsoidAcronym(), da_wsg84.ellipsoid()))) da_4314 = QgsDistanceArea() da_4314.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4314'), QgsProject.instance().transformContext()) if (da_4314.sourceCrs().isGeographic()): da_4314.setEllipsoid(da_4314.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_4314.sourceCrs().authid(), 'EPSG:4314') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:4314', da_4314.sourceCrs().authid(), da_4314.sourceCrs().description(), da_4314.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_4314.lengthUnits()), da_4314.sourceCrs().projectionAcronym(), da_4314.sourceCrs().ellipsoidAcronym()))) da_4805 = QgsDistanceArea() da_4805.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4805'), QgsProject.instance().transformContext()) if (da_4805.sourceCrs().isGeographic()): da_4805.setEllipsoid(da_4805.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_4805.sourceCrs().authid(), 'EPSG:4805') print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:4805', da_4805.sourceCrs().authid(), da_4805.sourceCrs().description(), da_4805.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_4805.lengthUnits()), da_4805.sourceCrs().projectionAcronym(), da_4805.sourceCrs().ellipsoidAcronym()))) # EPSG:5665 unknown, why? da_5665 = QgsDistanceArea() da_5665.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:5665'), QgsProject.instance().transformContext()) if (da_5665.sourceCrs().isGeographic()): da_5665.setEllipsoid(da_5665.sourceCrs().ellipsoidAcronym()) print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:5665', da_5665.sourceCrs().authid(), da_5665.sourceCrs().description(), da_5665.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_5665.lengthUnits()), da_5665.sourceCrs().projectionAcronym(), da_5665.sourceCrs().ellipsoidAcronym()))) #self.assertEqual(da_5665.sourceCrs().authid(), 'EPSG:5665') da_25833 = QgsDistanceArea() da_25833.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:25833'), QgsProject.instance().transformContext()) if (da_25833.sourceCrs().isGeographic()): da_25833.setEllipsoid(da_25833.sourceCrs().ellipsoidAcronym()) print(("setting [{}] srid [{}] description [{}] isGeographic[{}] lengthUnits[{}] projectionAcronym[{}] ellipsoidAcronym[{}]".format(u'EPSG:25833', da_25833.sourceCrs().authid(), da_25833.sourceCrs().description(), da_25833.sourceCrs().isGeographic(), QgsUnitTypes.toString(da_25833.lengthUnits()), da_25833.sourceCrs().projectionAcronym(), da_25833.sourceCrs().ellipsoidAcronym()))) self.assertEqual(da_25833.sourceCrs().authid(), 'EPSG:25833') # Berlin - Brandenburg Gate - Quadriga point_berlin_3068 = QgsPointXY(23183.38449999984, 21047.3225000017) point_berlin_3068_project = point_berlin_3068.project(1, (math.pi / 2)) point_meter_result = QgsPointXY(0, 0) length_meter_mapunits, point_meter_result = da_3068.measureLineProjected(point_berlin_3068, 1.0, (math.pi / 2)) pprint(point_meter_result) print('-I-> Berlin 3068 length_meter_mapunits[{}] point_meter_result[{}]'.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_3068.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 1, da_3068.lengthUnits(), True), '1.0 m') self.assertEqual(point_meter_result.toString(7), point_berlin_3068_project.toString(7)) point_berlin_wsg84 = QgsPointXY(13.37770458660236, 52.51627178856762) point_berlin_wsg84_project = QgsPointXY(13.37771931736259, 52.51627178856669) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_berlin_wsg84, 1.0, (math.pi / 2)) print('-I-> Berlin Wsg84 length_meter_mapunits[{}] point_meter_result[{}] ellipsoid[{}]'.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 20, da_wsg84.lengthUnits(), True), point_meter_result.asWkt(), da_wsg84.ellipsoid())) # for unknown reasons, this is returning '0.00001473026 m' instead of '0.00001473026 deg' when using da_wsg84.lengthUnits() # self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits,11,da_wsg84.lengthUnits(),True), '0.00001473026 deg') self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 11, QgsUnitTypes.DistanceDegrees, True), '0.00001473026 deg') self.assertEqual(point_meter_result.toString(7), point_berlin_wsg84_project.toString(7)) point_berlin_4314 = QgsPointXY(13.37944343021465, 52.51767872437083) point_berlin_4314_project = QgsPointXY(13.37945816324759, 52.5176787243699) length_meter_mapunits, point_meter_result = da_4314.measureLineProjected(point_berlin_4314, 1.0, (math.pi / 2)) print('-I-> Berlin 4314 length_meter_mapunits[{}] point_meter_result[{}]'.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_4314.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 9, QgsUnitTypes.DistanceDegrees, True), '0.000014733 deg') self.assertEqual(point_meter_result.toString(7), point_berlin_4314_project.toString(7)) point_berlin_4805 = QgsPointXY(31.04960570069176, 52.5174657497405) point_berlin_4805_project = QgsPointXY(31.04962043365347, 52.51746574973957) length_meter_mapunits, point_meter_result = da_4805.measureLineProjected(point_berlin_4805, 1.0, (math.pi / 2)) print('-I-> Berlin 4805 length_meter_mapunits[{}] point_meter_result[{}]'.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_4805.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 9, QgsUnitTypes.DistanceDegrees, True), '0.000014733 deg') self.assertEqual(point_meter_result.toString(7), point_berlin_4805_project.toString(7)) point_berlin_25833 = QgsPointXY(389918.0748318382, 5819698.772194743) point_berlin_25833_project = point_berlin_25833.project(1, (math.pi / 2)) length_meter_mapunits, point_meter_result = da_25833.measureLineProjected(point_berlin_25833, 1.0, (math.pi / 2)) print('-I-> Berlin 25833 length_meter_mapunits[{}] point_meter_result[{}]'.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_25833.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_25833.lengthUnits(), True), '1.0000000 m') self.assertEqual(point_meter_result.toString(7), point_berlin_25833_project.toString(7)) if da_5665.sourceCrs().authid() != "": point_berlin_5665 = QgsPointXY(3389996.871728864, 5822169.719727578) point_berlin_5665_project = point_berlin_5665.project(1, (math.pi / 2)) length_meter_mapunits, point_meter_result = da_5665.measureLineProjected(point_berlin_5665, 1.0, (math.pi / 2)) print('-I-> Berlin 5665 length_meter_mapunits[{}] point_meter_result[{}]'.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_5665.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 1.0, da_5665.lengthUnits(), True), '1.0 m') self.assertEqual(point_meter_result.toString(7), point_berlin_5665_project.toString(7)) print('\n12 points ''above over'' and on the Equator') point_wsg84 = QgsPointXY(25.7844, 71.1725) point_wsg84_project = QgsPointXY(25.78442775215388, 71.17249999999795) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Nordkap, Norway - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, QgsUnitTypes.DistanceDegrees, True), '0.0000278 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(24.95995, 60.16841) point_wsg84_project = QgsPointXY(24.95996801277454, 60.16840999999877) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Helsinki, Finnland - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001801 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(12.599278, 55.692861) point_wsg84_project = QgsPointXY(12.59929390161872, 55.69286099999897) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Copenhagen, Denmark - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001590 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-0.001389, 51.477778) point_wsg84_project = QgsPointXY(-0.001374606184398, 51.4777779999991) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Royal Greenwich Observatory, United Kingdom - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001439 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(7.58769, 47.55814) point_wsg84_project = QgsPointXY(7.587703287209086, 47.55813999999922) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Basel, Switzerland - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001329 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(11.255278, 43.775278) point_wsg84_project = QgsPointXY(11.25529042107924, 43.77527799999933) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Florenz, Italy - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001242 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(14.514722, 35.899722) point_wsg84_project = QgsPointXY(14.51473307693308, 35.89972199999949) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Valletta, Malta - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001108 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-79.933333, 32.783333) point_wsg84_project = QgsPointXY(-79.93332232547254, 32.78333299999955) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Charlston, South Carolina - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001067 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-17.6666666, 27.733333) point_wsg84_project = QgsPointXY(-17.66665645831515, 27.73333299999962) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Ferro, Spain - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001014 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-99.133333, 19.433333) point_wsg84_project = QgsPointXY(-99.1333234776827, 19.43333299999975) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Mexico City, Mexico - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000952 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-79.894444, 9.341667) point_wsg84_project = QgsPointXY(-79.89443489691369, 9.341666999999882) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Colón, Panama - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000910 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-74.075833, 4.598056) point_wsg84_project = QgsPointXY(-74.07582398803629, 4.598055999999943) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Bogotá, Colombia - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000901 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(0, 0) point_wsg84_project = QgsPointXY(0.000008983152841, 0) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Equator, Atlantic Ocean - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000898 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) print('\n12 points ''down under'' and 1 point that should be considered invalid') point_wsg84 = QgsPointXY(-78.509722, -0.218611) point_wsg84_project = QgsPointXY(-78.50971301678221, -0.218610999999997) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Quito, Ecuador - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000898 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(106.816667, -6.2) point_wsg84_project = QgsPointXY(106.8166760356519, -6.199999999999922) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Jakarta, Indonesia - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000904 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-77.018611, -12.035) point_wsg84_project = QgsPointXY(-77.01860181630058, -12.03499999999985) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Lima, Peru - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000918 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(25.466667, -10.716667) point_wsg84_project = QgsPointXY(25.46667614155322, -10.71666699999986) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Kolwezi, Congo - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000914 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-70.333333, -18.483333) point_wsg84_project = QgsPointXY(-70.3333235314429, -18.48333299999976) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Arica, Chile - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00000947 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-70.666667, -33.45) point_wsg84_project = QgsPointXY(-70.66665624452817, -33.44999999999953) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Santiago, Chile - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001076 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(144.9604, -37.8191) point_wsg84_project = QgsPointXY(144.96041135746983741, -37.81909999999945171) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Melbourne, Australia - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001136 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(147.29, -42.88) point_wsg84_project = QgsPointXY(147.2900122399815, -42.87999999999934) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Hobart City,Tasmania, Australia - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001224 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(168.101667, -46.899722) point_wsg84_project = QgsPointXY(168.101680123673, -46.89972199999923) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Ryan''s Creek Aerodrome, New Zealand - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001312 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-69.216667, -51.633333) point_wsg84_project = QgsPointXY(-69.21665255700216, -51.6333329999991) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Río Gallegos, Argentina - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001444 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-68.3, -54.8) point_wsg84_project = QgsPointXY(-68.29998445081456, -54.79999999999899) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Ushuaia, Tierra del Fuego, Argentina - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00001555 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-63.494444, -64.825278) point_wsg84_project = QgsPointXY(-63.49442294002932, -64.82527799999851) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Port Lockroy, Antarctica - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00002106 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-180, -84.863272250) point_wsg84_project = QgsPointXY(-179.9999000000025, -84.8632722499922) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-I-> Someware, Antarctica - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00010000 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7)) point_wsg84 = QgsPointXY(-180, -85.0511300) point_wsg84_project = QgsPointXY(-179.9998962142197, -85.05112999999191) length_meter_mapunits, point_meter_result = da_wsg84.measureLineProjected(point_wsg84, 1.0, (math.pi / 2)) print('-W-> Mercator''s Last Stop, Antarctica - Wsg84 - length_meter_mapunits[{}] point_meter_result[{}] '.format(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, da_wsg84.lengthUnits(), True), point_meter_result.asWkt())) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 8, QgsUnitTypes.DistanceDegrees, True), '0.00010379 deg') self.assertEqual(point_meter_result.toString(7), point_wsg84_project.toString(7))
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_VECTOR), context) rasterPath = str(self.getParameterValue(self.INPUT_RASTER)) rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterDS = None fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('poly_id', QVariant.Int, '', 10, 0)) fields.append(QgsField('point_id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter(fields, QgsWkbTypes.Point, layer.crs(), context) outFeature = QgsFeature() outFeature.setFields(fields) fid = 0 polyId = 0 pointId = 0 features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / layer.featureCount() if layer.featureCount() else 0 for current, f in enumerate(features): geom = f.geometry() bbox = geom.boundingBox() xMin = bbox.xMinimum() xMax = bbox.xMaximum() yMin = bbox.yMinimum() yMax = bbox.yMaximum() (startRow, startColumn) = raster.mapToPixel(xMin, yMax, geoTransform) (endRow, endColumn) = raster.mapToPixel(xMax, yMin, geoTransform) # use prepared geometries for faster intersection tests engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() for row in range(startRow, endRow + 1): for col in range(startColumn, endColumn + 1): (x, y) = raster.pixelToMap(row, col, geoTransform) point = QgsPointXY() point.setX(x) point.setY(y) if engine.contains(point): outFeature.setGeometry(QgsGeometry(point)) outFeature['id'] = fid outFeature['poly_id'] = polyId outFeature['point_id'] = pointId fid += 1 pointId += 1 writer.addFeature(outFeature, QgsFeatureSink.FastInsert) pointId = 0 polyId += 1 feedback.setProgress(int(current * total)) del writer
def transformedCornerCoordinates(self, center, rotation, xScale, yScale): # scale topLeft = QgsPointXY(-self.image.width() / 2.0 * xScale, self.image.height() / 2.0 * yScale) topRight = QgsPointXY(self.image.width() / 2.0 * xScale, self.image.height() / 2.0 * yScale) bottomLeft = QgsPointXY(-self.image.width() / 2.0 * xScale, - self.image.height() / 2.0 * yScale) bottomRight = QgsPointXY(self.image.width() / 2.0 * xScale, - self.image.height() / 2.0 * yScale) # rotate # minus sign because rotation is CW in this class and Qt) rotationRad = -rotation * math.pi / 180 cosRot = math.cos(rotationRad) sinRot = math.sin(rotationRad) topLeft = self._rotate(topLeft, cosRot, sinRot) topRight = self._rotate(topRight, cosRot, sinRot) bottomRight = self._rotate(bottomRight, cosRot, sinRot) bottomLeft = self._rotate(bottomLeft, cosRot, sinRot) topLeft.set(topLeft.x() + center.x(), topLeft.y() + center.y()) topRight.set(topRight.x() + center.x(), topRight.y() + center.y()) bottomRight.set(bottomRight.x() + center.x(), bottomRight.y() + center.y()) bottomLeft.set(bottomLeft.x() + center.x(), bottomLeft.y() + center.y()) return (topLeft, topRight, bottomRight, bottomLeft)
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[group_field_index] else: group = 1 order = f[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 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 transformedCornerCoordinatesFromPoint(self, startPoint, rotation, xScale, yScale): # startPoint is a fixed point for this new movement (rotation and # scale) # rotation is the global rotation of the image # xScale is the new xScale factor to be multiplied by self.xScale # idem for yScale # Calculate the coordinate of the center in a startPoint origin # coordinate system and apply scales dX = (self.center.x() - startPoint.x()) * xScale dY = (self.center.y() - startPoint.y()) * yScale # Half width and half height in the current transformation hW = (self.image.width() / 2.0) * self.xScale * xScale hH = (self.image.height() / 2.0) * self.yScale * yScale # Actual rectangle coordinates : pt1 = QgsPointXY(-hW, hH) pt2 = QgsPointXY(hW, hH) pt3 = QgsPointXY(hW, -hH) pt4 = QgsPointXY(-hW, -hH) # Actual rotation from the center # minus sign because rotation is CW in this class and Qt) rotationRad = -self.rotation * math.pi / 180 cosRot = math.cos(rotationRad) sinRot = math.sin(rotationRad) pt1 = self._rotate(pt1, cosRot, sinRot) pt2 = self._rotate(pt2, cosRot, sinRot) pt3 = self._rotate(pt3, cosRot, sinRot) pt4 = self._rotate(pt4, cosRot, sinRot) # Second transformation # displacement of the origin pt1 = QgsPointXY(pt1.x() + dX, pt1.y() + dY) pt2 = QgsPointXY(pt2.x() + dX, pt2.y() + dY) pt3 = QgsPointXY(pt3.x() + dX, pt3.y() + dY) pt4 = QgsPointXY(pt4.x() + dX, pt4.y() + dY) # Rotation # minus sign because rotation is CW in this class and Qt) rotationRad = -rotation * math.pi / 180 cosRot = math.cos(rotationRad) sinRot = math.sin(rotationRad) pt1 = self._rotate(pt1, cosRot, sinRot) pt2 = self._rotate(pt2, cosRot, sinRot) pt3 = self._rotate(pt3, cosRot, sinRot) pt4 = self._rotate(pt4, cosRot, sinRot) # translate to startPoint pt1 = QgsPointXY(pt1.x() + startPoint.x(), pt1.y() + startPoint.y()) pt2 = QgsPointXY(pt2.x() + startPoint.x(), pt2.y() + startPoint.y()) pt3 = QgsPointXY(pt3.x() + startPoint.x(), pt3.y() + startPoint.y()) pt4 = QgsPointXY(pt4.x() + startPoint.x(), pt4.y() + startPoint.y()) return (pt1, pt2, pt3, pt4)
def canvasPointXY(self, lat, lon): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(self.settings.epsg4326, canvasCrs) x, y = transform.transform(float(lon), float(lat)) pt = QgsPointXY(x, y) return pt
def clip_voronoi(self, edges, c, width, height, extent, exX, exY): """Clip voronoi function based on code written for Inkscape. Copyright (C) 2010 Alvin Penner, [email protected] """ def clip_line(x1, y1, x2, y2, w, h, x, y): if x1 < 0 - x and x2 < 0 - x: return [0, 0, 0, 0] if x1 > w + x and x2 > w + x: return [0, 0, 0, 0] if x1 < 0 - x: y1 = (y1 * x2 - y2 * x1) / (x2 - x1) x1 = 0 - x if x2 < 0 - x: y2 = (y1 * x2 - y2 * x1) / (x2 - x1) x2 = 0 - x if x1 > w + x: y1 = y1 + (w + x - x1) * (y2 - y1) / (x2 - x1) x1 = w + x if x2 > w + x: y2 = y1 + (w + x - x1) * (y2 - y1) / (x2 - x1) x2 = w + x if y1 < 0 - y and y2 < 0 - y: return [0, 0, 0, 0] if y1 > h + y and y2 > h + y: return [0, 0, 0, 0] if x1 == x2 and y1 == y2: return [0, 0, 0, 0] if y1 < 0 - y: x1 = (x1 * y2 - x2 * y1) / (y2 - y1) y1 = 0 - y if y2 < 0 - y: x2 = (x1 * y2 - x2 * y1) / (y2 - y1) y2 = 0 - y if y1 > h + y: x1 = x1 + (h + y - y1) * (x2 - x1) / (y2 - y1) y1 = h + y if y2 > h + y: x2 = x1 + (h + y - y1) * (x2 - x1) / (y2 - y1) y2 = h + y return [x1, y1, x2, y2] lines = [] hasXMin = False hasYMin = False hasXMax = False hasYMax = False for edge in edges: if edge[1] >= 0 and edge[2] >= 0: # Two vertices [x1, y1, x2, y2] = clip_line( c.vertices[edge[1]][0], c.vertices[edge[1]][1], c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, exX, exY, ) elif edge[1] >= 0: # Only one vertex if c.lines[edge[0]][1] == 0: # Vertical line xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] if c.vertices[edge[1]][1] > (height + exY) / 2: ytemp = height + exY else: ytemp = 0 - exX else: xtemp = width + exX ytemp = (c.lines[edge[0]][2] - (width + exX) * c.lines[edge[0]][0]) / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line( c.vertices[edge[1]][0], c.vertices[edge[1]][1], xtemp, ytemp, width, height, exX, exY, ) elif edge[2] >= 0: # Only one vertex if c.lines[edge[0]][1] == 0: # Vertical line xtemp = c.lines[edge[0]][2] / c.lines[edge[0]][0] if c.vertices[edge[2]][1] > (height + exY) / 2: ytemp = height + exY else: ytemp = 0.0 - exY else: xtemp = 0.0 - exX ytemp = c.lines[edge[0]][2] / c.lines[edge[0]][1] [x1, y1, x2, y2] = clip_line( xtemp, ytemp, c.vertices[edge[2]][0], c.vertices[edge[2]][1], width, height, exX, exY, ) if x1 or x2 or y1 or y2: lines.append( QgsPointXY(x1 + extent.xMinimum(), y1 + extent.yMinimum())) lines.append( QgsPointXY(x2 + extent.xMinimum(), y2 + extent.yMinimum())) if 0 - exX in (x1, x2): hasXMin = True if 0 - exY in (y1, y2): hasYMin = True if height + exY in (y1, y2): hasYMax = True if width + exX in (x1, x2): hasXMax = True if hasXMin: if hasYMax: lines.append( QgsPointXY(extent.xMinimum() - exX, height + extent.yMinimum() + exY)) if hasYMin: lines.append( QgsPointXY(extent.xMinimum() - exX, extent.yMinimum() - exY)) if hasXMax: if hasYMax: lines.append( QgsPointXY(width + extent.xMinimum() + exX, height + extent.yMinimum() + exY)) if hasYMin: lines.append( QgsPointXY(width + extent.xMinimum() + exX, extent.yMinimum() - exY)) return lines
def testQgsPointXYRepr(self): p = QgsPointXY(123.456, 987.654) self.assertTrue(p.__repr__().startswith('<QgsPointXY: POINT(123.456'))
def test_resetSnappingIndex(self): self.pointsLayer.setDependencies([]) self.linesLayer.setDependencies([]) self.pointsLayer2.setDependencies([]) ms = QgsMapSettings() ms.setOutputSize(QSize(100, 100)) ms.setExtent(QgsRectangle(0, 0, 1, 1)) self.assertTrue(ms.hasValidSettings()) u = QgsSnappingUtils() u.setMapSettings(ms) cfg = u.config() cfg.setEnabled(True) cfg.setMode(QgsSnappingConfig.AdvancedConfiguration) cfg.setIndividualLayerSettings( self.pointsLayer, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) m = u.snapToMap(QPoint(95, 100)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(1, 0)) f = QgsFeature(self.linesLayer.fields()) f.setId(1) geom = QgsGeometry.fromWkt("LINESTRING(0 0,1 1)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() l1 = len([f for f in self.pointsLayer.getFeatures()]) self.assertEqual(l1, 4) m = u.snapToMap(QPoint(95, 0)) # snapping not updated self.pointsLayer.setDependencies([]) self.assertEqual(m.isValid(), False) # set layer dependencies self.pointsLayer.setDependencies( [QgsMapLayerDependency(self.linesLayer.id())]) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(2) geom = QgsGeometry.fromWkt("LINESTRING(0 0,0.5 0.5)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the snapped point is OK m = u.snapToMap(QPoint(45, 50)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.5, 0.5)) self.pointsLayer.setDependencies([]) # test chained layer dependencies A -> B -> C cfg.setIndividualLayerSettings( self.pointsLayer2, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) self.pointsLayer.setDependencies( [QgsMapLayerDependency(self.linesLayer.id())]) self.pointsLayer2.setDependencies( [QgsMapLayerDependency(self.pointsLayer.id())]) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(3) geom = QgsGeometry.fromWkt("LINESTRING(0 0.2,0.5 0.8)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the second snapped point is OK m = u.snapToMap(QPoint(75, 100 - 80)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.7, 0.8)) self.pointsLayer.setDependencies([]) self.pointsLayer2.setDependencies([])
def testMeasureLineProjected(self): # +-+ # | | # +-+ + # test setting/getting the source CRS da_3068 = QgsDistanceArea() da_wsg84 = QgsDistanceArea() da_3068.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3068'), QgsProject.instance().transformContext()) if (da_3068.sourceCrs().isGeographic()): da_3068.setEllipsoid(da_3068.sourceCrs().ellipsoidAcronym()) print(("setting [{}] srid [{}] description [{}]".format(u'Soldner Berlin', da_3068.sourceCrs().authid(), da_3068.sourceCrs().description()))) self.assertEqual(da_3068.sourceCrs().authid(), 'EPSG:3068') da_wsg84.setSourceCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:4326'), QgsProject.instance().transformContext()) if (da_wsg84.sourceCrs().isGeographic()): da_wsg84.setEllipsoid(da_wsg84.sourceCrs().ellipsoidAcronym()) self.assertEqual(da_wsg84.sourceCrs().authid(), 'EPSG:4326') print(("setting [{}] srid [{}] description [{}] isGeographic[{}]".format(u'Wsg84', da_wsg84.sourceCrs().authid(), da_wsg84.sourceCrs().description(), da_wsg84.sourceCrs().isGeographic()))) # print(("-- projectionAcronym[{}] ellipsoidAcronym[{}] toWkt[{}] mapUnits[{}] toProj4[{}]".format(da_wsg84.sourceCrs().projectionAcronym(),da_wsg84.sourceCrs().ellipsoidAcronym(), da_wsg84.sourceCrs().toWkt(),da_wsg84.sourceCrs().mapUnits(),da_wsg84.sourceCrs().toProj4()))) print(("Testing Position change for[{}] years[{}]".format(u'Ampelanlage - Potsdamer Platz, Verkehrsinsel', u'1924 and 1998'))) # 1924-10-24 SRID=3068;POINT(23099.49 20296.69) # 1924-10-24 SRID=4326;POINT(13.37650707988041 52.50952361017194) # 1998-10-02 SRID=3068;POINT(23082.30 20267.80) # 1998-10-02 SRID=4326;POINT(13.37625537334001 52.50926345498337) # values returned by SpatiaLite point_soldner_1924 = QgsPointXY(23099.49, 20296.69) point_soldner_1998 = QgsPointXY(23082.30, 20267.80) distance_soldner_meters = 33.617379 azimuth_soldner_1924 = 3.678339 # ST_Transform(point_soldner_1924,point_soldner_1998,4326) point_wsg84_1924 = QgsPointXY(13.37650707988041, 52.50952361017194) point_wsg84_1998 = QgsPointXY(13.37625537334001, 52.50926345498337) # ST_Distance(point_wsg84_1924,point_wsg84_1998,1) distance_wsg84_meters = 33.617302 # ST_Distance(point_wsg84_1924,point_wsg84_1998) # distance_wsg84_mapunits=0.000362 distance_wsg84_mapunits_format = QgsDistanceArea.formatDistance(0.000362, 7, QgsUnitTypes.DistanceDegrees, True) # ST_Azimuth(point_wsg84_1924,point_wsg84_1998) azimuth_wsg84_1924 = 3.674878 # ST_Azimuth(point_wsg84_1998,point_wsg84_1998) azimuth_wsg84_1998 = 0.533282 # ST_Project(point_wsg84_1924,33.617302,3.674878) # SRID=4326;POINT(13.37625537318728 52.50926345503591) point_soldner_1998_project = QgsPointXY(13.37625537318728, 52.50926345503591) # ST_Project(point_wsg84_1998,33.617302,0.533282) # SRID=4326;POINT(13.37650708009255 52.50952361009799) point_soldner_1924_project = QgsPointXY(13.37650708009255, 52.50952361009799) distance_qpoint = point_soldner_1924.distance(point_soldner_1998) azimuth_qpoint = point_soldner_1924.azimuth(point_soldner_1998) point_soldner_1998_result = point_soldner_1924.project(distance_qpoint, azimuth_qpoint) point_soldner_1924_result = QgsPointXY(0, 0) point_soldner_1998_result = QgsPointXY(0, 0) # Test meter based projected point from point_1924 to point_1998 length_1998_mapunits, point_soldner_1998_result = da_3068.measureLineProjected(point_soldner_1924, distance_soldner_meters, azimuth_qpoint) self.assertEqual(point_soldner_1998_result.toString(6), point_soldner_1998.toString(6)) # Test degree based projected point from point_1924 1 meter due East point_wsg84_meter_result = QgsPointXY(0, 0) point_wsg84_1927_meter = QgsPointXY(13.37652180838435, 52.50952361017102) length_meter_mapunits, point_wsg84_meter_result = da_wsg84.measureLineProjected(point_wsg84_1924, 1.0, (math.pi / 2)) self.assertEqual(QgsDistanceArea.formatDistance(length_meter_mapunits, 7, QgsUnitTypes.DistanceDegrees, True), '0.0000147 deg') self.assertEqual(point_wsg84_meter_result.toString(7), point_wsg84_1927_meter.toString(7)) point_wsg84_1998_result = QgsPointXY(0, 0) length_1928_mapunits, point_wsg84_1998_result = da_wsg84.measureLineProjected(point_wsg84_1924, distance_wsg84_meters, azimuth_wsg84_1924) self.assertEqual(QgsDistanceArea.formatDistance(length_1928_mapunits, 7, QgsUnitTypes.DistanceDegrees, True), distance_wsg84_mapunits_format) self.assertEqual(point_wsg84_1998_result.toString(7), point_wsg84_1998.toString(7))
def test_signalConnection(self): # remove all layers QgsProject.instance().removeAllMapLayers() # set dependencies and add back layers self.pointsLayer = QgsVectorLayer( "dbname='%s' table=\"node\" (geom) sql=" % self.fn, "points", "spatialite") assert (self.pointsLayer.isValid()) self.linesLayer = QgsVectorLayer( "dbname='%s' table=\"section\" (geom) sql=" % self.fn, "lines", "spatialite") assert (self.linesLayer.isValid()) self.pointsLayer2 = QgsVectorLayer( "dbname='%s' table=\"node2\" (geom) sql=" % self.fn, "_points2", "spatialite") assert (self.pointsLayer2.isValid()) self.pointsLayer.setDependencies( [QgsMapLayerDependency(self.linesLayer.id())]) self.pointsLayer2.setDependencies( [QgsMapLayerDependency(self.pointsLayer.id())]) # this should update connections between layers QgsProject.instance().addMapLayers([self.pointsLayer]) QgsProject.instance().addMapLayers([self.linesLayer]) QgsProject.instance().addMapLayers([self.pointsLayer2]) ms = QgsMapSettings() ms.setOutputSize(QSize(100, 100)) ms.setExtent(QgsRectangle(0, 0, 1, 1)) self.assertTrue(ms.hasValidSettings()) u = QgsSnappingUtils() u.setMapSettings(ms) cfg = u.config() cfg.setEnabled(True) cfg.setMode(QgsSnappingConfig.AdvancedConfiguration) cfg.setIndividualLayerSettings( self.pointsLayer, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) cfg.setIndividualLayerSettings( self.pointsLayer2, QgsSnappingConfig.IndividualLayerSettings(True, QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels)) u.setConfig(cfg) # add another line f = QgsFeature(self.linesLayer.fields()) f.setId(4) geom = QgsGeometry.fromWkt("LINESTRING(0.5 0.2,0.6 0)") f.setGeometry(geom) self.linesLayer.startEditing() self.linesLayer.addFeatures([f]) self.linesLayer.commitChanges() # check the second snapped point is OK m = u.snapToMap(QPoint(75, 100 - 0)) self.assertTrue(m.isValid()) self.assertTrue(m.hasVertex()) self.assertEqual(m.point(), QgsPointXY(0.8, 0.0)) self.pointsLayer.setDependencies([]) self.pointsLayer2.setDependencies([])
def balanced(features, graph, feedback, balance=0, min_colors=4): feature_colors = {} # start with minimum number of colors in pool color_pool = set(range(1, min_colors + 1)) # calculate count of neighbours neighbour_count = defaultdict(int) for feature_id, neighbours in graph.node_edge.items(): neighbour_count[feature_id] += len(neighbours) # sort features by neighbour count - we want to handle those with more neighbours first sorted_by_count = [feature_id for feature_id in sorted(neighbour_count.items(), key=operator.itemgetter(1), reverse=True)] # counts for each color already assigned color_counts = defaultdict(int) color_areas = defaultdict(float) for c in color_pool: color_counts[c] = 0 color_areas[c] = 0 total = 10.0 / len(sorted_by_count) i = 0 for (feature_id, n) in sorted_by_count: if feedback.isCanceled(): break # first work out which already assigned colors are adjacent to this feature adjacent_colors = set() for neighbour in graph.node_edge[feature_id]: if neighbour in feature_colors: adjacent_colors.add(feature_colors[neighbour]) # from the existing colors, work out which are available (ie non-adjacent) available_colors = color_pool.difference(adjacent_colors) feature_color = -1 if len(available_colors) == 0: # no existing colors available for this feature, so add new color to pool and repeat min_colors += 1 return ColoringAlgorithm.balanced(features, graph, feedback, balance, min_colors) else: if balance == 0: # choose least used available color counts = [(c, v) for c, v in color_counts.items() if c in available_colors] feature_color = sorted(counts, key=operator.itemgetter(1))[0][0] color_counts[feature_color] += 1 elif balance == 1: areas = [(c, v) for c, v in color_areas.items() if c in available_colors] feature_color = sorted(areas, key=operator.itemgetter(1))[0][0] color_areas[feature_color] += features[feature_id].geometry().area() elif balance == 2: min_distances = {c: sys.float_info.max for c in available_colors} this_feature_centroid = QgsPointXY(features[feature_id].geometry().centroid().geometry()) # find features for all available colors other_features = {f_id: c for (f_id, c) in feature_colors.items() if c in available_colors} # loop through these, and calculate the minimum distance from this feature to the nearest # feature with each assigned color for other_feature_id, c in other_features.items(): if feedback.isCanceled(): break other_geometry = features[other_feature_id].geometry() other_centroid = QgsPointXY(other_geometry.centroid().geometry()) distance = this_feature_centroid.distanceSquared(other_centroid) if distance < min_distances[c]: min_distances[c] = distance # choose color such that minimum distance is maximised! ie we want MAXIMAL separation between # features with the same color feature_color = sorted(min_distances, key=min_distances.__getitem__, reverse=True)[0] feature_colors[feature_id] = feature_color i += 1 feedback.setProgress(70 + int(i * total)) return feature_colors
from time import sleep from qgis.core import ( QgsFeature, QgsGeometry, QgsPointXY, QgsFeatureRequest, QgsExpression, QgsProject, QgsOfflineEditing, ) # Tet features, fields: [id, name, geometry] # "id" is used as a pk to retriev features by attribute TEST_FEATURES = [ (1, 'name 1', QgsPointXY(9, 45)), (2, 'name 2', QgsPointXY(9.5, 45.5)), (3, 'name 3', QgsPointXY(9.5, 46)), (4, 'name 4', QgsPointXY(10, 46.5)), ] # Additional features for insert test TEST_FEATURES_INSERT = [ (5, 'name 5', QgsPointXY(9.7, 45.7)), (6, 'name 6', QgsPointXY(10.6, 46.6)), ] class OfflineTestBase(object): """Generic test methods for all online providers""" def _setUp(self):
def test_updateFeatures(self): ol, offline_layer = self._testInit() # Edit feature 2 feat2 = self._getFeatureByAttribute(offline_layer, 'name', "'name 2'") self.assertTrue(offline_layer.startEditing()) self.assertTrue( offline_layer.changeAttributeValue( feat2.id(), offline_layer.fields().lookupField('name'), 'name 2 edited')) self.assertTrue( offline_layer.changeGeometry( feat2.id(), QgsGeometry.fromPointXY(QgsPointXY(33.0, 60.0)))) self.assertTrue(offline_layer.commitChanges()) feat2 = self._getFeatureByAttribute(offline_layer, 'name', "'name 2 edited'") self.assertTrue(ol.isOfflineProject()) # Sync ol.synchronize() sleep(2) # Does anybody know why the sleep is needed? Is that a threaded WFS consequence? online_layer = list(self.registry.mapLayers().values())[0] self.assertTrue(online_layer.isValid()) self.assertFalse(online_layer.name().find('(offline)') > -1) self.assertEqual(len([f for f in online_layer.getFeatures()]), len(TEST_FEATURES)) # Check that data have changed in the backend (raise exception if not found) feat2 = self._getFeatureByAttribute(self._getLayer('test_point'), 'name', "'name 2 edited'") feat2 = self._getFeatureByAttribute(online_layer, 'name', "'name 2 edited'") self.assertEqual(feat2.geometry().asPoint().toString(), QgsPointXY(33.0, 60.0).toString()) # Check that all other features have not changed layer = self._getLayer('test_point') self.assertTrue(self._compareFeature(layer, TEST_FEATURES[1 - 1])) self.assertTrue(self._compareFeature(layer, TEST_FEATURES[3 - 1])) self.assertTrue(self._compareFeature(layer, TEST_FEATURES[4 - 1])) # Test for regression on double sync (it was a SEGFAULT) # goes offline ol = QgsOfflineEditing() offline_layer = list(self.registry.mapLayers().values())[0] # Edit feature 2 feat2 = self._getFeatureByAttribute(offline_layer, 'name', "'name 2 edited'") self.assertTrue(offline_layer.startEditing()) self.assertTrue( offline_layer.changeAttributeValue( feat2.id(), offline_layer.fields().lookupField('name'), 'name 2')) self.assertTrue( offline_layer.changeGeometry( feat2.id(), QgsGeometry.fromPointXY(TEST_FEATURES[1][2]))) # Edit feat 4 feat4 = self._getFeatureByAttribute(offline_layer, 'name', "'name 4'") self.assertTrue( offline_layer.changeAttributeValue( feat4.id(), offline_layer.fields().lookupField('name'), 'name 4 edited')) self.assertTrue(offline_layer.commitChanges()) # Sync ol.synchronize() # Does anybody knows why the sleep is needed? Is that a threaded WFS consequence? sleep(1) online_layer = list(self.registry.mapLayers().values())[0] layer = self._getLayer('test_point') # Check that data have changed in the backend (raise exception if not found) feat4 = self._getFeatureByAttribute(layer, 'name', "'name 4 edited'") feat4 = self._getFeatureByAttribute(online_layer, 'name', "'name 4 edited'") feat2 = self._getFeatureByAttribute(layer, 'name', "'name 2'") feat2 = self._getFeatureByAttribute(online_layer, 'name', "'name 2'") # Check that all other features have not changed layer = self._getLayer('test_point') self.assertTrue(self._compareFeature(layer, TEST_FEATURES[1 - 1])) self.assertTrue(self._compareFeature(layer, TEST_FEATURES[2 - 1])) self.assertTrue(self._compareFeature(layer, TEST_FEATURES[3 - 1]))
def processAlgorithm(self, parameters, context, feedback): network = self.parameterAsSource(parameters, self.INPUT, context) if network is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) startPoints = self.parameterAsSource(parameters, self.START_POINTS, context) if startPoints is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.START_POINTS)) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context) directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) include_bounds = True # default to true to maintain 3.0 API if self.INCLUDE_BOUNDS in parameters: include_bounds = self.parameterAsBool(parameters, self.INCLUDE_BOUNDS, context) fields = startPoints.fields() fields.append(QgsField('type', QVariant.String, '', 254, 0)) fields.append(QgsField('start', QVariant.String, '', 254, 0)) feat = QgsFeature() feat.setFields(fields) directionField = -1 if directionFieldName: directionField = network.fields().lookupField(directionFieldName) speedField = -1 if speedFieldName: speedField = network.fields().lookupField(speedFieldName) director = QgsVectorLayerDirector(network, directionField, forwardValue, backwardValue, bothValue, defaultDirection) distUnit = context.project().crs().mapUnits() multiplier = QgsUnitTypes.fromUnitToUnitFactor( distUnit, QgsUnitTypes.DistanceMeters) if strategy == 0: strategy = QgsNetworkDistanceStrategy() else: strategy = QgsNetworkSpeedStrategy(speedField, defaultSpeed, multiplier * 1000.0 / 3600.0) director.addStrategy(strategy) builder = QgsGraphBuilder(network.sourceCrs(), True, tolerance) feedback.pushInfo( QCoreApplication.translate('ServiceAreaFromLayer', 'Loading start points…')) request = QgsFeatureRequest() request.setDestinationCrs(network.sourceCrs(), context.transformContext()) features = startPoints.getFeatures(request) total = 100.0 / startPoints.featureCount() if startPoints.featureCount( ) else 0 points = [] source_attributes = {} i = 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue for p in f.geometry().vertices(): points.append(QgsPointXY(p)) source_attributes[i] = f.attributes() i += 1 feedback.setProgress(int(current * total)) feedback.pushInfo( QCoreApplication.translate('ServiceAreaFromLayer', 'Building graph…')) snappedPoints = director.makeGraph(builder, points, feedback) feedback.pushInfo( QCoreApplication.translate('ServiceAreaFromLayer', 'Calculating service areas…')) graph = builder.graph() (point_sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.MultiPoint, network.sourceCrs()) (line_sink, line_dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LINES, context, fields, QgsWkbTypes.MultiLineString, network.sourceCrs()) total = 100.0 / len(snappedPoints) if snappedPoints else 1 for i, p in enumerate(snappedPoints): if feedback.isCanceled(): break idxStart = graph.findVertex(snappedPoints[i]) origPoint = points[i].toString() tree, cost = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0) vertices = set() area_points = [] lines = [] for vertex, start_vertex_cost in enumerate(cost): inbound_edge_index = tree[vertex] if inbound_edge_index == -1 and vertex != idxStart: # unreachable vertex continue if start_vertex_cost > travelCost: # vertex is too expensive, discard continue vertices.add(vertex) start_point = graph.vertex(vertex).point() # find all edges coming from this vertex for edge_id in graph.vertex(vertex).outgoingEdges(): edge = graph.edge(edge_id) end_vertex_cost = start_vertex_cost + edge.cost(0) end_point = graph.vertex(edge.toVertex()).point() if end_vertex_cost <= travelCost: # end vertex is cheap enough to include vertices.add(edge.toVertex()) lines.append([start_point, end_point]) else: # travelCost sits somewhere on this edge, interpolate position interpolated_end_point = QgsGeometryUtils.interpolatePointOnLineByValue( start_point.x(), start_point.y(), start_vertex_cost, end_point.x(), end_point.y(), end_vertex_cost, travelCost) area_points.append(interpolated_end_point) lines.append([start_point, interpolated_end_point]) for v in vertices: area_points.append(graph.vertex(v).point()) feat = QgsFeature() if point_sink is not None: geomPoints = QgsGeometry.fromMultiPointXY(area_points) feat.setGeometry(geomPoints) attrs = source_attributes[i] attrs.extend(['within', origPoint]) feat.setAttributes(attrs) point_sink.addFeature(feat, QgsFeatureSink.FastInsert) if include_bounds: upperBoundary = [] lowerBoundary = [] vertices = [] for vertex, c in enumerate(cost): if c > travelCost and tree[vertex] != -1: vertexId = graph.edge(tree[vertex]).fromVertex() if cost[vertexId] <= travelCost: vertices.append(vertex) for v in vertices: upperBoundary.append( graph.vertex(graph.edge( tree[v]).toVertex()).point()) lowerBoundary.append( graph.vertex(graph.edge( tree[v]).fromVertex()).point()) geomUpper = QgsGeometry.fromMultiPointXY(upperBoundary) geomLower = QgsGeometry.fromMultiPointXY(lowerBoundary) feat.setGeometry(geomUpper) attrs[-2] = 'upper' feat.setAttributes(attrs) point_sink.addFeature(feat, QgsFeatureSink.FastInsert) feat.setGeometry(geomLower) attrs[-2] = 'lower' feat.setAttributes(attrs) point_sink.addFeature(feat, QgsFeatureSink.FastInsert) if line_sink is not None: geom_lines = QgsGeometry.fromMultiPolylineXY(lines) feat.setGeometry(geom_lines) attrs = source_attributes[i] attrs.extend(['lines', origPoint]) feat.setAttributes(attrs) line_sink.addFeature(feat, QgsFeatureSink.FastInsert) feedback.setProgress(int(i * total)) results = {} if point_sink is not None: results[self.OUTPUT] = dest_id if line_sink is not None: results[self.OUTPUT_LINES] = line_dest_id return results
def run(sources_layer_path, receivers_layer_path, emission_pts_layer_path, research_ray): sources_layer = QgsVectorLayer(sources_layer_path, "input layer", "ogr") receivers_layer = QgsVectorLayer(receivers_layer_path, "output layer", "ogr") sources_feat_all = sources_layer.dataProvider().getFeatures() receivers_feat_all_dict = {} receivers_feat_all = receivers_layer.dataProvider().getFeatures() receivers_spIndex = QgsSpatialIndex() for receivers_feat in receivers_feat_all: receivers_spIndex.insertFeature(receivers_feat) receivers_feat_all_dict[receivers_feat.id()] = receivers_feat emission_pts_fields = QgsFields() emission_pts_fields.append(QgsField("id_emi", QVariant.Int)) emission_pts_fields.append(QgsField("id_emi_source", QVariant.Int)) emission_pts_fields.append(QgsField("id_source", QVariant.Int)) emission_pts_fields.append(QgsField("d_rTOe", QVariant.Double, len=10, prec=2)) # update for QGIS 3 converting VectorWriter to QgsVectorFileWriter # emission_pts_writer = VectorWriter(emission_pts_layer_path, None, emission_pts_fields, 0, sources_layer.crs()) emission_pts_writer = QgsVectorFileWriter(emission_pts_layer_path, "System", emission_pts_fields, QgsWkbTypes.Point, sources_layer.crs(), "ESRI Shapefile") # initializes ray and emission point id emission_pt_id = 0 for sources_feat in sources_feat_all: # researches the receiver points in a rectangle created by the research_ray # creates the search rectangle rect = QgsRectangle() rect.setXMinimum(sources_feat.geometry().boundingBox().xMinimum() - research_ray) rect.setXMaximum(sources_feat.geometry().boundingBox().xMaximum() + research_ray) rect.setYMinimum(sources_feat.geometry().boundingBox().yMinimum() - research_ray) rect.setYMaximum(sources_feat.geometry().boundingBox().yMaximum() + research_ray) receiver_pts_request = receivers_spIndex.intersects(rect) distance_min = [] for receiver_pts_id in receiver_pts_request: receiver_pts_feat = receivers_feat_all_dict[receiver_pts_id] result = sources_feat.geometry().closestSegmentWithContext(receiver_pts_feat.geometry().asPoint()) distance_min_tmp = sqrt(result[0]) if distance_min_tmp <= research_ray: distance_min.append(distance_min_tmp) # defines segment max length if len(distance_min) >= 1: segment_max = min(distance_min) / 2 if segment_max < 2: segment_max = 2 else: continue # splits the sources line in emission points at a fix distance (minimum distance/2) and create the emission point layer # gets vertex sources_geom = sources_feat.geometry() if sources_geom.isMultipart(): sources_geom.convertToSingleType() sources_feat_vertex_pt_all = sources_geom.asPolyline() emission_pt_id_road = 0 for i in range(0, len(sources_feat_vertex_pt_all)): pt1 = QgsPointXY(sources_feat_vertex_pt_all[i]) add_point_to_layer(emission_pts_writer, pt1, [emission_pt_id, emission_pt_id_road, sources_feat.id(), segment_max]) emission_pt_id = emission_pt_id + 1 emission_pt_id_road = emission_pt_id_road + 1 if i < len(sources_feat_vertex_pt_all) - 1: pt2 = QgsPoint(sources_feat_vertex_pt_all[i + 1]) x1 = pt1.x() y1 = pt1.y() x2 = pt2.x() y2 = pt2.y() if y2 == y1: dx = segment_max dy = 0 m = 0 elif x2 == x1: dx = 0 dy = segment_max else: m = (y2 - y1) / (x2 - x1) dx = sqrt((segment_max ** 2) / (1 + m ** 2)) dy = sqrt(((segment_max ** 2) * (m ** 2)) / (1 + m ** 2)) pt = pt1 while compute_distance(pt, pt2) > segment_max: x_temp = pt.x() y_temp = pt.y() if x_temp < x2: if m > 0: pt = QgsPointXY(x_temp + dx, y_temp + dy) elif m < 0: pt = QgsPointXY(x_temp + dx, y_temp - dy) elif m == 0: pt = QgsPointXY(x_temp + dx, y_temp) elif x_temp > x2: if m > 0: pt = QgsPointXY(x_temp - dx, y_temp - dy) elif m < 0: pt = QgsPointXY(x_temp - dx, y_temp + dy) elif m == 0: pt = QgsPointXY(x_temp - dx, y_temp) elif x_temp == x2: if y2 > y_temp: pt = QgsPointXY(x_temp, y_temp + dy) else: pt = QgsPointXY(x_temp, y_temp - dy) add_point_to_layer(emission_pts_writer, pt, [emission_pt_id, emission_pt_id_road, sources_feat.id(), segment_max]) emission_pt_id = emission_pt_id + 1 emission_pt_id_road = emission_pt_id_road + 1 del emission_pts_writer