def swap_vectors(self, layer, selected=True): '''Swap / reverse vector direction for line layers Parameters ---------- layer : QgsVectorLayer input line vector layer selected : boolean swap only selected or all features (Default value = True) Returns ------- error : boolean 0/1 - no error/error result : str or None output or error msg if error == 1 ''' features = self.get_features(layer, selected=selected) with edit(layer): # reverse line direction for each (selected) feature for feature in features: geom = feature.geometry() if geom.isMultipart(): mls = QgsMultiLineString() for line in geom.asGeometryCollection(): mls.addGeometry(line.constGet().reversed()) newgeom = QgsGeometry(mls) layer.changeGeometry(feature.id(), newgeom) else: newgeom = QgsGeometry(geom.constGet().reversed()) layer.changeGeometry(feature.id(), newgeom) return 0, None
def reverseLineDirection(self, line_geom): mls1 = line_geom.get() mls2 = QgsMultiLineString() # For each reversed linestring, visited in reverse order for i in [QgsLineString([*i][::-1]) for i in [*mls1][::-1]]: _ = mls2.addGeometry(i) # add it to new geometry new_geometry = QgsGeometry(mls2) return new_geometry
def test_except_check_singlepart_lines_more_parts(self): mls = QgsMultiLineString() mls.addGeometry(QgsLineString([QgsPoint(210, 41), QgsPoint(301, 55)])) mls.addGeometry(QgsLineString([QgsPoint(210, 55), QgsPoint(301, 41)])) vl = TestGateTransformer.make_single_feature_layer( "MultiLineString", mls) with self.assertRaises(BadInputError) as e: TransformerAnalysis.GateTransformer.check_singlepart_lines(vl) self.assertEqual( str(e.exception), "Feature with id 1 contains more than 1 line, please correct")
def convertToLines(self, geometry): rings = self.getRings(geometry.constGet()) output_wkb = self.convertWkbToLines(geometry.wkbType()) out_geom = None if QgsWkbTypes.flatType(output_wkb) == QgsWkbTypes.MultiLineString: out_geom = QgsMultiLineString() else: out_geom = QgsMultiCurve() for ring in rings: out_geom.addGeometry(ring) return out_geom
def convertToLines(self, geometry): rings = self.getRings(geometry.constGet()) output_wkb = self.convertWkbToLines(geometry.wkbType()) out_geom = None if QgsWkbTypes.flatType(output_wkb) == QgsWkbTypes.MultiLineString: out_geom = QgsMultiLineString() else: out_geom = QgsMultiCurve() for ring in rings: out_geom.addGeometry(ring) return out_geom
def segmentizeBoundaries(self, geom): """geom: QgsGeometry (polygon or multi-polygon)""" xmin, ymax = (self.xmin, self.ymax) xres, yres = (self.xres, self.yres) z_func = self.valueOnSurface polys = [] for polygon in PolygonGeometry.nestedPointXYList(geom): rings = QgsMultiLineString() for i, bnd in enumerate(polygon): if GeometryUtils.isClockwise(bnd) ^ (i > 0): # xor bnd.reverse() # outer boundary should be ccw. inner boundaries should be cw. ring = QgsLineString() v = bnd[0] # QgsPointXY x0, y0 = (v.x(), v.y()) nx0 = (x0 - xmin) / xres ny0 = (ymax - y0) / yres ns0 = abs(ny0 + nx0) for v in bnd[1:]: x1, y1 = (v.x(), v.y()) nx1 = (x1 - xmin) / xres ny1 = (ymax - y1) / yres ns1 = abs(ny1 + nx1) p = set([0]) for v0, v1 in [[nx0, nx1], [ny0, ny1], [ns0, ns1]]: k = ceil(min(v0, v1)) n = floor(max(v0, v1)) for j in range(k, n + 1): p.add((j - v0) / (v1 - v0)) if 1 in p: p.remove(1) for m in sorted(p): x = x0 + (x1 - x0) * m y = y0 + (y1 - y0) * m ring.addVertex(QgsPoint(x, y, z_func(x, y))) x0, y0 = (x1, y1) nx0, ny0, ns0 = (nx1, ny1, ns1) ring.addVertex(QgsPoint(x0, y0, z_func(x0, y0))) # last vertex rings.addGeometry(ring) polys.append(QgsGeometry(rings)) return polys
def processFeature(self, feature, context, feedback): #pylint: disable=no-self-use,unused-argument,missing-docstring def transform(geometry): """ Transform Z into Slope. Opposite to mathematical convention, slope is oriented in reverse line direction. """ vertices = np.array([(v.x(), v.y(), v.z()) for v in geometry.vertices()]) slope = np.zeros(len(vertices), dtype=np.float32) if len(vertices) > 2: # 2D horizontal distance distance = np.linalg.norm(vertices[:-1, 0:2] - vertices[1:, 0:2], axis=1) slope[1:-1] = (vertices[:-2, 2] - vertices[2:, 2]) / ( distance[:-1] + distance[1:]) slope[0] = slope[1] slope[-1] = slope[-2] points = list() for i, vertex in enumerate(geometry.vertices()): vertex.setZ(float(slope[i])) points.append(vertex) return QgsLineString(points) geometry = feature.geometry() if geometry.isMultipart(): parts = QgsMultiLineString() for part in geometry.asGeometryCollection(): linestring = transform(part) parts.addGeometry(linestring) feature.setGeometry(QgsGeometry(parts)) else: feature.setGeometry(QgsGeometry(transform(geometry))) return [feature]
def convertToMultiLineStrings(self, geom): if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry: raise QgsProcessingException( self.tr('Cannot convert from {0} to MultiLineStrings').format(QgsWkbTypes.displayString(geom.wkbType()))) elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry: if QgsWkbTypes.isMultiType(geom.wkbType()): return [geom] else: # line to multiLine ml = QgsMultiLineString() ml.addGeometry(geom.constGet().clone()) return [QgsGeometry(ml)] else: # polygons to multilinestring # we just use the boundary here - that consists of all rings in the (multi)polygon return [QgsGeometry(geom.constGet().boundary())]
def convertToMultiLineStrings(self, geom): if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry: raise QgsProcessingException( self.tr('Cannot convert from {0} to MultiLineStrings').format(QgsWkbTypes.displayString(geom.wkbType()))) elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry: if QgsWkbTypes.isMultiType(geom.wkbType()): return [geom] else: # line to multiLine ml = QgsMultiLineString() ml.addGeometry(geom.constGet().clone()) return [QgsGeometry(ml)] else: # polygons to multilinestring # we just use the boundary here - that consists of all rings in the (multi)polygon return [QgsGeometry(geom.constGet().boundary())]
def process(self, name, desc, alt_mode, begin, end, when): if self.extDataSize > 0: extAttr = [''] * self.extDataSize for key in self.extendedData.keys(): if key in self.extDataMap: extAttr[self.extDataMap[key]] = self.extendedData[key] # POINTS if len(self.ptPts) != 0: for x, pt in enumerate(self.ptPts): feature = QgsFeature() feature.setGeometry(QgsGeometry(pt)) attr = [ name, self.folderString(), desc, self.ptAltitude[x], alt_mode, begin, end, when ] if self.extDataSize > 0: attr.extend(extAttr) feature.setAttributes(attr) self.addpoint.emit(feature) # LINES - lineStrings is a list of QgsLineString if len(self.lineStrings) != 0: feature = QgsFeature() if len(self.lineStrings) == 1: feature.setGeometry(QgsGeometry(self.lineStrings[0])) else: g = QgsMultiLineString() for lineStr in self.lineStrings: g.addGeometry(lineStr) feature.setGeometry(QgsGeometry(g)) attr = [ name, self.folderString(), desc, 0, alt_mode, begin, end, when ] if self.extDataSize > 0: attr.extend(extAttr) feature.setAttributes(attr) self.addline.emit(feature) # POLYGONS if len(self.polygons) != 0: feature = QgsFeature() if len(self.polygons) == 1: feature.setGeometry(QgsGeometry(self.polygons[0])) else: g = QgsMultiPolygon() for poly in self.polygons: g.addGeometry(poly) feature.setGeometry(QgsGeometry(g)) attr = [ name, self.folderString(), desc, 0, alt_mode, begin, end, when ] if self.extDataSize > 0: attr.extend(extAttr) feature.setAttributes(attr) self.addpolygon.emit(feature)
def test_except_check_singlepart_lines_no_parts(self): mls = QgsMultiLineString() vl = TestGateTransformer.make_single_feature_layer( "MultiLineString", mls) with self.assertRaises(BadInputError) as e: TransformerAnalysis.GateTransformer.check_singlepart_lines(vl) self.assertEqual( str(e.exception), "Feature with id 1 has no line geometry, please correct")
def get_pair_boundary_plot(self, boundary_layer, plot_layer, id_field=ID_FIELD, use_selection=True): id_field_idx = plot_layer.fields().indexFromName(id_field) request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx]) polygons = plot_layer.getSelectedFeatures( request) if use_selection else plot_layer.getFeatures(request) intersect_more_pairs = list() intersect_less_pairs = list() if boundary_layer.featureCount() == 0: return (intersect_more_pairs, intersect_less_pairs) id_field_idx = boundary_layer.fields().indexFromName(id_field) request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx]) dict_features = { feature.id(): feature for feature in boundary_layer.getFeatures(request) } index = QgsSpatialIndex(boundary_layer) candidate_features = None for polygon in polygons: bbox = polygon.geometry().boundingBox() bbox.scale(1.001) candidates_ids = index.intersects(bbox) candidate_features = [ dict_features[candidate_id] for candidate_id in candidates_ids ] for candidate_feature in candidate_features: polygon_geom = polygon.geometry() is_multipart = polygon_geom.isMultipart() candidate_geometry = candidate_feature.geometry() if polygon_geom.intersects(candidate_geometry): # Does the current multipolygon have inner rings? has_inner_rings = False multi_polygon = None single_polygon = None if is_multipart: multi_polygon = polygon_geom.get() for part in range(multi_polygon.numGeometries()): if multi_polygon.ringCount(part) > 1: has_inner_rings = True break else: single_polygon = polygon_geom.get() if single_polygon.numInteriorRings() > 0: has_inner_rings = True # Now we'll test intersections against borders if has_inner_rings: # In this case we need to identify whether the # intersection is with outer rings (goes to MOREBFS # table) or with inner rings (goes to LESS table) multi_outer_rings = QgsMultiLineString() multi_inner_rings = QgsMultiLineString() if is_multipart and multi_polygon: for i in range(multi_polygon.numGeometries()): temp_polygon = multi_polygon.geometryN(i) multi_outer_rings.addGeometry( temp_polygon.exteriorRing().clone()) for j in range( temp_polygon.numInteriorRings()): multi_inner_rings.addGeometry( temp_polygon.interiorRing(j).clone()) elif not is_multipart and single_polygon: multi_outer_rings.addGeometry( single_polygon.exteriorRing().clone()) for j in range(single_polygon.numInteriorRings()): multi_inner_rings.addGeometry( single_polygon.interiorRing(j).clone()) intersection_type = QgsGeometry( multi_outer_rings).intersection( candidate_geometry).type() if intersection_type == QgsWkbTypes.LineGeometry: intersect_more_pairs.append( (polygon[id_field], candidate_feature[id_field])) else: self.log.logMessage( "(MoreBFS) Intersection between plot (t_id={}) and boundary (t_id={}) is a geometry of type: {}" .format(polygon[id_field], candidate_feature[id_field], intersection_type), PLUGIN_NAME, Qgis.Warning) intersection_type = QgsGeometry( multi_inner_rings).intersection( candidate_geometry).type() if intersection_type == QgsWkbTypes.LineGeometry: intersect_less_pairs.append( (polygon[id_field], candidate_feature[id_field])) else: self.log.logMessage( "(Less) Intersection between plot (t_id={}) and boundary (t_id={}) is a geometry of type: {}" .format(polygon[id_field], candidate_feature[id_field], intersection_type), PLUGIN_NAME, Qgis.Warning) else: boundary = None if is_multipart and multi_polygon: boundary = multi_polygon.boundary() elif not is_multipart and single_polygon: boundary = single_polygon.boundary() intersection_type = QgsGeometry(boundary).intersection( candidate_geometry).type() if boundary and intersection_type == QgsWkbTypes.LineGeometry: intersect_more_pairs.append( (polygon[id_field], candidate_feature[id_field])) else: self.log.logMessage( "(MoreBFS) Intersection between plot (t_id={}) and boundary (t_id={}) is a geometry of type: {}" .format(polygon[id_field], candidate_feature[id_field], intersection_type), PLUGIN_NAME, Qgis.Warning) # free up memory del candidate_features del dict_features gc.collect() return (intersect_more_pairs, intersect_less_pairs)
import time from qgis.core import QgsLineString, QgsMultiLineString, QgsPoint, QgsProject POLY_LYR = "test_poly" lyr_polys = QgsProject.instance().mapLayersByName(POLY_LYR)[0] t0 = time.time() for counter, poly in enumerate(lyr_polys.getFeatures()): centroid = QgsPoint(poly.geometry().centroid().asPoint()) star = QgsMultiLineString() for vertex in poly.geometry().vertices(): line = QgsLineString(centroid, vertex) star.addGeometry(line) t1 = time.time() print(f"PYTHON: run {counter + 1} times for {t1-t0:.2f} secs")
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 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))