def theMaker(self, aDict, alayer): feats = [] for i in range(0, len(aDict) - 1): frwrd = QgsPointXY(aDict[i][1]) # start point rvrs = QgsPointXY(aDict[i + 1][1]) # second point az = frwrd.azimuth(rvrs) distance = math.sqrt(frwrd.sqrDist(rvrs)) c = Calculus.getSemiMajorAndSemiMinorAxis(self, alayer) a = Calculus.getGeographicCoordinates(self, alayer, rvrs.x(), rvrs.y()) conv = Calculus.calculateConvergence(self, a.x(), a.y(), c[0], c[1]) if az < 0: az += 360 azm = az + conv longitude = Calculus.dd2dms( self, Calculus.getGeographicCoordinates(self, alayer, aDict[i][1].x(), aDict[i][1].y()).x()) latitude = Calculus.dd2dms( self, Calculus.getGeographicCoordinates(self, alayer, aDict[i][1].x(), aDict[i][1].y()).y()) fet = QgsFeature() geom = QgsGeometry().fromPointXY(aDict[0][1]) fet.setGeometry(geom) feats.append(fet) fet.setAttributes([ list(aDict.keys())[i], aDict[i][0], aDict[i][1].x(), aDict[i][1].y(), longitude, latitude, aDict[i + 1][0], Calculus.dd2dms(self, azm), distance ]) return feats
def processAlgorithm(self, parameters, context, feedback): layer = self.parameterAsSource(parameters, self.Centerline, context) layer2 = self.parameterAsVectorLayer(parameters, self.Polygons, context) samples = parameters[self.Samples] distance = parameters[self.Distance] FC = parameters[self.FC] context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) if layer.sourceCrs() != layer2.sourceCrs(): feedback.reportError(QCoreApplication.translate('Error','WARNING: Centerline and Polygon input do not have the same projection')) Precision=5 if FC: field_names = ['Distance','SP_Dist','Width','Deviation','DWidthL','DWidthR'] else: field_names = ['Distance','SP_Dist','Width','Deviation','DWidthL','DWidthR','Diff'] fields = QgsFields() fields.append( QgsField('ID', QVariant.Int )) for name in field_names: fields.append( QgsField(name, QVariant.Double )) (writer, dest_id) = self.parameterAsSink(parameters, self.Output, context, fields, QgsWkbTypes.LineString, layer.sourceCrs()) fet = QgsFeature() field_check =layer.fields().indexFromName('ID') field_check2 =layer2.fields().indexFromName('ID') if field_check == -1 or field_check2 == -1: feedback.reportError(QCoreApplication.translate('Error','Centerline and Polygon input feature require a matching ID field!')) return {} total = 0 counts = {} if FC: vertices = st.run("native:extractvertices", {'INPUT':layer2,'OUTPUT':'memory:'}) index = QgsSpatialIndex(vertices['OUTPUT'].getFeatures()) data = {feature.id():feature for feature in vertices['OUTPUT'].getFeatures()} SPS = {} SPE = {} values = {} values2 = {} feats = {f["ID"]:f for f in layer2.getFeatures()} feedback.pushInfo(QCoreApplication.translate('Update','Defining Centerline Paths')) for enum,feature in enumerate(layer.getFeatures()): total += 1 try: pnt = feature.geometry() if pnt.isMultipart(): pnt = pnt.asMultiPolyline()[0] else: pnt = pnt.asPolyline() startx,starty = round(pnt[0][0],Precision),round(pnt[0][1],Precision) endx,endy = round(pnt[-1][0],Precision),round(pnt[-1][1],Precision) ID = feature['ID'] c = feature['Distance'] if ID in SPS: #Get start and endpoint of each centerline v = values[ID] v2 = values2[ID] v3 = counts[ID] + 1 if c > v: SPS[ID] = [(startx,starty),(endx,endy)] values[ID] = c if c < v2: SPE[ID] = [(startx,starty),(endx,endy)] values2[ID] = c counts[ID] = v3 else: SPS[ID] = [(startx,starty),(endx,endy)] values[ID] = c SPE[ID] = [(startx,starty),(endx,endy)] values2[ID] = c counts[ID] = 1 except Exception as e: feedback.reportError(QCoreApplication.translate('Error','%s'%(e))) continue ##Possible Collapsed Polyline? del values,values2 total = 100.0/float(total) ID = None feedback.pushInfo(QCoreApplication.translate('Update','Creating Width Measurements')) report = True for enum,feature in enumerate(layer.getFeatures()): try: if total != -1: feedback.setProgress(int(enum*total)) pnt = feature.geometry() L = pnt.length() if pnt.isMultipart(): pnt = pnt.asMultiPolyline()[0] else: pnt = pnt.asPolyline() curID = feature["ID"] if ID != curID: startx,starty = round(pnt[0][0],Precision),round(pnt[0][1],Precision) midx,midy = round(pnt[-1][0],Precision),round(pnt[-1][1],Precision) ID = curID if samples > 0: if distance: Counter = L Limit = float(samples) else: Counter = 1 Limit = round((counts[ID]/float(samples)),0) continue endx,endy = round(pnt[-1][0],Precision),round(pnt[-1][1],Precision) if samples > 0: if distance: Counter += L else: Counter += 1 if Counter < Limit: startx,starty = midx,midy midx,midy = endx,endy continue if FC: startx,starty = round(pnt[0][0],Precision),round(pnt[0][1],Precision) near = index.nearestNeighbor(QgsPointXY(startx,starty), 1) SPv = 1e12 midx,midy = data[near[0]].geometry().asPoint() dx,dy = startx-midx,starty-midy shortestPath = sqrt((dx**2)+(dy**2)) if shortestPath < SPv: SPv = shortestPath near = index.nearestNeighbor(QgsPointXY(endx,endy), 1) midx,midy = data[near[0]].geometry().asPoint() dx,dy = endx-midx,endy-midy shortestPath = sqrt((dx**2)+(dy**2)) if shortestPath < SPv: SP = shortestPath else: m = ((starty - endy)/(startx - endx)) #Slope inter = feats[curID] Distance = inter.geometry().boundingBox().width()/2 if startx==endx: #if vertical x1,y1 = midx+Distance,midy x2,y2 = midx - Distance,midy else: m = ((starty - endy)/(startx - endx)) #Slope angle = degrees(atan(m)) + 90 m = tan(radians(angle)) #Angle to Slope c,s = (1/sqrt(1+m**2),m/sqrt(1+m**2)) #cosine and sin x1,y1 = (midx + Distance*(c),midy + Distance*(s)) x2,y2 = (midx - Distance*(c),midy - Distance*(s)) geom = QgsGeometry.fromPolylineXY([QgsPointXY(x1,y1),QgsPointXY(midx,midy),QgsPointXY(x2,y2)]) geom = geom.intersection(inter.geometry()) if geom.isMultipart(): polyline = geom.asMultiPolyline() if len(polyline) == 0: startx,starty = midx,midy midx,midy = endx,endy continue for line in polyline: if len(line)==3: t=1 start,mid,end = line geom1 = QgsGeometry.fromPolylineXY([QgsPointXY(start[0],start[1]),QgsPointXY(mid[0],mid[1])]) geom2 = QgsGeometry.fromPolylineXY([QgsPointXY(mid[0],mid[1]),QgsPointXY(end[0],end[1])]) geom = QgsGeometry.fromPolylineXY([QgsPointXY(start[0],start[1]),QgsPointXY(mid[0],mid[1]),QgsPointXY(end[0],end[1])]) break else: try: line = geom.asPolyline() except Exception as e: startx,starty = midx,midy midx,midy = endx,endy if report: report = False feedback.reportError(QCoreApplication.translate('Error','Width measurement along centerline does not intersect with input polygons. Check 1. ID fields corresponds between centerline and polygons 2. Geometry of centerline and polygon inputs by using the "Fix Geometries" tool')) continue geom1 = QgsGeometry.fromPolylineXY([QgsPointXY(line[0][0],line[0][1]),QgsPointXY(line[1][0],line[1][1])]) geom2 = QgsGeometry.fromPolylineXY([QgsPointXY(line[1][0],line[1][1]),QgsPointXY(line[2][0],line[2][1])]) Widths = [geom1.length(),geom2.length()] SP = list(SPS[curID]) SP.extend(list(SPE[curID])) D = 0 for start,end in combinations(SP,2): dx = start[0] - end[0] dy = start[1] - end[1] shortestPath = sqrt((dx**2)+(dy**2)) if shortestPath > D: D = shortestPath s = QgsPointXY(start[0],start[1]) e = QgsPointXY(end[0],end[1]) m = s.sqrDist(e) u = ((midx - s.x()) * (e.x() - s.x()) + (midy - s.y()) * (e.y() - s.y()))/(m) x = s.x() + u * (e.x() - s.x()) y = s.y() + u * (e.y() - s.y()) d = ((e.x()-s.x())*(midy-s.y()) - (e.y() - s.y())*(midx - s.x())) #Determine which side of the SP the symmetry occurs dx = s.x() - e.x() dy = s.y() - e.y() shortestPath = sqrt((dx**2)+(dy**2)) dx = s.x() - x dy = s.y() - y shortestPath1 = sqrt((dx**2)+(dy**2)) if shortestPath < shortestPath1: sym = QgsGeometry.fromPolylineXY([QgsPointXY(e.x(),e.y()),QgsPointXY(midx,midy)]) else: sym = QgsGeometry.fromPolylineXY([QgsPointXY(x,y),QgsPointXY(midx,midy)]) if d < 0: DW = -(sym.length()) else: DW = sym.length() if FC: W = SPv*2 rows = [curID,feature['Distance'],feature['SP_Dist'],W,DW,(W/2)+DW,-(W/2)+DW] geom = feature.geometry() else: W = geom.length() rows = [curID,feature['Distance'],feature['SP_Dist'],W,DW,(W/2)+DW,-(W/2)+DW,(min(Widths)/max(Widths))*100] startx,starty = midx,midy midx,midy = endx,endy fet.setGeometry(geom) fet.setAttributes(rows) writer.addFeature(fet) if distance: Counter -= samples else: Counter = 0 except Exception as e: #feedback.reportError(QCoreApplication.translate('Error','%s'%(e))) startx,starty = midx,midy midx,midy = endx,endy continue del writer if FC: del data del SPS,SPE return {self.Output:dest_id}