Skip to main content
added 1883 characters in body
Source Link
Stefan
  • 4.5k
  • 1
  • 33
  • 66

Here is aare two Python solution:

Rubber Band approach:

  1. searches for the nearest point on the road

  2. creates a rubber band

  3. labels the rubber band

     from qgis.gui import *
     from qgis.utils import *
     from qgis.core import *
     from PyQt4 import QtGui, uic
     from PyQt4.QtGui import *
     from PyQt4.QtCore import *
    
     p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
     l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
     lines = [feature for feature in l_lyr.getFeatures()]
    
     def removeCanvasItems():
         canvas_items = [ i for i in iface.mapCanvas().scene().items() if issubclass(type(i), QgsRubberBand) or issubclass(type(i), QgsTextAnnotationItem) or issubclass(type(i), QgsVertexMarker)]
         if canvas_items:
             for item in canvas_items:
                 if item in iface.mapCanvas().scene().items():
                     iface.mapCanvas().scene().removeItem(item)
    
     def nearest_road():
         #removeCanvasItems()           #removes the old rubberbands, calling this causes a mini dump
         for point in p_lyr.getFeatures():
             minDistPoint = min([l.geometry().closestSegmentWithContext(QgsPoint(point.geometry().asPoint())) for l in lines])[1]
             points = [QgsPoint(point.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]
             r_polyline = QgsRubberBand(iface.mapCanvas(), False)
             r_polyline.setToGeometry(QgsGeometry.fromPolyline(points), None)
             r_polyline.setWidth(2)
             r_polyline.setColor(QColor(0,255,0,255))
             seg = QgsFeature()
             seg.setGeometry(QgsGeometry.fromPolyline(points))
             geom = seg.geometry()
             length = seg.geometry().length()
             symbol = QgsMarkerSymbolV2()
             symbol.setSize(0)
             lbltext = QTextDocument(str(round(length,2)) + 'm')
             label = QgsTextAnnotationItem(iface.mapCanvas())
             label.setMapPosition(seg.geometry().interpolate(length/2.0).asPoint())
             label.setDocument(lbltext)
             label.setFrameSize(QSizeF(lbltext.size().width(),lbltext.size().height()))
             label.setFrameBorderWidth(0)
             label.setFrameColor(QColor("#ff4b00"))
             label.setFrameBackgroundColor(QColor("#ff4b00"))
             label.setMarkerSymbol(symbol)
    
     nearest_road() #calling the function the first time
     p_lyr.geometryChanged.connect(nearest_road) # creates new rubberband when point is moved
     p_lyr.featureAdded.connect(nearest_road) # creates new rubberband when a new point is created
    

Vector Layer approach:

Instead of rubber bands you could use a temporary layer, which stores the label lines and refrehs on the signaldistance. In my case I will addhave a shapefile with two attributes: id (int), and distance(double). With this layer you can better label and style your features. The function removeFeatures(), removes all features from the distance layer. Better way is to my postget the ID of the feature that is moved and deletes only this line from the provider. I think I will update this soon.

from qgis.gui import *
from qgis.utils import *
from qgis.core import *
from PyQt4.QtGui import *
from PyQt4.QtCore import *

p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
lines = [feature for feature in l_lyr.getFeatures()]
d_lyr = QgsMapLayerRegistry.instance().mapLayersByName('distance')[0]
prov = d_lyr.dataProvider()

def removeFeatures():
    with edit(d_lyr):
        listOfIds = [feat.id() for feat in d_lyr.getFeatures()]
        d_lyr.deleteFeatures( listOfIds )

def nearest_road():
    removeFeatures()
    for point in p_lyr.getFeatures():
        minDistPoint = min([l.geometry().closestSegmentWithContext(QgsPoint(point.geometry().asPoint())) for l in lines])[1]
        points = [QgsPoint(point.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]
        seg = QgsFeature()
        seg.setGeometry(QgsGeometry.fromPolyline(points))
        geom = seg.geometry()
        length = seg.geometry().length()
        seg.setAttributes([1,seg.geometry().length()])
        prov.addFeatures([seg])
    d_lyr.updateExtents()
    d_lyr.triggerRepaint()
    d_lyr.updateFields()

nearest_road()
p_lyr.geometryChanged.connect(nearest_road)
p_lyr.featureAdded.connect(nearest_road)

enter image description here

Here is a Python solution:

  1. searches for the nearest point on the road

  2. creates a rubber band

  3. labels the rubber band

     from qgis.gui import *
     from qgis.utils import *
     from qgis.core import *
     from PyQt4 import QtGui, uic
     from PyQt4.QtGui import *
     from PyQt4.QtCore import *
    
     p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
     l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
     lines = [feature for feature in l_lyr.getFeatures()]
    
     def removeCanvasItems():
         canvas_items = [ i for i in iface.mapCanvas().scene().items() if issubclass(type(i), QgsRubberBand) or issubclass(type(i), QgsTextAnnotationItem) or issubclass(type(i), QgsVertexMarker)]
         if canvas_items:
             for item in canvas_items:
                 if item in iface.mapCanvas().scene().items():
                     iface.mapCanvas().scene().removeItem(item)
    
     def nearest_road():
         #removeCanvasItems()           #removes the old rubberbands, calling this causes a mini dump
         for point in p_lyr.getFeatures():
             minDistPoint = min([l.geometry().closestSegmentWithContext(QgsPoint(point.geometry().asPoint())) for l in lines])[1]
             points = [QgsPoint(point.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]
             r_polyline = QgsRubberBand(iface.mapCanvas(), False)
             r_polyline.setToGeometry(QgsGeometry.fromPolyline(points), None)
             r_polyline.setWidth(2)
             r_polyline.setColor(QColor(0,255,0,255))
             seg = QgsFeature()
             seg.setGeometry(QgsGeometry.fromPolyline(points))
             geom = seg.geometry()
             length = seg.geometry().length()
             symbol = QgsMarkerSymbolV2()
             symbol.setSize(0)
             lbltext = QTextDocument(str(round(length,2)) + 'm')
             label = QgsTextAnnotationItem(iface.mapCanvas())
             label.setMapPosition(seg.geometry().interpolate(length/2.0).asPoint())
             label.setDocument(lbltext)
             label.setFrameSize(QSizeF(lbltext.size().width(),lbltext.size().height()))
             label.setFrameBorderWidth(0)
             label.setFrameColor(QColor("#ff4b00"))
             label.setFrameBackgroundColor(QColor("#ff4b00"))
             label.setMarkerSymbol(symbol)
    
     nearest_road() #calling the function the first time
     p_lyr.geometryChanged.connect(nearest_road) # creates new rubberband when point is moved
     p_lyr.featureAdded.connect(nearest_road) # creates new rubberband when a new point is created
    

Instead of rubber bands you could use a temporary layer, which stores the label lines and refrehs on the signal. I will add this to my post soon.

Here are two Python solution:

Rubber Band approach:

  1. searches for the nearest point on the road

  2. creates a rubber band

  3. labels the rubber band

     from qgis.gui import *
     from qgis.utils import *
     from qgis.core import *
     from PyQt4.QtGui import *
     from PyQt4.QtCore import *
    
     p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
     l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
     lines = [feature for feature in l_lyr.getFeatures()]
    
     def removeCanvasItems():
         canvas_items = [ i for i in iface.mapCanvas().scene().items() if issubclass(type(i), QgsRubberBand) or issubclass(type(i), QgsTextAnnotationItem) or issubclass(type(i), QgsVertexMarker)]
         if canvas_items:
             for item in canvas_items:
                 if item in iface.mapCanvas().scene().items():
                     iface.mapCanvas().scene().removeItem(item)
    
     def nearest_road():
         #removeCanvasItems()           #removes the old rubberbands, calling this causes a mini dump
         for point in p_lyr.getFeatures():
             minDistPoint = min([l.geometry().closestSegmentWithContext(QgsPoint(point.geometry().asPoint())) for l in lines])[1]
             points = [QgsPoint(point.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]
             r_polyline = QgsRubberBand(iface.mapCanvas(), False)
             r_polyline.setToGeometry(QgsGeometry.fromPolyline(points), None)
             r_polyline.setWidth(2)
             r_polyline.setColor(QColor(0,255,0,255))
             seg = QgsFeature()
             seg.setGeometry(QgsGeometry.fromPolyline(points))
             geom = seg.geometry()
             length = seg.geometry().length()
             symbol = QgsMarkerSymbolV2()
             symbol.setSize(0)
             lbltext = QTextDocument(str(round(length,2)) + 'm')
             label = QgsTextAnnotationItem(iface.mapCanvas())
             label.setMapPosition(seg.geometry().interpolate(length/2.0).asPoint())
             label.setDocument(lbltext)
             label.setFrameSize(QSizeF(lbltext.size().width(),lbltext.size().height()))
             label.setFrameBorderWidth(0)
             label.setFrameColor(QColor("#ff4b00"))
             label.setFrameBackgroundColor(QColor("#ff4b00"))
             label.setMarkerSymbol(symbol)
    
     nearest_road() #calling the function the first time
     p_lyr.geometryChanged.connect(nearest_road) # creates new rubberband when point is moved
     p_lyr.featureAdded.connect(nearest_road) # creates new rubberband when a new point is created
    

Vector Layer approach:

Instead of rubber bands you could use a layer, which stores the lines and the distance. In my case I have a shapefile with two attributes: id (int), and distance(double). With this layer you can better label and style your features. The function removeFeatures(), removes all features from the distance layer. Better way is to get the ID of the feature that is moved and deletes only this line from the provider. I think I will update this soon.

from qgis.gui import *
from qgis.utils import *
from qgis.core import *
from PyQt4.QtGui import *
from PyQt4.QtCore import *

p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
lines = [feature for feature in l_lyr.getFeatures()]
d_lyr = QgsMapLayerRegistry.instance().mapLayersByName('distance')[0]
prov = d_lyr.dataProvider()

def removeFeatures():
    with edit(d_lyr):
        listOfIds = [feat.id() for feat in d_lyr.getFeatures()]
        d_lyr.deleteFeatures( listOfIds )

def nearest_road():
    removeFeatures()
    for point in p_lyr.getFeatures():
        minDistPoint = min([l.geometry().closestSegmentWithContext(QgsPoint(point.geometry().asPoint())) for l in lines])[1]
        points = [QgsPoint(point.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]
        seg = QgsFeature()
        seg.setGeometry(QgsGeometry.fromPolyline(points))
        geom = seg.geometry()
        length = seg.geometry().length()
        seg.setAttributes([1,seg.geometry().length()])
        prov.addFeatures([seg])
    d_lyr.updateExtents()
    d_lyr.triggerRepaint()
    d_lyr.updateFields()

nearest_road()
p_lyr.geometryChanged.connect(nearest_road)
p_lyr.featureAdded.connect(nearest_road)

enter image description here

Source Link
Stefan
  • 4.5k
  • 1
  • 33
  • 66

Here is a Python solution:

  1. searches for the nearest point on the road

  2. creates a rubber band

  3. labels the rubber band

     from qgis.gui import *
     from qgis.utils import *
     from qgis.core import *
     from PyQt4 import QtGui, uic
     from PyQt4.QtGui import *
     from PyQt4.QtCore import *
    
     p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
     l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
     lines = [feature for feature in l_lyr.getFeatures()]
    
     def removeCanvasItems():
         canvas_items = [ i for i in iface.mapCanvas().scene().items() if issubclass(type(i), QgsRubberBand) or issubclass(type(i), QgsTextAnnotationItem) or issubclass(type(i), QgsVertexMarker)]
         if canvas_items:
             for item in canvas_items:
                 if item in iface.mapCanvas().scene().items():
                     iface.mapCanvas().scene().removeItem(item)
    
     def nearest_road():
         #removeCanvasItems()           #removes the old rubberbands, calling this causes a mini dump
         for point in p_lyr.getFeatures():
             minDistPoint = min([l.geometry().closestSegmentWithContext(QgsPoint(point.geometry().asPoint())) for l in lines])[1]
             points = [QgsPoint(point.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]
             r_polyline = QgsRubberBand(iface.mapCanvas(), False)
             r_polyline.setToGeometry(QgsGeometry.fromPolyline(points), None)
             r_polyline.setWidth(2)
             r_polyline.setColor(QColor(0,255,0,255))
             seg = QgsFeature()
             seg.setGeometry(QgsGeometry.fromPolyline(points))
             geom = seg.geometry()
             length = seg.geometry().length()
             symbol = QgsMarkerSymbolV2()
             symbol.setSize(0)
             lbltext = QTextDocument(str(round(length,2)) + 'm')
             label = QgsTextAnnotationItem(iface.mapCanvas())
             label.setMapPosition(seg.geometry().interpolate(length/2.0).asPoint())
             label.setDocument(lbltext)
             label.setFrameSize(QSizeF(lbltext.size().width(),lbltext.size().height()))
             label.setFrameBorderWidth(0)
             label.setFrameColor(QColor("#ff4b00"))
             label.setFrameBackgroundColor(QColor("#ff4b00"))
             label.setMarkerSymbol(symbol)
    
     nearest_road() #calling the function the first time
     p_lyr.geometryChanged.connect(nearest_road) # creates new rubberband when point is moved
     p_lyr.featureAdded.connect(nearest_road) # creates new rubberband when a new point is created
    

enter image description here

When I want to remove the old rubber bands while adding a new feature or move an existing feature, I get a mini dump and QGIS crashes.

Instead of rubber bands you could use a temporary layer, which stores the label lines and refrehs on the signal. I will add this to my post soon.