def convertToPolygon(self, geom): if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry and geom.constGet().nCoordinates() < 3: raise QgsProcessingException( self.tr('Cannot convert from Point to Polygon').format(QgsWkbTypes.displayString(geom.wkbType()))) elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry: # multipoint with at least 3 points # TODO: mega inefficient - needs rework when geometry iterators land # (but at least it doesn't lose Z/M values) points = [] for g in geom.constGet().coordinateSequence(): for r in g: for p in r: points.append(p) linestring = QgsLineString(points) linestring.close() p = QgsPolygon() p.setExteriorRing(linestring) return [QgsGeometry(p)] elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry: if QgsWkbTypes.isMultiType(geom): parts = [] for i in range(geom.constGet().numGeometries()): p = QgsPolygon() linestring = geom.constGet().geometryN(i).clone() linestring.close() p.setExteriorRing(linestring) parts.append(QgsGeometry(p)) return QgsGeometry.collectGeometry(parts) else: # linestring to polygon p = QgsPolygon() linestring = geom.constGet().clone() linestring.close() p.setExteriorRing(linestring) return [QgsGeometry(p)] else: #polygon if QgsWkbTypes.isMultiType(geom): return geom.asGeometryCollection() else: return [geom]
def testJSONExporter(self): """ test converting features to GeoJSON """ fields = QgsFields() fields.append(QgsField("name", QVariant.String)) fields.append(QgsField("cost", QVariant.Double)) fields.append(QgsField("population", QVariant.Int)) feature = QgsFeature(fields, 5) feature.setGeometry(QgsGeometry(QgsPoint(5, 6))) feature.setAttributes(['Valsier Peninsula', 6.8, 198]) exporter = QgsJsonExporter() expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) # test with linestring for bbox inclusion l = QgsLineString() l.setPoints([QgsPoint(5, 6), QgsPoint(15, 16)]) feature.setGeometry(QgsGeometry(QgsLineString(l))) expected = """{ "type":"Feature", "id":5, "bbox":[5, 6, 15, 16], "geometry": {"type": "LineString", "coordinates": [ [5, 6], [15, 16]]}, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) # test that precision is respected feature.setGeometry(QgsGeometry(QgsPoint(5.444444444, 6.333333333))) exporter.setPrecision(3) self.assertEqual(exporter.precision(), 3) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5.444, 6.333]}, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) feature.setGeometry(QgsGeometry(QgsPoint(5, 6))) exporter.setPrecision(17) # test that attribute subset is respected exporter.setAttributes([0, 2]) self.assertEqual(exporter.attributes(), [0, 2]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula", "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setAttributes([1]) self.assertEqual(exporter.attributes(), [1]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "cost":6.8 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setAttributes([]) # text excluding attributes exporter.setExcludedAttributes([1]) self.assertEqual(exporter.excludedAttributes(), [1]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula", "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setExcludedAttributes([1, 2]) self.assertEqual(exporter.excludedAttributes(), [1, 2]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula" } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setExcludedAttributes([0, 1, 2]) self.assertEqual(exporter.excludedAttributes(), [0, 1, 2]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":null }""" self.assertEqual(exporter.exportFeature(feature), expected) # test that excluded attributes take precedence over included exporter.setAttributes([1, 2]) exporter.setExcludedAttributes([0, 1]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setAttributes([]) exporter.setExcludedAttributes([]) # test excluding geometry exporter.setIncludeGeometry(False) self.assertEqual(exporter.includeGeometry(), False) feature.setGeometry(QgsGeometry(QgsLineString(l))) expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setIncludeGeometry(True) feature.setGeometry(QgsGeometry(QgsPoint(5, 6))) # test excluding attributes exporter.setIncludeAttributes(False) self.assertEqual(exporter.includeAttributes(), False) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":null }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setIncludeGeometry(False) expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":null }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setIncludeAttributes(True) # test overriding ID expected = """{ "type":"Feature", "id":29, "geometry":null, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature, id=29), expected) # test injecting extra attributes expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198, "extra":"val1", "extra2":2 } }""" self.assertEqual( exporter.exportFeature(feature, extraProperties={ "extra": "val1", "extra2": 2 }), expected) exporter.setIncludeAttributes(False) expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "extra":"val1", "extra2":{"nested_map":5, "nested_map2":"val"}, "extra3":[1,2,3] } }""" expected2 = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "extra":"val1", "extra2":{"nested_map":5,"nested_map2":"val"}, "extra3":[1,2,3] } }""" exp_f = exporter.exportFeature(feature, extraProperties={ "extra": "val1", "extra2": { "nested_map": 5, "nested_map2": "val" }, "extra3": [1, 2, 3] }) self.assertTrue(exp_f == expected or exp_f == expected2) exporter.setIncludeGeometry(True)
def testQgsLineStringRepr(self): ls = QgsLineString([QgsPoint(10, 2), QgsPoint(10, 1), QgsPoint(5, 1)]) self.assertEqual(ls.__repr__(), '<QgsLineString: LineString (10 2, 10 1, 5 1)>')
def txestGeometryRendering(self): '''Tests rendering a bunch of different geometries, including bad/odd geometries.''' empty_multipolygon = QgsMultiPolygon() empty_multipolygon.addGeometry(QgsPolygon()) empty_polygon = QgsPolygon() empty_linestring = QgsLineString() tests = [{'name': 'Point', 'wkt': 'Point (1 2)', 'reference_image': 'point'}, {'name': 'MultiPoint', 'wkt': 'MultiPoint ((10 30),(40 20),(30 10),(20 10))', 'reference_image': 'multipoint'}, {'name': 'LineString', 'wkt': 'LineString (0 0,3 4,4 3)', 'reference_image': 'linestring'}, {'name': 'Empty LineString', 'geom': QgsGeometry(empty_linestring), 'reference_image': 'empty'}, {'name': 'MultiLineString', 'wkt': 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 1, 5 1, 5 0, 6 0))', 'reference_image': 'multilinestring'}, {'name': 'Polygon', 'wkt': 'Polygon ((0 0, 10 0, 10 10, 0 10, 0 0),(5 5, 7 5, 7 7 , 5 7, 5 5))', 'reference_image': 'polygon'}, {'name': 'Empty Polygon', 'geom': QgsGeometry(empty_polygon), 'reference_image': 'empty'}, {'name': 'MultiPolygon', 'wkt': 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))', 'reference_image': 'multipolygon'}, {'name': 'Empty MultiPolygon', 'geom': QgsGeometry(empty_multipolygon), 'reference_image': 'empty'}, {'name': 'CircularString', 'wkt': 'CIRCULARSTRING(268 415,227 505,227 406)', 'reference_image': 'circular_string'}, {'name': 'CompoundCurve', 'wkt': 'COMPOUNDCURVE((5 3, 5 13), CIRCULARSTRING(5 13, 7 15, 9 13), (9 13, 9 3), CIRCULARSTRING(9 3, 7 1, 5 3))', 'reference_image': 'compound_curve'}, {'name': 'CurvePolygon', 'wkt': 'CURVEPOLYGON(CIRCULARSTRING(1 3, 3 5, 4 7, 7 3, 1 3))', 'reference_image': 'curve_polygon'}, {'name': 'MultiCurve', 'wkt': 'MultiCurve((5 5,3 5,3 3,0 3),CIRCULARSTRING(0 0, 2 1,2 2))', 'reference_image': 'multicurve'}, {'name': 'CurvePolygon_no_arc', # refs #14028 'wkt': 'CURVEPOLYGON(LINESTRING(1 3, 3 5, 4 7, 7 3, 1 3))', 'reference_image': 'curve_polygon_no_arc'}] for test in tests: def get_geom(): if 'geom' not in test: geom = QgsGeometry.fromWkt(test['wkt']) assert geom and not geom.isNull(), 'Could not create geometry {}'.format(test['wkt']) else: geom = test['geom'] return geom geom = get_geom() rendered_image = self.renderGeometry(geom) assert self.imageCheck(test['name'], test['reference_image'], rendered_image) # Note - each test is repeated with the same geometry and reference image, but with added # z and m dimensions. This tests that presence of the dimensions does not affect rendering # test with Z geom_z = get_geom() geom_z.get().addZValue(5) rendered_image = self.renderGeometry(geom_z) assert self.imageCheck(test['name'] + 'Z', test['reference_image'], rendered_image) # test with ZM geom_z.get().addMValue(15) rendered_image = self.renderGeometry(geom_z) assert self.imageCheck(test['name'] + 'ZM', test['reference_image'], rendered_image) # test with M geom_m = get_geom() geom_m.get().addMValue(15) rendered_image = self.renderGeometry(geom_m) assert self.imageCheck(test['name'] + 'M', test['reference_image'], rendered_image)
def _lineGrid(self, sink, bbox, hSpacing, vSpacing, hOverlay, vOverlay, feedback): feat = QgsFeature() if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] count = 0 id = 1 # latitude lines count_max = bbox.height() / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): if feedback.isCanceled(): break pt1 = QgsPoint(bbox.xMinimum(), y) pt2 = QgsPoint(bbox.xMaximum(), y) line = QgsLineString([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) sink.addFeature(feat, QgsFeatureSink.FastInsert) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = bbox.width() / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): if feedback.isCanceled(): break pt1 = QgsPoint(x, bbox.yMaximum()) pt2 = QgsPoint(x, bbox.yMinimum()) line = QgsLineString([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) sink.addFeature(feat, QgsFeatureSink.FastInsert) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50))
def processAlgorithm(self, context, feedback): extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) hOverlay = self.getParameterValue(self.HOVERLAY) vOverlay = self.getParameterValue(self.VOVERLAY) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: {0}/{1}').format(hSpacing, vSpacing)) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise GeoAlgorithmExecutionException( self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = [QgsField('left', QVariant.Double, '', 24, 16), QgsField('top', QVariant.Double, '', 24, 16), QgsField('right', QVariant.Double, '', 24, 16), QgsField('bottom', QVariant.Double, '', 24, 16), QgsField('id', QVariant.Int, '', 10, 0), QgsField('coord', QVariant.Double, '', 24, 15) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs, context) if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPointV2(bbox.xMinimum(), y) pt2 = QgsPointV2(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPointV2(x, bbox.yMaximum()) pt2 = QgsPointV2(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50)) del writer
def testRenderWithTransform(self): layer = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer.isValid()) item = QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(11.5, 13), QgsPoint(12, 13), QgsPoint(12, 13.5), QgsPoint(11.5, 13) ]))) item.setSymbol( QgsFillSymbol.createSimple({ 'color': '200,100,100', 'outline_color': 'black', 'outline_width': '2' })) item.setZIndex(1) layer.addItem(item) item = QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)])) item.setSymbol( QgsLineSymbol.createSimple({ 'color': '#ffff00', 'line_width': '3' })) item.setZIndex(2) layer.addItem(item) item = QgsAnnotationMarkerItem(QgsPoint(12, 13)) item.setSymbol( QgsMarkerSymbol.createSimple({ 'color': '100,200,200', 'size': '6', 'outline_color': 'black' })) item.setZIndex(3) layer.addItem(item) layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) settings = QgsMapSettings() settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857')) settings.setExtent(QgsRectangle(1250958, 1386945, 1420709, 1532518)) settings.setOutputSize(QSize(300, 300)) settings.setFlag(QgsMapSettings.Antialiasing, False) rc = QgsRenderContext.fromMapSettings(settings) rc.setCoordinateTransform( QgsCoordinateTransform(layer.crs(), settings.destinationCrs(), QgsProject.instance())) image = QImage(200, 200, QImage.Format_ARGB32) image.setDotsPerMeterX(96 / 25.4 * 1000) image.setDotsPerMeterY(96 / 25.4 * 1000) image.fill(QColor(255, 255, 255)) painter = QPainter(image) rc.setPainter(painter) try: renderer = layer.createMapRenderer(rc) renderer.render() finally: painter.end() self.assertTrue( self.imageCheck('layer_render_transform', 'layer_render_transform', image))
def test_render_via_job_with_transform(self): """ Test rendering an annotation layer via a map render job """ layer = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer.isValid()) item = QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(11.5, 13), QgsPoint(12, 13), QgsPoint(12, 13.5), QgsPoint(11.5, 13) ]))) item.setSymbol( QgsFillSymbol.createSimple({ 'color': '200,100,100', 'outline_color': 'black', 'outline_width': '2' })) item.setZIndex(1) i1_id = layer.addItem(item) item = QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)])) item.setSymbol( QgsLineSymbol.createSimple({ 'color': '#ffff00', 'line_width': '3' })) item.setZIndex(2) i2_id = layer.addItem(item) item = QgsAnnotationMarkerItem(QgsPoint(12, 13)) item.setSymbol( QgsMarkerSymbol.createSimple({ 'color': '100,200,200', 'size': '6', 'outline_color': 'black' })) item.setZIndex(3) i3_id = layer.addItem(item) layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) settings = QgsMapSettings() settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857')) settings.setExtent(QgsRectangle(1250958, 1386945, 1420709, 1532518)) settings.setOutputSize(QSize(200, 200)) settings.setLayers([layer]) job = QgsMapRendererSequentialJob(settings) job.start() job.waitForFinished() # check rendered item results item_results = job.takeRenderedItemResults() item_details = item_results.renderedItems() self.assertEqual(len(item_details), 3) self.assertEqual([i.layerId() for i in item_details], [layer.id()] * 3) self.assertCountEqual([i.itemId() for i in item_details], [i1_id, i2_id, i3_id]) # bounds should be in map crs self.assertEqual( [ QgsGeometry.fromRect(i.boundingBox()).asWkt(0) for i in item_details if i.itemId() == i1_id ][0], 'Polygon ((1280174 1459732, 1335834 1459732, 1335834 1516914, 1280174 1516914, 1280174 1459732))' ) self.assertEqual( [ QgsGeometry.fromRect(i.boundingBox()).asWkt(0) for i in item_details if i.itemId() == i2_id ][0], 'Polygon ((1224514 1459732, 1335834 1459732, 1335834 1689200, 1224514 1689200, 1224514 1459732))' ) expected = 'Polygon ((1325786 1449684, 1345882 1449684, 1345882 1469780, 1325786 1469780, 1325786 1449684))' result = [ QgsGeometry.fromRect(i.boundingBox()).asWkt(0) for i in item_details if i.itemId() == i3_id ][0] self.assertTrue( compareWkt(result, expected, tol=1000), "mismatch Expected:\n{}\nGot:\n{}\n".format(expected, result))
def testItems(self): layer = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer.isValid()) self.assertTrue(layer.isEmpty()) polygon_item_id = layer.addItem( QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(12, 13), QgsPoint(14, 13), QgsPoint(14, 15), QgsPoint(12, 13) ])))) linestring_item_id = layer.addItem( QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)]))) marker_item_id = layer.addItem( QgsAnnotationMarkerItem(QgsPoint(12, 13))) self.assertEqual(len(layer.items()), 3) self.assertFalse(layer.isEmpty()) self.assertIsInstance(layer.items()[polygon_item_id], QgsAnnotationPolygonItem) self.assertIsInstance(layer.items()[linestring_item_id], QgsAnnotationLineItem) self.assertIsInstance(layer.items()[marker_item_id], QgsAnnotationMarkerItem) self.assertFalse(layer.removeItem('xxxx')) self.assertEqual(len(layer.items()), 3) self.assertTrue(layer.removeItem(linestring_item_id)) self.assertEqual(len(layer.items()), 2) self.assertIsInstance(layer.items()[polygon_item_id], QgsAnnotationPolygonItem) self.assertIsInstance(layer.items()[marker_item_id], QgsAnnotationMarkerItem) self.assertFalse(layer.removeItem(linestring_item_id)) self.assertTrue(layer.removeItem(polygon_item_id)) self.assertEqual(len(layer.items()), 1) self.assertIsInstance(layer.items()[marker_item_id], QgsAnnotationMarkerItem) self.assertTrue(layer.removeItem(marker_item_id)) self.assertEqual(len(layer.items()), 0) self.assertTrue(layer.isEmpty()) layer.addItem( QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(12, 13), QgsPoint(14, 13), QgsPoint(14, 15), QgsPoint(12, 13) ])))) layer.addItem( QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)]))) layer.addItem(QgsAnnotationMarkerItem(QgsPoint(12, 13))) self.assertEqual(len(layer.items()), 3) layer.clear() self.assertEqual(len(layer.items()), 0)
def get_mesh_geometry(self, mesh, index): edge = mesh.edge(index) start_point = mesh.vertex(edge[0]) end_point = mesh.vertex(edge[1]) line = QgsLineString(start_point, end_point) return QgsGeometry(line)
def test_rendered_item_results_remove_outdated(self): """ Test that outdated results are removed from rendered item result caches """ canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326')) canvas.setFrameStyle(0) canvas.resize(600, 400) canvas.setCachingEnabled(True) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer.isValid()) layer2 = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer2.isValid()) item = QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(11.5, 13), QgsPoint(12, 13), QgsPoint(12, 13.5), QgsPoint(11.5, 13) ]))) item.setSymbol( QgsFillSymbol.createSimple({ 'color': '200,100,100', 'outline_color': 'black', 'outline_width': '2' })) item.setZIndex(1) i1_id = layer.addItem(item) item = QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)])) item.setZIndex(2) i2_id = layer.addItem(item) item = QgsAnnotationMarkerItem(QgsPoint(12, 13)) item.setZIndex(3) i3_id = layer2.addItem(item) layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) layer2.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) canvas.setLayers([layer, layer2]) canvas.setExtent(QgsRectangle(10, 10, 18, 18)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() results = canvas.renderedItemResults() self.assertCountEqual([i.itemId() for i in results.renderedItems()], [i1_id, i2_id, i3_id]) # now try modifying an annotation in the layer -- it will redraw, and we don't want to reuse any previously # cached rendered item results for this layer! item = QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(11.5, 13), QgsPoint(12.5, 13), QgsPoint(12.5, 13.5), QgsPoint(11.5, 13) ]))) item.setZIndex(1) layer.replaceItem(i1_id, item) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() item = QgsAnnotationMarkerItem(QgsPoint(17, 18)) item.setZIndex(3) layer2.replaceItem(i3_id, item) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() results = canvas.renderedItemResults() items_in_bounds = results.renderedAnnotationItemsInBounds( QgsRectangle(10, 10, 15, 15)) self.assertCountEqual([i.itemId() for i in items_in_bounds], [i1_id, i2_id]) items_in_bounds = results.renderedAnnotationItemsInBounds( QgsRectangle(15, 15, 20, 20)) self.assertCountEqual([i.itemId() for i in items_in_bounds], [i3_id])
def test_rendered_items(self): canvas = QgsMapCanvas() canvas.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326')) canvas.setFrameStyle(0) canvas.resize(600, 400) canvas.setCachingEnabled(True) self.assertEqual(canvas.width(), 600) self.assertEqual(canvas.height(), 400) layer = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer.isValid()) layer2 = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer2.isValid()) item = QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(11.5, 13), QgsPoint(12, 13), QgsPoint(12, 13.5), QgsPoint(11.5, 13) ]))) item.setSymbol( QgsFillSymbol.createSimple({ 'color': '200,100,100', 'outline_color': 'black', 'outline_width': '2' })) item.setZIndex(1) i1_id = layer.addItem(item) item = QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)])) item.setZIndex(2) i2_id = layer.addItem(item) item = QgsAnnotationMarkerItem(QgsPoint(12, 13)) item.setZIndex(3) i3_id = layer2.addItem(item) layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) layer2.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) canvas.setLayers([layer, layer2]) canvas.setExtent(QgsRectangle(10, 10, 18, 18)) canvas.show() # need to wait until first redraw can occur (note that we first need to wait till drawing starts!) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() results = canvas.renderedItemResults() self.assertCountEqual([i.itemId() for i in results.renderedItems()], [i1_id, i2_id, i3_id]) # turn off a layer -- the other layer will be rendered direct from the cached version canvas.setLayers([layer2]) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() results = canvas.renderedItemResults() # only layer2 items should be present in results -- but these MUST be present while layer2 is visible in the canvas, # even though the most recent canvas redraw used a cached version of layer2 and didn't actually have to redraw the layer self.assertEqual([i.itemId() for i in results.renderedItems()], [i3_id]) # turn layer 1 back on canvas.setLayers([layer, layer2]) while not canvas.isDrawing(): app.processEvents() canvas.waitWhileRendering() results = canvas.renderedItemResults() # both layer1 and layer2 items should be present in results -- even though NEITHER of these layers were re-rendered, # and instead we used precached renders of both layers self.assertCountEqual([i.itemId() for i in results.renderedItems()], [i1_id, i2_id, i3_id])
def test_merge_nodes(self): error_tolerance = 0.0001 axial_lines = TestRCLCleaner.make_geometry_feature_layer( "LineString", [ QgsLineString([ QgsPoint(535088.141198, 185892.128181), QgsPoint(535037.617992, 186342.145327) ]), QgsLineString([ QgsPoint(534931.347277, 186103.472964), QgsPoint(535285.548630, 186203.990233) ]), QgsLineString([ QgsPoint(534952.224080, 186285.463215), QgsPoint(535248.881516, 185907.343107) ]) ]) unlinks = TestRCLCleaner.make_geometry_feature_layer("Point", []) # segment the axial lines without removing stubs stub_ratio = 0 buffer = 1 errors = True my_segmentor = segmentor(axial_lines, unlinks, stub_ratio, buffer, errors) my_segmentor.step = 10 / float(my_segmentor.layer.featureCount()) my_segmentor.load_graph() # self.step specified in load_graph # progress emitted by break_segm & break_feats_iter cross_p_list = [ my_segmentor.break_segm(feat) for feat in my_segmentor.list_iter( list(my_segmentor.feats.values())) ] my_segmentor.step = 20 / float(len(cross_p_list)) segmented_feats = [ my_segmentor.copy_feat(feat_geom_fid[0], feat_geom_fid[1], feat_geom_fid[2]) for feat_geom_fid in my_segmentor.break_feats_iter(cross_p_list) ] segmented_geometry = [ segmented_feat.geometry() for segmented_feat in segmented_feats ] segment_layer = TestRCLCleaner.make_geometry_feature_layer( "LineString", segmented_geometry) # segment the axial lines without removing stubs self.assertEqual(segment_layer.featureCount(), 9) # cleaning settings snap_threshold = 10 # TODO: Test break_at_vertices = True # TODO: Test merge_type = 'intersections' # TODO: Test # merge_type = 'colinear' # TODO: Test collinear_threshold = 0 # TODO: Test angle_threshold = 10 # TODO: Test fix_unlinks = True # TODO: Test orphans = True # TODO: Test get_unlinks = True # TODO: Test [ load_range, cl1_range, cl2_range, cl3_range, break_range, merge_range, snap_range, unlinks_range, fix_range ] = RoadNetworkCleanerDialog.get_progress_ranges( break_at_vertices, merge_type, snap_threshold, get_unlinks, fix_unlinks) points = [] multiparts = [] pseudo_graph = sGraph({}, {}) if break_at_vertices: pseudo_graph.step = load_range / float( segment_layer.featureCount()) graph = sGraph({}, {}) graph.total_progress = load_range pseudo_graph.load_edges_w_o_topology( clean_features_iter(segment_layer.getFeatures())) # QgsMessageLog.logMessage('pseudo_graph edges added %s' % load_range, level=Qgis.Critical) pseudo_graph.step = break_range / float(len(pseudo_graph.sEdges)) graph.load_edges( pseudo_graph.break_features_iter(get_unlinks, angle_threshold, fix_unlinks), angle_threshold) # QgsMessageLog.logMessage('pseudo_graph edges broken %s' % break_range, level=Qgis.Critical) else: graph = sGraph({}, {}) graph.step = load_range / float(segment_layer.featureCount()) graph.load_edges(clean_features_iter(segment_layer.getFeatures()), angle_threshold) # QgsMessageLog.logMessage('graph edges added %s' % load_range, level=Qgis.Critical) graph.step = cl1_range / (float(len(graph.sEdges)) * 2.0) if orphans: graph.clean(True, False, snap_threshold, True) else: graph.clean(True, False, snap_threshold, False) # QgsMessageLog.logMessage('graph clean parallel and closed pl %s' % cl1_range, level=Qgis.Critical) if fix_unlinks: graph.step = fix_range / float(len(graph.sEdges)) graph.fix_unlinks() # QgsMessageLog.logMessage('unlinks added %s' % fix_range, level=Qgis.Critical) if snap_threshold != 0: graph.step = snap_range / float(len(graph.sNodes)) graph.snap_endpoints(snap_threshold) # QgsMessageLog.logMessage('snap %s' % snap_range, level=Qgis.Critical) graph.step = cl2_range / (float(len(graph.sEdges)) * 2.0) if orphans: graph.clean(True, False, snap_threshold, True) else: graph.clean(True, False, snap_threshold, False) # QgsMessageLog.logMessage('clean %s' % cl2_range, level=Qgis.Critical) if merge_type == 'intersections': graph.step = merge_range / float(len(graph.sNodes)) graph.merge_b_intersections(angle_threshold) # QgsMessageLog.logMessage('merge %s %s angle_threshold ' % (merge_range, angle_threshold), # level=Qgis.Critical) elif merge_type == 'collinear': graph.step = merge_range / float(len(graph.sEdges)) graph.merge_collinear(collinear_threshold, angle_threshold) # QgsMessageLog.logMessage('merge %s' % merge_range, level=Qgis.Critical) # cleaned multiparts so that unlinks are generated properly if orphans: graph.step = cl3_range / (float(len(graph.sEdges)) * 2.0) graph.clean(True, orphans, snap_threshold, False, True) else: graph.step = cl3_range / (float(len(graph.sEdges)) * 2.0) graph.clean(True, False, snap_threshold, False, True) if get_unlinks: graph.step = unlinks_range / float(len(graph.sEdges)) graph.generate_unlinks() unlinks = graph.unlinks else: unlinks = [] cleaned_features = [e.feature for e in list(graph.sEdges.values())] # add to errors multiparts and points graph.errors += multiparts graph.errors += points expected_segments = [ QgsLineString([ QgsPoint(535088.141198, 185892.128181), QgsPoint(535061.604423, 186143.502228) ]), QgsLineString([ QgsPoint(535061.604423, 186143.502228), QgsPoint(535037.617992, 186342.145327) ]), QgsLineString([ QgsPoint(534931.347277, 186103.472964), QgsPoint(535061.604423, 186143.502228) ]), QgsLineString([ QgsPoint(535061.604423, 186143.502228), QgsPoint(535285.548630, 186203.990233) ]), QgsLineString([ QgsPoint(534952.224080, 186285.463215), QgsPoint(535061.604423, 186143.502228) ]), QgsLineString([ QgsPoint(535061.604423, 186143.502228), QgsPoint(535248.881516, 185907.343107) ]) ] self.assertEqual(len(cleaned_features), len(expected_segments)) for cleaned_feature in cleaned_features: cleaned_line = cleaned_feature.geometry().asPolyline() for expected_segment in expected_segments: x1eq = abs(cleaned_line[0].x() - expected_segment[0].x()) < error_tolerance y1eq = abs(cleaned_line[0].y() - expected_segment[0].y()) < error_tolerance x2eq = abs(cleaned_line[1].x() - expected_segment[1].x()) < error_tolerance y2eq = abs(cleaned_line[1].y() - expected_segment[1].y()) < error_tolerance if x1eq and y1eq and x2eq and y2eq: expected_segments.remove(expected_segment) break # all the expected features should have been found and removed self.assertEqual(len(expected_segments), 0) expected_errors = [ QgsPoint(535060.304968, 186140.069309), QgsPoint(535059.304801, 186148.977932), QgsPoint(535065.203499, 186141.459442) ] self.assertEqual(len(graph.errors), len(expected_errors)) for break_point_feat in graph.errors: break_point = break_point_feat.geometry().asPoint() for expected_break_point in expected_errors: xeq = abs(break_point.x() - expected_break_point.x()) < error_tolerance yeq = abs(break_point.y() - expected_break_point.y()) < error_tolerance if xeq and yeq: expected_errors.remove(expected_break_point) break # all the expected errors should have been found and removed self.assertEqual(len(expected_errors), 0)
def _correct_connections(self, line_points): """We prolong the lines in a case when the connection is not in normal angle""" i = 0 previous_diff = 90 while i < (len(line_points) - 3): diff = self._get_azimuth_diff(line_points[i], line_points[i + 1], line_points[i + 2]) if not ((math.fabs(diff) > 85 and math.fabs(diff) < 95) or (math.fabs(diff) > 265 and math.fabs(diff) < 275)): # The angle is not close to normal if i == 0: # We are at the beginning and do not have previous diff, so we read next diff # TODO possible change to detect diff according to engles of the area, better results previous_diff = self._get_azimuth_diff( line_points[2], line_points[3], line_points[4]) distance_current = line_points[i].distance(line_points[i + 1]) distance_next = line_points[i + 2].distance(line_points[i + 3]) line_g = QgsGeometry.fromPolylineXY( [line_points[i + 1], line_points[i + 2]]) # hack - there is a problem of intersection on Windows - two lines that does not intersect # have intersection result close to 0 0, but in digits of e-300 intersection_error_limit = 0.000000000001 if distance_current > distance_next: # we go from longer to shorter line line = QgsLineString(QgsPoint(line_points[i + 2]), QgsPoint(line_points[i + 3])) line.extend(distance_current, 0) # We rotate the line segment to find cross with extended line # The rotation is based on diff (rotate to be along extended line) # and angle of the previous normal line line_g.rotate(diff + (180 - previous_diff), line_points[i + 1]) line = QgsGeometry.fromWkt(line.asWkt()) intersection = line_g.intersection(line).centroid() if not intersection.isNull(): intersection = intersection.asPoint() # hack if intersection.x( ) > intersection_error_limit and intersection.y( ) > intersection_error_limit: line_points[i + 2] = intersection else: # we go from shorter to longer line line = QgsLineString(QgsPoint(line_points[i]), QgsPoint(line_points[i + 1])) line.extend(0, distance_next) line_g.rotate(diff + (180 - previous_diff), line_points[i + 2]) line = QgsGeometry.fromWkt(line.asWkt()) intersection = line_g.intersection(line).centroid() if not intersection.isNull(): intersection = intersection.asPoint() # hack if intersection.x( ) > intersection_error_limit and intersection.y( ) > intersection_error_limit: line_points[i + 1] = intersection i += 2 previous_diff = diff return line_points
def processAlgorithm(self, parameters, context, feedback): """ RUN PROCESS """ # READ NETWORK landxmlfn = self.parameterAsFile(parameters, self.INPUT, context) imp_net = landxml.network_from_xml(landxmlfn) networks = imp_net['networks'] if 'epsg_code' in imp_net: crs = QgsCoordinateReferenceSystem('EPSG:' + imp_net['epsg_code']) else: crs = QgsCoordinateReferenceSystem('WKT:' + imp_net['wkt_crs']) # SHOW INFO feedback.pushInfo('=' * 40) feedback.pushInfo('CRS is {}'.format(crs.authid())) # GENERATE SINK LAYER newfields = QgsFields() newfields.append(QgsField("network", QVariant.String)) newfields.append(QgsField("network_type", QVariant.String)) newfields.append(QgsField("id", QVariant.String)) newfields.append(QgsField("elev_sump", QVariant.Double)) newfields.append(QgsField("elev_rim", QVariant.Double)) newfields.append(QgsField("depth", QVariant.Double)) (node_sink, node_id) = self.parameterAsSink(parameters, self.NODE_OUTPUT, context, newfields, QgsWkbTypes.Point, crs) newfields = QgsFields() newfields.append(QgsField("network", QVariant.String)) newfields.append(QgsField("network_type", QVariant.String)) newfields.append(QgsField("id", QVariant.String)) newfields.append(QgsField("start", QVariant.String)) newfields.append(QgsField("end", QVariant.String)) newfields.append(QgsField("start_elv", QVariant.Double)) newfields.append(QgsField("start_offset", QVariant.Double)) newfields.append(QgsField("start_depth", QVariant.Double)) newfields.append(QgsField("end_elv", QVariant.Double)) newfields.append(QgsField("end_offset", QVariant.Double)) newfields.append(QgsField("end_depth", QVariant.Double)) newfields.append(QgsField("length", QVariant.Double)) newfields.append(QgsField("slope", QVariant.Double)) newfields.append(QgsField("sect_type", QVariant.String)) newfields.append(QgsField("section", QVariant.String)) (link_sink, link_id) = self.parameterAsSink(parameters, self.LINK_OUTPUT, context, newfields, QgsWkbTypes.LineString, crs) netcnt = nodcnt = lnkcnt = 0 # ADD NETWORK for netname, network in networks.items(): netcnt += 1 net_type = network['net_type'] # ADD NODES for nodename, node in network['nodes'].items(): nodcnt += 1 f = QgsFeature() point = QgsPoint(node['x'], node['y']) f.setGeometry(point) f.setAttributes([ netname, net_type, nodename, node['elev_sump'], node['elev_rim'], node['depth'] ]) node_sink.addFeature(f) # ADD LINKS for linkname, link in network['links'].items(): lnkcnt += 1 g = QgsFeature() x = network['nodes'][link['start']]['x'] y = network['nodes'][link['start']]['y'] spoint = QgsPoint(x, y) x = network['nodes'][link['end']]['x'] y = network['nodes'][link['end']]['y'] epoint = QgsPoint(x, y) g.setGeometry(QgsGeometry(QgsLineString([spoint, epoint]))) g.setAttributes([ netname, net_type, linkname, link['start'], link['end'], link['start_elev'], link['start_offset'], link['start_depth'], link['end_elev'], link['end_offset'], link['end_depth'], link['length'], link['slope'], link['sect_type'], link['section'] ]) link_sink.addFeature(g) # SHOW PROGRESS feedback.setProgress(100) # Update the progress bar # SHOW INFO msg = 'LandXML Loaded successfully.' feedback.pushInfo(msg) msg = 'Added: {} networks.'.format(netcnt) feedback.pushInfo(msg) msg = 'Added: {} nodes.'.format(nodcnt) feedback.pushInfo(msg) msg = 'Added: {} links.'.format(lnkcnt) feedback.pushInfo(msg) feedback.pushInfo('=' * 40) # PROCCES CANCELED if feedback.isCanceled(): return {} # OUTPUT return {self.NODE_OUTPUT: node_id, self.LINK_OUTPUT: link_id}
def processAlgorithm(self, parameters, context, feedback): hSpacing = self.parameterAsDouble(parameters, self.HSPACING, context) vSpacing = self.parameterAsDouble(parameters, self.VSPACING, context) hOverlay = self.parameterAsDouble(parameters, self.HOVERLAY, context) vOverlay = self.parameterAsDouble(parameters, self.VOVERLAY, context) crs = self.parameterAsCrs(parameters, self.CRS, context) bbox = self.parameterAsExtent(parameters, self.EXTENT, context, crs) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise QgsProcessingException( self.tr('Invalid grid spacing: {0}/{1}').format(hSpacing, vSpacing)) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise QgsProcessingException( self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) if width < hSpacing: raise QgsProcessingException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise QgsProcessingException( self.tr('Vertical spacing is too small for the covered area')) fields = QgsFields() fields.append(QgsField('left', QVariant.Double, '', 24, 16)) fields.append(QgsField('top', QVariant.Double, '', 24, 16)) fields.append(QgsField('right', QVariant.Double, '', 24, 16)) fields.append(QgsField('bottom', QVariant.Double, '', 24, 16)) fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('coord', QVariant.Double, '', 24, 15)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, crs) if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): if feedback.isCanceled(): break pt1 = QgsPoint(bbox.xMinimum(), y) pt2 = QgsPoint(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) sink.addFeature(feat, QgsFeatureSink.FastInsert) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): if feedback.isCanceled(): break pt1 = QgsPoint(x, bbox.yMaximum()) pt2 = QgsPoint(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) sink.addFeature(feat, QgsFeatureSink.FastInsert) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, progress): extent = self.getParameterValue(self.EXTENT).split(",") hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException(self.tr("Invalid grid spacing: %s/%s" % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException(self.tr("Horizontal spacing is too small for the covered area")) if height < vSpacing: raise GeoAlgorithmExecutionException(self.tr("Vertical spacing is too small for the covered area")) fields = [ QgsField("left", QVariant.Double, "", 24, 16), QgsField("top", QVariant.Double, "", 24, 16), QgsField("right", QVariant.Double, "", 24, 16), QgsField("bottom", QVariant.Double, "", 24, 16), QgsField("id", QVariant.Int, "", 10, 0), QgsField("coord", QVariant.Double, "", 24, 15), ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs) feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPointV2(bbox.xMinimum(), y) pt2 = QgsPointV2(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat) y = y - vSpacing id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: progress.setPercentage(int(count / count_max * 50)) progress.setPercentage(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPointV2(x, bbox.yMaximum()) pt2 = QgsPointV2(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat) x = x + hSpacing id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: progress.setPercentage(50 + int(count / count_max * 50)) del writer
def testLineGenerationRelativeExtrusion(self): vl = QgsVectorLayer('LineStringZ?crs=EPSG:27700', 'lines', 'memory') self.assertTrue(vl.isValid()) for line in ['LineStringZ(322006 129874 12, 322008 129910 13, 322038 129909 14, 322037 129868 15)', 'LineStringZ(322068 129900 16, 322128 129813 17)', 'LineStringZ(321996 129914 11, 321990 129896 15)', 'LineStringZ(321595 130176 1, 321507 130104 10)', 'LineStringZ(321558 129930 1, 321568 130029 10, 321516 130049 5)', 'LineStringZ(321603 129967 3, 321725 130042 9)']: f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt(line)) self.assertTrue(vl.dataProvider().addFeature(f)) vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Relative) vl.elevationProperties().setZScale(2.5) vl.elevationProperties().setZOffset(10) vl.elevationProperties().setExtrusionEnabled(True) vl.elevationProperties().setExtrusionHeight(7) curve = QgsLineString() curve.fromWkt( 'LineString (-347692.88994020794052631 6632796.97473032586276531, -346576.99897185183363035 6632367.38372825458645821, -346396.02439485350623727 6632344.35087973903864622, -346374.34608158958144486 6632220.09952207934111357)') req = QgsProfileRequest(curve) req.setTransformContext(self.create_transform_context()) rl = QgsRasterLayer(os.path.join(unitTestDataPath(), '3d', 'dtm.tif'), 'DTM') self.assertTrue(rl.isValid()) terrain_provider = QgsRasterDemTerrainProvider() terrain_provider.setLayer(rl) terrain_provider.setScale(0.3) terrain_provider.setOffset(-5) req.setTerrainProvider(terrain_provider) req.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) generator = vl.createProfileGenerator(req) self.assertTrue(generator.generateProfile()) results = generator.takeResults() if QgsProjUtils.projVersionMajor() >= 8: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {675.2: 84.2, 1195.7: 86.8, 1223.1: 81.4, 1272.0: 90.0, 1339.4: 98.7, 1444.4: 100.0}) else: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {675.2: 80.5, 1195.7: 90.5, 1223.1: 87.4, 1272.0: 94.5, 1339.4: 98.0, 1444.4: 94.0}) if QgsProjUtils.projVersionMajor() >= 8: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], ['LineStringZ (-346549.8 6632363.9 81.4, -346549.8 6632363.9 88.4)', 'LineStringZ (-346501.4 6632357.8 90, -346501.4 6632357.8 97)', 'LineStringZ (-346434.5 6632349.2 98.7, -346434.5 6632349.2 105.7)', 'LineStringZ (-346384.6 6632279.1 100, -346384.6 6632279.1 107)', 'LineStringZ (-346577 6632367.4 86.8, -346577 6632367.4 93.8)', 'LineStringZ (-347062.8 6632554.4 84.2, -347062.8 6632554.4 91.2)']) self.assertAlmostEqual(results.zRange().lower(), 81.3588, 2) self.assertAlmostEqual(results.zRange().upper(), 107.009, 2) else: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], ['LineStringZ (-346549.8 6632363.9 87.4, -346549.8 6632363.9 94.4)', 'LineStringZ (-346501.4 6632357.8 94.5, -346501.4 6632357.8 101.5)', 'LineStringZ (-346434.5 6632349.2 98, -346434.5 6632349.2 105)', 'LineStringZ (-346384.6 6632279.1 94, -346384.6 6632279.1 101)', 'LineStringZ (-346577 6632367.4 90.5, -346577 6632367.4 97.5)', 'LineStringZ (-347062.8 6632554.4 80.5, -347062.8 6632554.4 87.5)']) self.assertAlmostEqual(results.zRange().lower(), 80.45645, 2) self.assertAlmostEqual(results.zRange().upper(), 104.9811499, 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)) 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) mvalue_exp = self.parameterAsExpression(parameters, self.MVALUE_EXPRESSION, context) text_dir = self.parameterAsString(parameters, self.OUTPUT_TEXT_DIR, context) QgsMessageLog.logMessage("-"*80) QgsMessageLog.logMessage(str(mvalue_exp)) QgsMessageLog.logMessage(mvalue_exp.__class__.__name__) # instantiate the expression if mvalue is set if mvalue_exp: expression = QgsExpression(mvalue_exp) expression_ctx = self.createExpressionContext(parameters, context) expression.prepare(expression_ctx) else: expression = None 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()) or mvalue_exp: 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() referencedColumns = expression.referencedAttributeIndexes(source.fields()) if expression else [group_field_index, order_field_index] features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(referencedColumns), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().constGet().clone() if group_field_index >= 0: group = f.attributes()[group_field_index] else: group = 1 order = f.attributes()[order_field_index] if date_format != '': order = datetime.strptime(str(order), date_format) if group in points: points[group].append((order, point, f)) else: points[group] = [(order, point, f)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) current = 0 total = 100.0 / len(points) if points else 1 for group, vertices in list(points.items()): if feedback.isCanceled(): break vertices.sort(key=lambda x: (x[0] is None, x[0])) f = QgsFeature() attributes = [] if group_field_index >= 0: attributes.append(group) attributes.extend([vertices[0][0], vertices[-1][0]]) f.setAttributes(attributes) line = [node[1] for node in vertices] if text_dir: fileName = os.path.join(text_dir, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') for i in range(len(line)): if i == 0: fl.write('startAt=%f;%f;90\n' % (line[i].x(), line[i].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(QgsPointXY(line[i - 1]), QgsPointXY(line[i])) fl.write('%f;%f;90\n' % (angle, distance)) linestring = QgsLineString(line) if mvalue_exp: linestring.addMValue(0) for i, node in enumerate(vertices): QgsMessageLog.logMessage(node[2].__class__.__name__) expression_ctx.setFeature(node[2]) mvalue = expression.evaluate(expression_ctx) if expression.hasEvalError(): raise QgsProcessingException( self.tr('Evaluation error: {0}').format(expression.evalErrorString())) QgsMessageLog.logMessage("evaluating mvalue at {} to {} {}".format(i, mvalue, mvalue.__class__)) # mvalue = 12 # TODO : remove QgsMessageLog.logMessage("setting mvalue at {} to {} {}".format(i, mvalue, mvalue.__class__)) linestring.setMAt(i, mvalue) # test = linestring.mAt(i) # TODO : remove # QgsMessageLog.logMessage("mvalue at {} is {} {}".format(i, test, test.__class__)) # TODO : remove f.setGeometry(QgsGeometry(linestring)) sink.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def testPolygonGenerationRelativeExtrusion(self): vl = QgsVectorLayer('PolygonZ?crs=EPSG:27700', 'lines', 'memory') self.assertTrue(vl.isValid()) for line in [ 'PolygonZ ((321829.48893365426920354 129991.38697145861806348 1, 321847.89668515208177269 129996.63588572069420479 1, 321848.97131609614007175 129979.22330882755341008 1, 321830.31725845142500475 129978.07136809575604275 1, 321829.48893365426920354 129991.38697145861806348 1))', 'PolygonZ ((321920.00953056826256216 129924.58260190498549491 2, 321924.65299345907988027 129908.43546159457764588 2, 321904.78543491888558492 129903.99811821122420952 2, 321900.80605239619035274 129931.39860145389684476 2, 321904.84799937985371798 129931.71552911199978553 2, 321908.93646715773502365 129912.90030360443051904 2, 321914.20495146053144708 129913.67693978428724222 2, 321911.30165811872575432 129923.01272751353099011 2, 321920.00953056826256216 129924.58260190498549491 2))', 'PolygonZ ((321923.10517279652412981 129919.61521573827485554 3, 321922.23537852568551898 129928.3598982143739704 3, 321928.60423935484141111 129934.22530528216157109 3, 321929.39881197665818036 129923.29054521876969375 3, 321930.55804549407912418 129916.53248518184409477 3, 321923.10517279652412981 129919.61521573827485554 3))', 'PolygonZ ((321990.47451346553862095 129909.63588680300745182 4, 321995.04325810901354998 129891.84052284323843196 4, 321989.66826330573530868 129890.5092018858413212 4, 321990.78512359503656626 129886.49917887404444627 4, 321987.37291929306229576 129885.64982962771318853 4, 321985.2254804756375961 129893.81317058412241749 4, 321987.63158903241856024 129894.41078495365218259 4, 321984.34022761805681512 129907.57450046355370432 4, 321990.47451346553862095 129909.63588680300745182 4))', 'PolygonZ ((322103.03910495212767273 129795.91051736124791205 5, 322108.25568856322206557 129804.76113295342656784 5, 322113.29666162584908307 129803.9285887333098799 5, 322117.78645010641776025 129794.48194090687320568 5, 322103.03910495212767273 129795.91051736124791205 5))']: f = QgsFeature() f.setGeometry(QgsGeometry.fromWkt(line)) self.assertTrue(vl.dataProvider().addFeature(f)) vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Relative) vl.elevationProperties().setZScale(2.5) vl.elevationProperties().setZOffset(10) vl.elevationProperties().setExtrusionEnabled(True) vl.elevationProperties().setExtrusionHeight(7) curve = QgsLineString() curve.fromWkt( 'LineString (-347701.59207547508412972 6632766.96282589063048363, -346577.00878971704514697 6632369.7371364813297987, -346449.93654899462126195 6632331.81857067719101906, -346383.52035177784273401 6632216.85897350125014782)') req = QgsProfileRequest(curve) req.setTransformContext(self.create_transform_context()) rl = QgsRasterLayer(os.path.join(unitTestDataPath(), '3d', 'dtm.tif'), 'DTM') self.assertTrue(rl.isValid()) terrain_provider = QgsRasterDemTerrainProvider() terrain_provider.setLayer(rl) terrain_provider.setScale(0.3) terrain_provider.setOffset(-5) req.setTerrainProvider(terrain_provider) req.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) generator = vl.createProfileGenerator(req) self.assertTrue(generator.generateProfile()) results = generator.takeResults() if QgsProjUtils.projVersionMajor() >= 8: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {1041.8: 60.3, 1042.4: 60.2, 1049.5: 60.2, 1070.2: 60.2, 1073.1: 60.2, 1074.8: 60.3, 1078.9: 62.0, 1083.9: 62.0, 1091.1: 62.0, 1186.8: 59.3, 1189.8: 59.2, 1192.7: 59.2, 1199.2: 59.2, 1450.0: 65.5, 1455.6: 65.5, 1458.1: 65.5}) self.assertAlmostEqual(results.zRange().lower(), 59.2499, 2) self.assertAlmostEqual(results.zRange().upper(), 72.50000, 2) else: self.assertEqual(self.round_dict(results.distanceToHeightMap(), 1), {1041.8: 53.5, 1042.4: 53.5, 1049.5: 53.5, 1070.2: 53.5, 1073.1: 53.5, 1074.8: 53.5, 1078.9: 56.0, 1083.9: 56.0, 1091.1: 56.0, 1186.8: 62.3, 1189.8: 62.3, 1192.7: 62.3, 1199.2: 62.2, 1450.0: 67.0, 1455.6: 67.0, 1458.1: 67.0}) self.assertAlmostEqual(results.zRange().lower(), 53.5, 2) self.assertAlmostEqual(results.zRange().upper(), 74.00000, 2) if QgsProjUtils.projVersionMajor() >= 8: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], [ 'PolygonZ ((-346718.7 6632419.8 60.3, -346712 6632417.4 60.3, -346712 6632417.4 67.3, -346718.7 6632419.8 67.3, -346718.7 6632419.8 60.3))', 'PolygonZ ((-346719.3 6632420 60.3, -346718.7 6632419.8 60.2, -346718.7 6632419.8 67.3, -346719.3 6632420 67.3, -346719.3 6632420 60.3))', 'PolygonZ ((-346689.7 6632409.5 60.3, -346688.2 6632409 60.3, -346688.2 6632409 67.3, -346689.7 6632409.5 67.3, -346689.7 6632409.5 60.3))', 'PolygonZ ((-346692.5 6632410.5 60.3, -346689.7 6632409.5 60.3, -346689.7 6632409.5 67.3, -346692.5 6632410.5 67.3, -346692.5 6632410.5 60.3))', 'PolygonZ ((-346684.3 6632407.6 62, -346679.6 6632406 62, -346679.6 6632406 69, -346684.3 6632407.6 69, -346684.3 6632407.6 62))', 'PolygonZ ((-346679.6 6632406 62, -346672.8 6632403.6 62, -346672.8 6632403.6 69, -346679.6 6632406 69, -346679.6 6632406 62))', 'PolygonZ ((-346582.6 6632371.7 59.3, -346579.7 6632370.7 59.3, -346579.7 6632370.7 66.3, -346582.6 6632371.7 66.3, -346582.6 6632371.7 59.3))', 'PolygonZ ((-346579.7 6632370.7 59.3, -346577 6632369.7 59.2, -346570.8 6632367.9 59.3, -346570.8 6632367.9 66.3, -346577 6632369.7 66.3, -346579.7 6632370.7 66.3, -346579.7 6632370.7 59.3))', 'PolygonZ ((-346387.6 6632223.9 65.5, -346384.8 6632219 65.5, -346384.8 6632219 72.5, -346387.6 6632223.9 72.5, -346387.6 6632223.9 65.5))', 'PolygonZ ((-346384.8 6632219 65.5, -346383.5 6632216.9 65.5, -346383.5 6632216.9 72.5, -346384.8 6632219 72.5, -346384.8 6632219 65.5))']) else: self.assertCountEqual([g.asWkt(1) for g in results.asGeometries()], [ 'PolygonZ ((-346718.7 6632419.8 53.5, -346712 6632417.4 53.5, -346712 6632417.4 60.5, -346718.7 6632419.8 60.5, -346718.7 6632419.8 53.5))', 'PolygonZ ((-346719.3 6632420 53.5, -346718.7 6632419.8 53.5, -346718.7 6632419.8 60.5, -346719.3 6632420 60.5, -346719.3 6632420 53.5))', 'PolygonZ ((-346689.7 6632409.5 53.5, -346688.2 6632409 53.5, -346688.2 6632409 60.5, -346689.7 6632409.5 60.5, -346689.7 6632409.5 53.5))', 'PolygonZ ((-346692.5 6632410.5 53.5, -346689.7 6632409.5 53.5, -346689.7 6632409.5 60.5, -346692.5 6632410.5 60.5, -346692.5 6632410.5 53.5))', 'PolygonZ ((-346684.3 6632407.6 56, -346679.6 6632406 56, -346679.6 6632406 63, -346684.3 6632407.6 63, -346684.3 6632407.6 56))', 'PolygonZ ((-346679.6 6632406 56, -346672.8 6632403.6 56, -346672.8 6632403.6 63, -346679.6 6632406 63, -346679.6 6632406 56))', 'PolygonZ ((-346582.6 6632371.7 62.3, -346579.7 6632370.7 62.3, -346579.7 6632370.7 69.3, -346582.6 6632371.7 69.3, -346582.6 6632371.7 62.3))', 'PolygonZ ((-346579.7 6632370.7 62.3, -346577 6632369.7 62.3, -346570.8 6632367.9 62.3, -346570.8 6632367.9 69.3, -346577 6632369.7 69.3, -346579.7 6632370.7 69.3, -346579.7 6632370.7 62.3))', 'PolygonZ ((-346387.6 6632223.9 67, -346384.8 6632219 67, -346384.8 6632219 74, -346387.6 6632223.9 74, -346387.6 6632223.9 67))', 'PolygonZ ((-346384.8 6632219 67, -346383.5 6632216.9 67, -346383.5 6632216.9 74, -346384.8 6632219 74, -346384.8 6632219 67))'])
def processAlgorithm(self, parameters, context, feedback): """ RUN PROCESS """ # INPUT slayer = self.parameterAsSource(parameters, self.SOURCE_INPUT, context) sfields = self.parameterAsFields(parameters, self.SOURCE_FIELDS, context) tlayer = self.parameterAsSource(parameters, self.TARGET_INPUT, context) # CHECK CRS crs = slayer.sourceCrs() if crs == tlayer.sourceCrs(): # SEND INFORMATION TO THE USER feedback.pushInfo('='*40) feedback.pushInfo('CRS is {}'.format(crs.authid())) else: msg = 'ERROR: Layers have different CRS!' feedback.reportError(msg) return {} # OUTPUT LAYERS fields = QgsFields() fields.append(QgsField('source', QVariant.String)) fields.append(QgsField('target', QVariant.String)) for field in sfields: fields.append(QgsField(field, QVariant.Double)) (assignment_sink, assignment_id) = self.parameterAsSink( parameters, self.ASSIGN_OUTPUT, context, fields, QgsWkbTypes.LineString, crs ) fields = tlayer.fields() for field in sfields: fields.append(QgsField(field, QVariant.Double)) (node_sink, node_id) = self.parameterAsSink( parameters, self.NODE_OUTPUT, context, fields, QgsWkbTypes.Point, crs ) # ASSIGN, ACCUMULATE AND WRITE ASSIGNMENT LAYER values = {} for tfeature in tlayer.getFeatures(): for field in sfields: values[(tfeature["id"], field)] = 0.0 cnt = 0 for sfeature in slayer.getFeatures(): sxy = sfeature.geometry().asPoint() cdist = inf cfeature = None for tfeature in tlayer.getFeatures(): dist = tfeature.geometry().asPoint().distance(sxy) if dist < cdist: cdist = dist cfeature = tfeature f = QgsFeature() spoint = QgsPoint(sxy) cpoint = QgsPoint(cfeature.geometry().asPoint()) f.setGeometry(QgsLineString([spoint, cpoint])) attr = [sfeature["id"], cfeature["id"]] for field in sfields: attr.append(sfeature[field]) values[(cfeature["id"], field)] += sfeature[field] f.setAttributes(attr) assignment_sink.addFeature(f) cnt += 1 # SHOW PROGRESS feedback.setProgress(50*cnt/slayer.featureCount()) # WRITE NODE LAYER cnt = 0 for tfeature in tlayer.getFeatures(): f = QgsFeature() attr = tfeature.attributes() for field in sfields: attr.append(values[(tfeature["id"], field)]) f = tfeature f.setAttributes(attr) node_sink.addFeature(f) cnt += 1 # SHOW PROGRESS feedback.setProgress(50+50*cnt/tlayer.featureCount()) # SHOW PROGRESS feedback.pushInfo('Source #: {}.'.format(slayer.featureCount())) feedback.pushInfo('Target #: {}.'.format(tlayer.featureCount())) nncnt = sum((1 for x in values if abs(values[x]) > 0)) feedback.pushInfo('Not null assignment #: {}.'.format(nncnt)) feedback.pushInfo('='*40) # PROCCES CANCELED if feedback.isCanceled(): return {} # OUTPUT return {self.ASSIGN_OUTPUT: assignment_id, self.NODE_OUTPUT: node_id}
def processing(options, f, progressBar, progressMessage): ''' Select trees which are on the contour of the forest and isolated trees. ''' # Export Grid contour and isolated to crowns values forestSelectedPath = options['dst'] + 'tif/' + f + \ '_forest_selected.tif' crownsPath = options['dst'] + 'shp/' + f + '_crowns.shp' # crownsStatsPath = options['dst'] + 'shp/' + f + '_crowns_stats.shp' outputDir = options["dst"] fileTxt = open(outputDir + "/log.txt", "a") fileTxt.write("gridstatisticsforpolygons started\n") fileTxt.close() crowns = QgsVectorLayer(crownsPath, "crowns", "ogr") inputStatRaster = QgsRasterLayer(forestSelectedPath, "forestSelected") z_stat = QgsZonalStatistics(crowns, inputStatRaster, '_', 1, QgsZonalStatistics.Max) result_z_stat = z_stat.calculateStatistics(QgsFeedback()) outputDir = options["dst"] fileTxt = open(outputDir + "/log.txt", "a") fileTxt.write("gridstatisticsforpolygons passed\n") fileTxt.close() # crowns = QgsVectorLayer(crownsStatsPath, 'Crowns stats', 'ogr') crowns.selectByExpression('"_max"=1.0') selected_array = crowns.getValues("N", True) crowns.invertSelection() unselected_array = crowns.getValues("N", True) unselected_crowns_ids = crowns.getValues("$id", True) unselected_top_ids = crowns.getValues('"N" - 1', True) crowns.dataProvider().deleteFeatures(unselected_crowns_ids[0]) treetopsPath = options['dst'] + 'shp/' + f + '_treetops.shp' treetops = QgsVectorLayer(treetopsPath, 'Tree tops', 'ogr') treetops.dataProvider().deleteFeatures(unselected_top_ids[0]) treetopsSelectedPath = options['dst'] + 'shp/' + f + \ '_treetops_selected.shp' crownsSelectedPath = options['dst'] + 'shp/' + f + '_crowns_selected.shp' treetopsTrianglesPath = options['dst'] + 'shp/' + f + \ '_treetops_triangles.shp' outputDir = options["dst"] fileTxt = open(outputDir + "/log.txt", "a") fileTxt.write("advancedpythonfieldcalculator started\n") fileTxt.close() treetops.dataProvider().addAttributes([QgsField('N', QVariant.Int)]) treetops.updateFields() treetops.startEditing() for treetop in treetops.getFeatures(): treetops.changeAttributeValue(treetop.id(), treetop.fieldNameIndex('N'), treetop.id()) treetops.commitChanges() outputDir = options["dst"] fileTxt = open(outputDir + "/log.txt", "a") fileTxt.write("joinattributesbylocation started\n") fileTxt.close() # Adapted from https://github.com/qgis/QGIS-Processing # TODO: replace by native QGIS c++ algo when available... crowns.dataProvider().addAttributes([QgsField('tid', QVariant.Int)]) crowns.updateFields() crowns.startEditing() fcount = crowns.featureCount() counter = 0 for crown in crowns.getFeatures(): counter += 1 progressBar.setValue(100 + int(counter * (600 / fcount))) progressMessage.setText('Joining crown ' + str(counter) + '/' + str(fcount)) request = QgsFeatureRequest() request.setFilterRect(crown.geometry().boundingBox()) dp = treetops.dataProvider() for r in dp.getFeatures(request): if crown.geometry().intersects(r.geometry()): crowns.changeAttributeValue(crown.id(), crown.fieldNameIndex('tid'), r.id()) crowns.commitChanges() fileTxt = open(outputDir + "/log.txt", "a") fileTxt.write("delaunaytriangulation started\n") fileTxt.close() # delaunay triangulation Adapted from official Python plugin # TODO: replace by native QGIS c++ algo when available... 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)) crs = QgsCoordinateReferenceSystem('EPSG:2056') triangleFile = QgsVectorFileWriter(treetopsTrianglesPath, 'utf-8', fields, QgsWkbTypes.Polygon, crs, 'ESRI Shapefile') pts = [] ptDict = {} ptNdx = -1 c = voronoi.Context() features = treetops.getFeatures() total = 100.0 / treetops.featureCount() if treetops.featureCount() else 0 progressMessage.setText('Starting triangulation...') for current, inFeat in enumerate(features): 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) progressMessage.setText('Triangulation step 1 ok') if len(pts) < 3: raise QgsProcessingException( '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): 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(treetops.getFeatures(request)) geom = QgsGeometry(inFeat.geometry()) point = QgsPoint(geom.asPoint()) polygon.append(point) if step <= 3: attrs.append(ids[index]) step += 1 linestring = QgsLineString(polygon) poly = QgsPolygon() poly.setExteriorRing(linestring) feat.setAttributes(attrs) geometry = QgsGeometry().fromWkt(poly.asWkt()) feat.setGeometry(geometry) triangleFile.addFeature(feat) progressMessage.setText('Triangulation terminated') # Remove triangles with perimeter higher than threshold triangles = QgsVectorLayer(treetopsTrianglesPath, 'triangles', 'ogr') maxPeri = str(options['MaxTrianglePerimeter']) triangles.selectByExpression('$perimeter > ' + maxPeri) triangles_to_delete_ids = triangles.getValues("$id", True) triangles.dataProvider().deleteFeatures(triangles_to_delete_ids[0]) outputDir = options["dst"] fileTxt = open(outputDir + "/log.txt", "a") fileTxt.write("treeSelector passed\n") fileTxt.close() progressMessage.setText('Starting convexhull computing...')
def UpdateBeamsData(packet, cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL, ele): ''' Update Beams Values ''' lat = packet.SensorLatitude lon = packet.SensorLongitude alt = packet.SensorTrueAltitude global groupName beamsLyr = qgsu.selectLayerByName(Beams_lyr, groupName) try: if all(v is not None for v in [ beamsLyr, lat, lon, alt, cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL ]) and all(v >= 2 for v in [ len(cornerPointUL), len(cornerPointUR), len(cornerPointLR), len(cornerPointLL) ]): beamsLyr.startEditing() if beamsLyr.featureCount() == 0: # UL featureUL = QgsFeature() featureUL.setAttributes( [lon, lat, alt, cornerPointUL[1], cornerPointUL[0]]) featureUL.setGeometry( QgsLineString(QgsPoint(lon, lat, alt), QgsPoint(cornerPointUL[1], cornerPointUL[0]))) beamsLyr.addFeatures([featureUL]) # UR featureUR = QgsFeature() featureUR.setAttributes( [lon, lat, alt, cornerPointUR[1], cornerPointUR[0]]) featureUR.setGeometry( QgsLineString(QgsPoint(lon, lat, alt), QgsPoint(cornerPointUR[1], cornerPointUR[0]))) beamsLyr.addFeatures([featureUR]) # LR featureLR = QgsFeature() featureLR.setAttributes( [lon, lat, alt, cornerPointLR[1], cornerPointLR[0]]) featureLR.setGeometry( QgsLineString(QgsPoint(lon, lat, alt), QgsPoint(cornerPointLR[1], cornerPointLR[0]))) beamsLyr.addFeatures([featureLR]) # LL featureLL = QgsFeature() featureLL.setAttributes( [lon, lat, alt, cornerPointLL[1], cornerPointLL[0]]) featureLL.setGeometry( QgsLineString(QgsPoint(lon, lat, alt), QgsPoint(cornerPointLL[1], cornerPointLL[0]))) beamsLyr.addFeatures([featureLL]) else: # UL beamsLyr.dataProvider().changeAttributeValues({ 1: { 0: lon, 1: lat, 2: alt, 3: cornerPointUL[1], 4: cornerPointUL[0] } }) beamsLyr.dataProvider().changeGeometryValues({ 1: QgsGeometry( QgsLineString( QgsPoint(lon, lat, alt), QgsPoint(cornerPointUL[1], cornerPointUL[0]))) }) # UR beamsLyr.dataProvider().changeAttributeValues({ 2: { 0: lon, 1: lat, 2: alt, 3: cornerPointUR[1], 4: cornerPointUR[0] } }) beamsLyr.dataProvider().changeGeometryValues({ 2: QgsGeometry( QgsLineString( QgsPoint(lon, lat, alt), QgsPoint(cornerPointUR[1], cornerPointUR[0]))) }) # LR beamsLyr.dataProvider().changeAttributeValues({ 3: { 0: lon, 1: lat, 2: alt, 3: cornerPointLR[1], 4: cornerPointLR[0] } }) beamsLyr.dataProvider().changeGeometryValues({ 3: QgsGeometry( QgsLineString( QgsPoint(lon, lat, alt), QgsPoint(cornerPointLR[1], cornerPointLR[0]))) }) # LL beamsLyr.dataProvider().changeAttributeValues({ 4: { 0: lon, 1: lat, 2: alt, 3: cornerPointLL[1], 4: cornerPointLL[0] } }) beamsLyr.dataProvider().changeGeometryValues({ 4: QgsGeometry( QgsLineString( QgsPoint(lon, lat, alt), QgsPoint(cornerPointLL[1], cornerPointLL[0]))) }) CommonLayer(beamsLyr) # 3D Style if ele: SetDefaultBeams3DStyle(beamsLyr) except Exception as e: qgsu.showUserAndLogMessage( QCoreApplication.translate("QgsFmvUtils", "Failed Update Beams Layer! : "), str(e)) return
def processAlgorithm(self, parameters, context, feedback): ''' Here is where the processing itself takes place. ''' # if not is_dependencies_satisfied: return {} # Init # The number of features in the input layer could be trimmed to user selection. the_layer = self.parameterAsSource(parameters, self.THE_LAYER, context) gok = QgsWkbTypes.geometryType( the_layer.wkbType()) == QgsWkbTypes.PointGeometry if the_layer is None or not gok: raise QgsProcessingException( self.invalidSourceError(parameters, self.THE_LAYER)) # bCHscal = self.parameterAsBool(parameters, self.BSCALE, context) if bCHscal: # Use another channel for scaling. All data from that channel will be used. scally = self.parameterAsSource(parameters, self.SCALLY, context) if scally is None or the_layer.wkbType() != QgsWkbTypes.Point: raise QgsProcessingException( self.invalidSourceError(parameters, self.SCALLY)) fidu_fld = self.parameterAsString(parameters, self.FID_FLD, context) data_fld = self.parameterAsString(parameters, self.DATA_FLD, context) line_fld = self.parameterAsString(parameters, self.LINE_FLD, context) invP = self.parameterAsBool(parameters, self.INVERTP, context) dumval = self.parameterAsDouble(parameters, self.DUMVAL, context) scale = self.parameterAsDouble(parameters, self.SCALE, context) offset = self.parameterAsDouble(parameters, self.OFFSET, context) join_to_line = self.parameterAsBool(parameters, self.JOINL, context) data = the_layer.fields().at(the_layer.fields().lookupField(data_fld)) fidu = the_layer.fields().at(the_layer.fields().lookupField(fidu_fld)) if not data.isNumeric() or not fidu.isNumeric(): raise QgsProcessingException( self.invalidSourceError(parameters, self.THE_LAYER)) line = the_layer.fields().at(the_layer.fields().lookupField(line_fld)) data_ix = the_layer.fields().lookupField(data_fld) line_ix = the_layer.fields().lookupField(line_fld) fidu_ix = the_layer.fields().lookupField(fidu_fld) # Set output vector layer: point(X, Y, M) M is data value at that point output_wkb = QgsWkbTypes.LineString output_wkb = QgsWkbTypes.addM(output_wkb) # Fields of stacked profiles vector line_def = the_layer.fields().at(line_ix) fields = QgsFields() if line_def is not None: fields = QgsFields() fields.append(QgsField('Line', QVariant.String, '', 16)) fields.append(QgsField('Type', QVariant.String, '', 2)) fields.append(QgsField('NbPts', QVariant.Int, '', 10, 0)) fields.append(QgsField('Azimuth', QVariant.Double, '', 10, 6)) fields.append(QgsField('DistEP', QVariant.Double, '', 10, 2)) fields.append(QgsField('Length', QVariant.Double, '', 10, 2)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, output_wkb, the_layer.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) # Get the features and fields of interest features = the_layer.getFeatures( QgsFeatureRequest().setSubsetOfAttributes( [fidu_ix, line_ix, data_ix]), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) # CSV # Find min/max of data values for all lines and save each line in a csv file # Then process each line separately: can have any number of lines... lines = [] xyzf = [] lineN = '' nL = 0 TL = 0. total = 60.0 / the_layer.featureCount() if the_layer.featureCount( ) else 0 stat = QgsStatisticalSummary() for current, ft in enumerate(features): if feedback.isCanceled(): break feedback.setProgress(int(current * total)) if not ft.hasGeometry(): continue # if ft[line.name()] != lineN: if xyzf != []: lines.append([lineN, nL]) the_csv = os.path.join(self.tmpDir, '%s.csv' % str(lineN)) with codecs.open(the_csv, 'w', 'utf-8') as fo: fo.write('X,Y,FID,Data\n') for ar in xyzf: fo.write(','.join(map(str, ar))) fo.write('\n') le = sqrt((xyzf[0][0] - xyzf[-1][0])**2 + (xyzf[0][1] - xyzf[-1][1])**2) if le > TL: TL = le xyzf = [] nL = 0 lineN = ft[line.name()] # rdata = float(ft[data.name()]) fiduu = int(ft[fidu.name()]) if abs(rdata - dumval) < 1e-6: # Dummy value: skip continue # stat.addVariant(ft[data.name()]) # how to handle QgsMultiPoint ??? if (the_layer.wkbType() == QgsWkbTypes.MultiPoint or the_layer.wkbType() == QgsWkbTypes.MultiPointM or the_layer.wkbType() == QgsWkbTypes.MultiPointZ or the_layer.wkbType() == QgsWkbTypes.MultiPointZM or the_layer.wkbType() == QgsWkbTypes.MultiPoint25D): # Suppose they all have the same attributes: # in this case it seems useless to get more than the first point, but... points = ft.geometry().constGet().clone() else: points = [ft.geometry().constGet().clone()] try: for point in points: xyzf.append([point.x(), point.y(), fiduu, rdata]) nL += 1 except: pass # last line if xyzf != []: lines.append([lineN, nL]) the_csv = os.path.join(self.tmpDir, '%s.csv' % str(lineN)) with codecs.open(the_csv, 'w', 'utf-8') as fo: fo.write('X,Y,FID,Data\n') for ar in xyzf: fo.write(','.join(map(str, ar))) fo.write('\n') le = sqrt((xyzf[0][0] - xyzf[-1][0])**2 + (xyzf[0][1] - xyzf[-1][1])**2) if le > TL: TL = le # stat.finalize() self.dmean = stat.mean() self.mult = TL / (stat.max() - stat.min()) # if bCHscal: # Scaling field: retrieve its stats scch_fld = self.parameterAsString(parameters, self.SCALCH, context) scch = scally.fields().at(scally.fields().lookupField(scch_fld)) scch_ix = scally.fields().lookupField(scch_fld) scch_f = scally.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([scch_ix]), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) stat = QgsStatisticalSummary() for current, ft in enumerate(scch_f): stat.addVariant(ft[scch.name()]) stat.finalize() self.dmean = stat.mean() self.mult = TL / (stat.max() - stat.min()) # if invP: iv = -1 else: iv = 1 # Profile total = 40.0 / (len(lines) + 1) # For each line: for current, z in enumerate(lines): line = z[0] if feedback.isCanceled(): break if not ft.hasGeometry(): continue feedback.setProgress(int(current * total) + 60.) # Read line back from csv the_csv = os.path.join(self.tmpDir, '%s.csv' % str(line)) if not os.path.exists(the_csv): raise ValueError( 'It seems parameters are swaped: LINE <-> DATA!') ar = pd.read_csv(the_csv) ar = ar.sort_values('FID') # Create the profile px, py = self._do_profile(ar, iv, scale, offset) #Construct vector layer f = QgsFeature() typeL = str(self.type) azimut = float(self.azimut) Len = float(self.length) CLen = float(self.clength) f.setAttributes( [str(line), typeL, int(len(px)), azimut, Len, CLen]) line_pts = [ QgsPoint(x, y, m=m) for x, y, m in zip(px, py, ar.Data) ] if join_to_line: # Join profile to its line e = len(ar) - 1 ar0 = [QgsPoint(ar.X[0], ar.Y[0], m=0.)] ar1 = [QgsPoint(ar.X[e], ar.Y[e], m=0.)] line_pts = ar0 + line_pts + ar1 # f.setGeometry(QgsGeometry(QgsLineString(line_pts))) sink.addFeature(f, QgsFeatureSink.FastInsert) # Delete temp csv file try: os.remove(the_csv) except: pass return {self.OUTPUT: dest_id}
def test_merge_nodes(self): error_tolerance = 0.0001 vl = TestNetworkSegmenter.make_geometry_feature_layer( "LineString", [ QgsLineString([ QgsPoint(535088.141198, 185892.128181), QgsPoint(535037.617992, 186342.145327) ]), QgsLineString([ QgsPoint(535012.560937, 186126.546603), QgsPoint(535285.548630, 186203.990233) ]), QgsLineString([ QgsPoint(534952.224080, 186285.463215), QgsPoint(535248.881516, 185907.343107) ]) ]) ul = TestNetworkSegmenter.make_geometry_feature_layer( "Point", [ QgsPoint(535067.772756, 186147.054373), # invalid unlink QgsPoint(535059.304801, 186148.977932) # valid unlink ]) self.assertEqual(vl.featureCount(), 3) # segment the axial lines without removing stubs stub_ratio = 0.3 buffer = 1 errors = True my_segmentor = segmentor(vl, ul, stub_ratio, buffer, errors) break_point_feats, invalid_unlink_point_feats, stubs_point_feats, segmented_feats = [], [], [], [] my_segmentor.step = 10 / float(my_segmentor.layer.featureCount()) my_segmentor.load_graph() # self.step specified in load_graph # progress emitted by break_segm & break_feats_iter cross_p_list = [ my_segmentor.break_segm(feat) for feat in my_segmentor.list_iter( list(my_segmentor.feats.values())) ] my_segmentor.step = 20 / float(len(cross_p_list)) segmented_feats = [ my_segmentor.copy_feat(feat_geom_fid[0], feat_geom_fid[1], feat_geom_fid[2]) for feat_geom_fid in my_segmentor.break_feats_iter(cross_p_list) ] if errors: cross_p_list = set( list(itertools.chain.from_iterable(cross_p_list))) ids1 = [i for i in range(0, len(cross_p_list))] break_point_feats = [ my_segmentor.copy_feat(my_segmentor.break_f, QgsGeometry.fromPointXY(p_fid[0]), p_fid[1]) for p_fid in (list(zip(cross_p_list, ids1))) ] ids2 = [ i for i in range( max(ids1) + 1, max(ids1) + 1 + len(my_segmentor.invalid_unlinks)) ] invalid_unlink_point_feats = [ my_segmentor.copy_feat(my_segmentor.invalid_unlink_f, QgsGeometry.fromPointXY(p_fid1[0]), p_fid1[1]) for p_fid1 in (list(zip(my_segmentor.invalid_unlinks, ids2))) ] ids = [ i for i in range( max(ids1 + ids2) + 1, max(ids1 + ids2) + 1 + len(my_segmentor.stubs_points)) ] stubs_point_feats = [ my_segmentor.copy_feat(my_segmentor.stub_f, QgsGeometry.fromPointXY(p_fid2[0]), p_fid2[1]) for p_fid2 in (list(zip(my_segmentor.stubs_points, ids))) ] expected_segments = [ QgsLineString([ QgsPoint(535088.141198, 185892.128181), QgsPoint(535060.302601, 186140.090392) ]), QgsLineString([ QgsPoint(535060.302601, 186140.090392), QgsPoint(535037.617991, 186342.145327) ]), QgsLineString([ QgsPoint(535060.302601, 186140.090392), QgsPoint(535065.189841, 186141.476849) ]), QgsLineString([ QgsPoint(535065.189841, 186141.476849), QgsPoint(535285.548629, 186203.990232) ]), QgsLineString([ QgsPoint(534952.224079, 186285.463214), QgsPoint(535065.189841, 186141.476849) ]), QgsLineString([ QgsPoint(535065.189841, 186141.476849), QgsPoint(535248.881516, 185907.343106) ]), ] self.assertEqual(len(segmented_feats), len(expected_segments)) for segmented_feat in segmented_feats: segmented_line = segmented_feat.geometry().asPolyline() for expected_segment in expected_segments: x1eq = abs(segmented_line[0].x() - expected_segment[0].x()) < error_tolerance y1eq = abs(segmented_line[0].y() - expected_segment[0].y()) < error_tolerance x2eq = abs(segmented_line[1].x() - expected_segment[1].x()) < error_tolerance y2eq = abs(segmented_line[1].y() - expected_segment[1].y()) < error_tolerance if x1eq and y1eq and x2eq and y2eq: expected_segments.remove(expected_segment) break # all the expected features should have been found and removed self.assertEqual(len(expected_segments), 0) expected_break_points = [ QgsPoint(535285.548629, 186203.990232), QgsPoint(535065.189841, 186141.476849), QgsPoint(535060.302601, 186140.090392), QgsPoint(535037.617991, 186342.145327), QgsPoint(534952.224079, 186285.463214), QgsPoint(535248.881516, 185907.343106), QgsPoint(535088.141198, 185892.128181) ] self.assertEqual(len(break_point_feats), len(expected_break_points)) for break_point_feat in break_point_feats: break_point = break_point_feat.geometry().asPoint() for expected_break_point in expected_break_points: xeq = abs(break_point.x() - expected_break_point.x()) < error_tolerance yeq = abs(break_point.y() - expected_break_point.y()) < error_tolerance if xeq and yeq: expected_break_points.remove(expected_break_point) break # all the expected break points should have been found and removed self.assertEqual(len(expected_segments), 0) # only one stub removed self.assertEqual(len(stubs_point_feats), 1) self.assertAlmostEqual(stubs_point_feats[0].geometry().asPoint().x(), 535012.560936, places=3) self.assertAlmostEqual(stubs_point_feats[0].geometry().asPoint().y(), 186126.546602, places=3) # first unlink is invalid self.assertEqual(len(invalid_unlink_point_feats), 1) self.assertAlmostEqual( invalid_unlink_point_feats[0].geometry().asPoint().x(), next(ul.getFeatures()).geometry().asPoint().x(), places=3) self.assertAlmostEqual( invalid_unlink_point_feats[0].geometry().asPoint().y(), next(ul.getFeatures()).geometry().asPoint().y(), places=3)
def get_mesh_geometry(self, mesh, index): face = mesh.face(index) points = [mesh.vertex(v) for v in face] polygon = QgsPolygon() polygon.setExteriorRing(QgsLineString(points)) return QgsGeometry(polygon)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) group_field_name = self.parameterAsString(parameters, self.GROUP_FIELD, context) order_field_name = self.parameterAsString(parameters, self.ORDER_FIELD, context) date_format = self.parameterAsString(parameters, self.DATE_FORMAT, context) text_dir = self.parameterAsString(parameters, self.OUTPUT_TEXT_DIR, context) group_field_index = source.fields().lookupField(group_field_name) order_field_index = source.fields().lookupField(order_field_name) if group_field_index >= 0: group_field_def = source.fields().at(group_field_index) else: group_field_def = None order_field_def = source.fields().at(order_field_index) fields = QgsFields() if group_field_def is not None: fields.append(group_field_def) begin_field = QgsField(order_field_def) begin_field.setName('begin') fields.append(begin_field) end_field = QgsField(order_field_def) end_field.setName('end') fields.append(end_field) output_wkb = QgsWkbTypes.LineString if QgsWkbTypes.hasM(source.wkbType()): output_wkb = QgsWkbTypes.addM(output_wkb) if QgsWkbTypes.hasZ(source.wkbType()): output_wkb = QgsWkbTypes.addZ(output_wkb) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, output_wkb, source.sourceCrs()) points = dict() features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([group_field_index, order_field_index])) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue point = f.geometry().constGet().clone() if group_field_index >= 0: group = f.attributes()[group_field_index] else: group = 1 order = f.attributes()[order_field_index] if date_format != '': order = datetime.strptime(str(order), date_format) if group in points: points[group].append((order, point)) else: points[group] = [(order, point)] feedback.setProgress(int(current * total)) feedback.setProgress(0) da = QgsDistanceArea() da.setSourceCrs(source.sourceCrs(), context.transformContext()) da.setEllipsoid(context.project().ellipsoid()) current = 0 total = 100.0 / len(points) if points else 1 for group, vertices in list(points.items()): if feedback.isCanceled(): break vertices.sort(key=lambda x: (x[0] is None, x[0])) f = QgsFeature() attributes = [] if group_field_index >= 0: attributes.append(group) attributes.extend([vertices[0][0], vertices[-1][0]]) f.setAttributes(attributes) line = [node[1] for node in vertices] if text_dir: fileName = os.path.join(text_dir, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') for i in range(len(line)): if i == 0: fl.write('startAt=%f;%f;90\n' % (line[i].x(), line[i].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(QgsPointXY(line[i - 1]), QgsPointXY(line[i])) fl.write('%f;%f;90\n' % (angle, distance)) f.setGeometry(QgsGeometry(QgsLineString(line))) sink.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def createGeom(self, coords): crsDest = self.__layer.crs() rc = ReprojectCoordinates(self.crsId, crsDest.srsid(), self.__hasZ, self.__hasM) if self.crsId != crsDest.srsid(): coordsPoint = list(rc.reproject(coords, True)) else: coordsPoint = list(rc.copyCoordstoPoints(coords)) # Point and multipoint Geometry # Always 1 part, 0 element of matrix if self.__layergeometryType == QgsWkbTypes.PointGeometry: if self.__isMultiType: multipoint = QgsMultiPoint() for coords_item in coordsPoint[0][1]: multipoint.addGeometry(coords_item) geom = QgsGeometry(multipoint) self.createFeature(geom) else: geom = QgsGeometry(coordsPoint[0][1][0]) self.createFeature(geom) elif self.__layergeometryType == QgsWkbTypes.LineGeometry: if self.__isMultiType: multiline = QgsGeometry(QgsMultiLineString()) for j in range(len(coordsPoint)): line = QgsLineString(coordsPoint[j][1]) multiline.addPart(line) self.createFeature(multiline) else: line = QgsGeometry(QgsLineString(coordsPoint[0][1])) self.createFeature(line) elif self.__layergeometryType == QgsWkbTypes.PolygonGeometry: if self.__isMultiType: multipoly = QgsGeometry(QgsMultiPolygon()) for i in range(len(coordsPoint)): if int(coordsPoint[i][0]) > 0: mycurve = QgsLineString(coordsPoint[i][1]) poly = QgsPolygon() poly.setExteriorRing(mycurve) polyGeometry = QgsGeometry(QgsPolygon(poly)) for j in range(len(coordsPoint)): if int(coordsPoint[j][0]) < 0: containsAllPoints = True for k in range(len(coordsPoint[j][1])): containsAllPoints = True curPoint = coordsPoint[j][1][k].clone() containsAllPoints = containsAllPoints \ and polyGeometry.contains(QgsPointXY(curPoint.x(), curPoint.y())) if containsAllPoints: mycurve = QgsLineString(coordsPoint[j][1]) poly.addInteriorRing(mycurve) multipoly.addPart(poly) self.createFeature(multipoly) else: extRing = 0 for i in range(len(coordsPoint)): if int(coordsPoint[i][0]) > 0: extRing = i mycurve = QgsLineString(coordsPoint[extRing][1]) poly = QgsPolygon() poly.setExteriorRing(mycurve) polyGeometry = QgsGeometry(QgsPolygon(poly)) for i in range(len(coordsPoint)): if int(coordsPoint[i][0]) < 0: containsAllPoints = True for j in range(len(coordsPoint[i][1])): containsAllPoints = True curPoint = coordsPoint[i][1][j].clone() containsAllPoints = containsAllPoints \ and polyGeometry.contains(QgsPointXY(curPoint.x(), curPoint.y())) if containsAllPoints: mycurve = QgsLineString(coordsPoint[i][1]) poly.addInteriorRing(mycurve) else: QMessageBox.question(self.iface.mainWindow(), self.translate_str("Ring not in exterior contour"), self.translate_str("The new geometry of the feature" " isn't valid. Do you want to use it anyway?"), QMessageBox.Yes, QMessageBox.No) self.createFeature(QgsGeometry(poly))
def processAlgorithm(self, parameters, context, feedback): hSpacing = self.parameterAsDouble(parameters, self.HSPACING, context) vSpacing = self.parameterAsDouble(parameters, self.VSPACING, context) hOverlay = self.parameterAsDouble(parameters, self.HOVERLAY, context) vOverlay = self.parameterAsDouble(parameters, self.VOVERLAY, context) bbox = self.parameterAsExtent(parameters, self.EXTENT, context) crs = self.parameterAsCrs(parameters, self.CRS, context) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise QgsProcessingException( self.tr('Invalid grid spacing: {0}/{1}').format(hSpacing, vSpacing)) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise QgsProcessingException( self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) if width < hSpacing: raise QgsProcessingException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise QgsProcessingException( self.tr('Vertical spacing is too small for the covered area')) fields = QgsFields() fields.append(QgsField('left', QVariant.Double, '', 24, 16)) fields.append(QgsField('top', QVariant.Double, '', 24, 16)) fields.append(QgsField('right', QVariant.Double, '', 24, 16)) fields.append(QgsField('bottom', QVariant.Double, '', 24, 16)) fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('coord', QVariant.Double, '', 24, 15)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, crs) if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): if feedback.isCanceled(): break pt1 = QgsPoint(bbox.xMinimum(), y) pt2 = QgsPoint(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) sink.addFeature(feat, QgsFeatureSink.FastInsert) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): if feedback.isCanceled(): break pt1 = QgsPoint(x, bbox.yMaximum()) pt2 = QgsPoint(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) sink.addFeature(feat, QgsFeatureSink.FastInsert) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) hOverlay = self.getParameterValue(self.HOVERLAY) vOverlay = self.getParameterValue(self.VOVERLAY) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: {0}/{1}').format(hSpacing, vSpacing)) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise GeoAlgorithmExecutionException( self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = QgsFields() fields.append(QgsField('left', QVariant.Double, '', 24, 16)) fields.append(QgsField('top', QVariant.Double, '', 24, 16)) fields.append(QgsField('right', QVariant.Double, '', 24, 16)) fields.append(QgsField('bottom', QVariant.Double, '', 24, 16)) fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('coord', QVariant.Double, '', 24, 15)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs, context) if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPoint(bbox.xMinimum(), y) pt2 = QgsPoint(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat, QgsFeatureSink.FastInsert) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPoint(x, bbox.yMaximum()) pt2 = QgsPoint(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat, QgsFeatureSink.FastInsert) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50)) del writer
def processAlgorithmOutput(self, results, parameters, context, feedback): output_fields = QgsFields() result_type = self.RESULT_TYPE[self.params["OUTPUT_RESULT_TYPE"]] if result_type == "NORMAL": output_fields.append(QgsField("search_id", QVariant.String, "text", 255)) output_fields.append(QgsField("location_id", QVariant.String, "text", 255)) output_fields.append(QgsField("travel_time", QVariant.Double, "text", 255)) output_fields.append(QgsField("distance", QVariant.Double, "text", 255)) output_fields.append( QgsField("departure_time", QVariant.String, "text", 255) ) output_fields.append(QgsField("arrival_time", QVariant.String, "text", 255)) else: output_fields.append(QgsField("type", QVariant.String, "text", 255)) output_fields.append(QgsField("mode", QVariant.String, "text", 255)) output_fields.append(QgsField("directions", QVariant.String, "text", 255)) output_crs = EPSG4326 output_type = QgsWkbTypes.LineString (sink, sink_id) = self.parameterAsSink( parameters, "OUTPUT", context, output_fields, output_type, output_crs ) for result in results: for location in result["locations"]: if result_type == "NORMAL": # Create the geom geom = QgsLineString() for part in location["properties"][0]["route"]["parts"]: for coord in part["coords"]: point = QgsPoint(coord["lng"], coord["lat"]) if geom.endPoint() != point: geom.addVertex(point) # Create the feature feature = QgsFeature(output_fields) feature.setGeometry(geom) feature.setAttribute(0, result["search_id"]) feature.setAttribute(1, location["id"]) feature.setAttribute(2, location["properties"][0]["travel_time"]) feature.setAttribute(3, location["properties"][0]["distance"]) feature.setAttribute( 4, location["properties"][0]["route"]["departure_time"] ) feature.setAttribute( 5, location["properties"][0]["route"]["arrival_time"] ) sink.addFeature(feature, QgsFeatureSink.FastInsert) else: for part in location["properties"][0]["route"]["parts"]: # Create the geom geom = QgsLineString() for coord in part["coords"]: point = QgsPoint(coord["lng"], coord["lat"]) geom.addVertex(point) # Create the feature feature_d = QgsFeature(output_fields) feature_d.setGeometry(geom) feature_d.setAttribute(0, part["type"]) feature_d.setAttribute(1, part["mode"]) feature_d.setAttribute(2, part["directions"]) sink.addFeature(feature_d, QgsFeatureSink.FastInsert) feedback.pushDebugInfo("TimeFilterAlgorithm done !") # to get hold of the layer in post processing self.sink_id = sink_id return {"OUTPUT": sink_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) close_path = self.parameterAsBool(parameters, self.CLOSE_PATH, context) group_field_name = self.parameterAsString(parameters, self.GROUP_FIELD, context) order_field_name = self.parameterAsString(parameters, self.ORDER_FIELD, context) order_expression = self.parameterAsString(parameters, self.ORDER_EXPRESSION, 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) if group_field_index >= 0: group_field_def = source.fields().at(group_field_index) else: group_field_def = None if order_field_name: order_expression = QgsExpression.quotedColumnRef(order_field_name) if not order_expression: raise QgsProcessingException( self.tr('ORDER_EXPRESSION parameter is missing.')) expression_context = self.createExpressionContext( parameters, context, source) expression = QgsExpression(order_expression) if expression.hasParserError(): raise QgsProcessingException(expression.parserErrorString()) expression.prepare(expression_context) order_field_type = QVariant.String if expression.isField(): field_name = next(iter(expression.referencedColumns())) order_field_type = source.fields().field(field_name).type() fields = QgsFields() if group_field_def is not None: fields.append(group_field_def) begin_field = QgsField('begin', order_field_type) fields.append(begin_field) end_field = QgsField('end', order_field_type) 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() required_fields = expression.referencedColumns() required_fields.add(group_field_name) features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes(required_fields, source.fields()), 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 expression_context.setFeature(f) order = expression.evaluate(expression_context) 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.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 close_path is True: if line[0] != line[-1]: line.append(line[0]) if text_dir: fileName = os.path.join(text_dir, '%s.txt' % group) with open(fileName, 'w') as fl: fl.write('angle=Azimuth\n') fl.write('heading=Coordinate_System\n') fl.write('dist_units=Default\n') for i in range(len(line)): if i == 0: fl.write('startAt=%f;%f;90\n' % (line[i].x(), line[i].y())) fl.write('survey=Polygonal\n') fl.write('[data]\n') else: angle = line[i - 1].azimuth(line[i]) distance = da.measureLine(QgsPointXY(line[i - 1]), QgsPointXY(line[i])) fl.write('%f;%f;90\n' % (angle, distance)) f.setGeometry(QgsGeometry(QgsLineString(line))) sink.addFeature(f, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.PrmInputLayer, context) outer_col = self.parameterAsString(parameters, self.PrmOuterRadiusField, context) inner_col = self.parameterAsString(parameters, self.PrmInnerRadiusField, context) lines_col = self.parameterAsString(parameters, self.PrmNumberOfLinesField, context) def_outer_radius = self.parameterAsDouble(parameters, self.PrmDefaultOuterRadius, context) def_inner_radius = self.parameterAsDouble(parameters, self.PrmDefaultInnerRadius, context) def_lines = self.parameterAsDouble(parameters, self.PrmDefaultNumberOfLines, context) units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure, context) export_geom = self.parameterAsBool(parameters, self.PrmExportInputGeometry, context) measure_factor = conversionToMeters(units) def_inner_radius *= measure_factor def_outer_radius *= measure_factor src_crs = source.sourceCrs() fields = source.fields() if export_geom: names = fields.names() name_x, name_y = settings.getGeomNames(names) fields.append(QgsField(name_x, QVariant.Double)) fields.append(QgsField(name_y, QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.PrmOutputLayer, context, fields, QgsWkbTypes.MultiLineString, src_crs) if src_crs != epsg4326: geom_to_4326 = QgsCoordinateTransform(src_crs, epsg4326, QgsProject.instance()) to_sink_crs = QgsCoordinateTransform(epsg4326, src_crs, QgsProject.instance()) feature_count = source.featureCount() total = 100.0 / feature_count if feature_count else 0 iterator = source.getFeatures() num_bad = 0 for cnt, feature in enumerate(iterator): if feedback.isCanceled(): break try: line_strings = [] pt = feature.geometry().asPoint() pt_orig = QgsPoint(pt) # make sure the coordinates are in EPSG:4326 if src_crs != epsg4326: pt = geom_to_4326.transform(pt.x(), pt.y()) lat = pt.y() lon = pt.x() if inner_col: inner_radius = float(feature[inner_col]) * measure_factor else: inner_radius = def_inner_radius if outer_col: outer_radius = float(feature[outer_col]) * measure_factor else: outer_radius = def_outer_radius if lines_col: num_lines = int(feature[lines_col]) else: num_lines = def_lines if num_lines <= 0: num_bad += 1 continue angle = 0 angle_step = 360.0 / num_lines while angle < 360: if inner_radius == 0: pt_start = pt_orig else: g = geod.Direct(lat, lon, angle, inner_radius, Geodesic.LATITUDE | Geodesic.LONGITUDE) pt_start = QgsPoint(g['lon2'], g['lat2']) if src_crs != epsg4326: pt_start = to_sink_crs.transform(pt_start) g = geod.Direct(lat, lon, angle, outer_radius, Geodesic.LATITUDE | Geodesic.LONGITUDE) pt_end = QgsPoint(g['lon2'], g['lat2']) if src_crs != epsg4326: pt_end = to_sink_crs.transform(pt_end) line_str = QgsLineString([pt_start, pt_end]) line_strings.append(line_str) angle += angle_step f = QgsFeature() if len(line_strings) == 1: f.setGeometry(QgsGeometry(line_strings[0])) else: g = QgsMultiLineString() for line_str in line_strings: g.addGeometry(line_str) f.setGeometry(QgsGeometry(g)) attr = feature.attributes() if export_geom: attr.append(pt_orig.x()) attr.append(pt_orig.y()) f.setAttributes(attr) sink.addFeature(f) except Exception: s = traceback.format_exc() feedback.pushInfo(s) num_bad += 1 feedback.setProgress(int(cnt * total)) if num_bad > 0: feedback.pushInfo( tr("{} out of {} features had invalid parameters and were ignored." .format(num_bad, feature_count))) return {self.PrmOutputLayer: dest_id}
def testJSONExporter(self): """ test converting features to GeoJSON """ fields = QgsFields() fields.append(QgsField("name", QVariant.String)) fields.append(QgsField("cost", QVariant.Double)) fields.append(QgsField("population", QVariant.Int)) feature = QgsFeature(fields, 5) feature.setGeometry(QgsGeometry(QgsPointV2(5, 6))) feature.setAttributes(['Valsier Peninsula', 6.8, 198]) exporter = QgsJSONExporter() expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) # test with linestring for bbox inclusion l = QgsLineString() l.setPoints([QgsPointV2(5, 6), QgsPointV2(15, 16)]) feature.setGeometry(QgsGeometry(QgsLineString(l))) expected = """{ "type":"Feature", "id":5, "bbox":[5, 6, 15, 16], "geometry": {"type": "LineString", "coordinates": [ [5, 6], [15, 16]]}, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) # test that precision is respected feature.setGeometry(QgsGeometry(QgsPointV2(5.444444444, 6.333333333))) exporter.setPrecision(3) self.assertEqual(exporter.precision(), 3) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5.444, 6.333]}, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) feature.setGeometry(QgsGeometry(QgsPointV2(5, 6))) exporter.setPrecision(17) # test that attribute subset is respected exporter.setAttributes([0, 2]) self.assertEqual(exporter.attributes(), [0, 2]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula", "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setAttributes([1]) self.assertEqual(exporter.attributes(), [1]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "cost":6.8 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setAttributes([]) # text excluding attributes exporter.setExcludedAttributes([1]) self.assertEqual(exporter.excludedAttributes(), [1]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula", "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setExcludedAttributes([1, 2]) self.assertEqual(exporter.excludedAttributes(), [1, 2]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "name":"Valsier Peninsula" } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setExcludedAttributes([0, 1, 2]) self.assertEqual(exporter.excludedAttributes(), [0, 1, 2]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":null }""" self.assertEqual(exporter.exportFeature(feature), expected) # test that excluded attributes take precedence over included exporter.setAttributes([1, 2]) exporter.setExcludedAttributes([0, 1]) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":{ "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setAttributes([]) exporter.setExcludedAttributes([]) # test excluding geometry exporter.setIncludeGeometry(False) self.assertEqual(exporter.includeGeometry(), False) feature.setGeometry(QgsGeometry(QgsLineString(l))) expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setIncludeGeometry(True) feature.setGeometry(QgsGeometry(QgsPointV2(5, 6))) # test excluding attributes exporter.setIncludeAttributes(False) self.assertEqual(exporter.includeAttributes(), False) expected = """{ "type":"Feature", "id":5, "geometry": {"type": "Point", "coordinates": [5, 6]}, "properties":null }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setIncludeGeometry(False) expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":null }""" self.assertEqual(exporter.exportFeature(feature), expected) exporter.setIncludeAttributes(True) # test overriding ID expected = """{ "type":"Feature", "id":29, "geometry":null, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198 } }""" self.assertEqual(exporter.exportFeature(feature, id=29), expected) # test injecting extra attributes expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "name":"Valsier Peninsula", "cost":6.8, "population":198, "extra":"val1", "extra2":2 } }""" self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": 2}), expected) exporter.setIncludeAttributes(False) expected = """{ "type":"Feature", "id":5, "geometry":null, "properties":{ "extra":"val1", "extra2":{"nested_map":5, "nested_map2":"val"}, "extra3":[1,2,3] } }""" self.assertEqual(exporter.exportFeature(feature, extraProperties={"extra": "val1", "extra2": {"nested_map": 5, "nested_map2": "val"}, "extra3": [1, 2, 3]}), expected) exporter.setIncludeGeometry(True)
def test_render_via_job(self): """ Test rendering an annotation layer via a map render job """ layer = QgsAnnotationLayer( 'test', QgsAnnotationLayer.LayerOptions( QgsProject.instance().transformContext())) self.assertTrue(layer.isValid()) item = QgsAnnotationPolygonItem( QgsPolygon( QgsLineString([ QgsPoint(11.5, 13), QgsPoint(12, 13), QgsPoint(12, 13.5), QgsPoint(11.5, 13) ]))) item.setSymbol( QgsFillSymbol.createSimple({ 'color': '200,100,100', 'outline_color': 'black', 'outline_width': '2' })) item.setZIndex(1) i1_id = layer.addItem(item) item = QgsAnnotationLineItem( QgsLineString( [QgsPoint(11, 13), QgsPoint(12, 13), QgsPoint(12, 15)])) item.setSymbol( QgsLineSymbol.createSimple({ 'color': '#ffff00', 'line_width': '3' })) item.setZIndex(2) i2_id = layer.addItem(item) item = QgsAnnotationMarkerItem(QgsPoint(12, 13)) item.setSymbol( QgsMarkerSymbol.createSimple({ 'color': '100,200,200', 'size': '6', 'outline_color': 'black' })) item.setZIndex(3) i3_id = layer.addItem(item) layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) settings = QgsMapSettings() settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326')) settings.setExtent(QgsRectangle(10, 10, 18, 18)) settings.setOutputSize(QSize(200, 200)) settings.setLayers([layer]) job = QgsMapRendererParallelJob(settings) job.start() job.waitForFinished() # check rendered item results item_results = job.takeRenderedItemResults() item_details = item_results.renderedItems() self.assertEqual(len(item_details), 3) self.assertEqual([i.layerId() for i in item_details], [layer.id()] * 3) self.assertCountEqual([i.itemId() for i in item_details], [i1_id, i2_id, i3_id]) self.assertCountEqual([ i.itemId() for i in item_results.renderedAnnotationItemsInBounds( QgsRectangle(0, 0, 1, 1)) ], []) self.assertCountEqual([ i.itemId() for i in item_results.renderedAnnotationItemsInBounds( QgsRectangle(10, 10, 11, 18)) ], [i2_id]) self.assertCountEqual([ i.itemId() for i in item_results.renderedAnnotationItemsInBounds( QgsRectangle(10, 10, 12, 18)) ], [i1_id, i2_id, i3_id]) # bounds should be in map crs self.assertEqual( [i.boundingBox() for i in item_details if i.itemId() == i1_id][0], QgsRectangle(11.5, 13, 12, 13.5)) self.assertEqual( [i.boundingBox() for i in item_details if i.itemId() == i2_id][0], QgsRectangle(11, 13, 12, 15)) self.assertEqual([ i.boundingBox().toString(1) for i in item_details if i.itemId() == i3_id ][0], '11.5,12.5 : 12.5,13.5')