Draw line to next feature with distance as label (dynamic)

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
2
down vote

favorite
2












I would like to dynamically draw the shortest line to the next object (Geometry Generator?).



Example: I have a network-layer (streets) and a point-layer. I know the distance from every point to the closes street (static, by using v.distance or one of the plugins NNjoin or refFunctions) but I would like to have a dynamically drawn line to the next street with the distance as label. It's important to have it dynamic, so if I move a point, the line and distance should update.
Since I use GeoPackage a SQL-Solution would also be nice.



In the screenshot below you can see my static solution:



enter image description here










share|improve this question























  • Sounds like something that Attribute Assistant for ArcMap would be able to do although I don't know how specifically. But it can be set up to use geometry changes as a trigger to update attributes. I've been looking for a while to see if there's a similar tool available for QGIS but I haven't had any luck yet. Sorry not much help.
    – TeddyTedTed
    2 days ago














up vote
2
down vote

favorite
2












I would like to dynamically draw the shortest line to the next object (Geometry Generator?).



Example: I have a network-layer (streets) and a point-layer. I know the distance from every point to the closes street (static, by using v.distance or one of the plugins NNjoin or refFunctions) but I would like to have a dynamically drawn line to the next street with the distance as label. It's important to have it dynamic, so if I move a point, the line and distance should update.
Since I use GeoPackage a SQL-Solution would also be nice.



In the screenshot below you can see my static solution:



enter image description here










share|improve this question























  • Sounds like something that Attribute Assistant for ArcMap would be able to do although I don't know how specifically. But it can be set up to use geometry changes as a trigger to update attributes. I've been looking for a while to see if there's a similar tool available for QGIS but I haven't had any luck yet. Sorry not much help.
    – TeddyTedTed
    2 days ago












up vote
2
down vote

favorite
2









up vote
2
down vote

favorite
2






2





I would like to dynamically draw the shortest line to the next object (Geometry Generator?).



Example: I have a network-layer (streets) and a point-layer. I know the distance from every point to the closes street (static, by using v.distance or one of the plugins NNjoin or refFunctions) but I would like to have a dynamically drawn line to the next street with the distance as label. It's important to have it dynamic, so if I move a point, the line and distance should update.
Since I use GeoPackage a SQL-Solution would also be nice.



In the screenshot below you can see my static solution:



enter image description here










share|improve this question















I would like to dynamically draw the shortest line to the next object (Geometry Generator?).



Example: I have a network-layer (streets) and a point-layer. I know the distance from every point to the closes street (static, by using v.distance or one of the plugins NNjoin or refFunctions) but I would like to have a dynamically drawn line to the next street with the distance as label. It's important to have it dynamic, so if I move a point, the line and distance should update.
Since I use GeoPackage a SQL-Solution would also be nice.



In the screenshot below you can see my static solution:



enter image description here







qgis labeling sql qgis-3 geometry-generator






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 2 days ago

























asked 2 days ago









MAP

2,36523566




2,36523566











  • Sounds like something that Attribute Assistant for ArcMap would be able to do although I don't know how specifically. But it can be set up to use geometry changes as a trigger to update attributes. I've been looking for a while to see if there's a similar tool available for QGIS but I haven't had any luck yet. Sorry not much help.
    – TeddyTedTed
    2 days ago
















  • Sounds like something that Attribute Assistant for ArcMap would be able to do although I don't know how specifically. But it can be set up to use geometry changes as a trigger to update attributes. I've been looking for a while to see if there's a similar tool available for QGIS but I haven't had any luck yet. Sorry not much help.
    – TeddyTedTed
    2 days ago















Sounds like something that Attribute Assistant for ArcMap would be able to do although I don't know how specifically. But it can be set up to use geometry changes as a trigger to update attributes. I've been looking for a while to see if there's a similar tool available for QGIS but I haven't had any luck yet. Sorry not much help.
– TeddyTedTed
2 days ago




Sounds like something that Attribute Assistant for ArcMap would be able to do although I don't know how specifically. But it can be set up to use geometry changes as a trigger to update attributes. I've been looking for a while to see if there's a similar tool available for QGIS but I haven't had any luck yet. Sorry not much help.
– TeddyTedTed
2 days ago










2 Answers
2






active

oldest

votes

















up vote
1
down vote



accepted










You can do this with geometry generator in QGIS 3.x using the collect aggregate within the aggregate() function to extract the collected geometry of another layer and use it in an expression.



Here are the expressions I used to generate this example - the line layer was called Line; you just need to replace that with the name of your line layer.



enter image description here



Geometry generator (on point layer):



shortest_line($geometry,aggregate('Line','collect',$geometry))



Label:



round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)



Label placement (Label -> Placement -> Data defined):



Coordinate X - x(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



Coordinate Y -
y(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



Rotation -
line_interpolate_angle(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2)+90



Label alignment horizontal -
'Center'






share|improve this answer



























    up vote
    3
    down vote













    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


    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.



    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






    share|improve this answer






















    • Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
      – MAP
      2 days ago










    Your Answer








    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "79"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fgis.stackexchange.com%2fquestions%2f302886%2fdraw-line-to-next-feature-with-distance-as-label-dynamic%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    1
    down vote



    accepted










    You can do this with geometry generator in QGIS 3.x using the collect aggregate within the aggregate() function to extract the collected geometry of another layer and use it in an expression.



    Here are the expressions I used to generate this example - the line layer was called Line; you just need to replace that with the name of your line layer.



    enter image description here



    Geometry generator (on point layer):



    shortest_line($geometry,aggregate('Line','collect',$geometry))



    Label:



    round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)



    Label placement (Label -> Placement -> Data defined):



    Coordinate X - x(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



    Coordinate Y -
    y(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



    Rotation -
    line_interpolate_angle(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2)+90



    Label alignment horizontal -
    'Center'






    share|improve this answer
























      up vote
      1
      down vote



      accepted










      You can do this with geometry generator in QGIS 3.x using the collect aggregate within the aggregate() function to extract the collected geometry of another layer and use it in an expression.



      Here are the expressions I used to generate this example - the line layer was called Line; you just need to replace that with the name of your line layer.



      enter image description here



      Geometry generator (on point layer):



      shortest_line($geometry,aggregate('Line','collect',$geometry))



      Label:



      round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)



      Label placement (Label -> Placement -> Data defined):



      Coordinate X - x(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



      Coordinate Y -
      y(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



      Rotation -
      line_interpolate_angle(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2)+90



      Label alignment horizontal -
      'Center'






      share|improve this answer






















        up vote
        1
        down vote



        accepted







        up vote
        1
        down vote



        accepted






        You can do this with geometry generator in QGIS 3.x using the collect aggregate within the aggregate() function to extract the collected geometry of another layer and use it in an expression.



        Here are the expressions I used to generate this example - the line layer was called Line; you just need to replace that with the name of your line layer.



        enter image description here



        Geometry generator (on point layer):



        shortest_line($geometry,aggregate('Line','collect',$geometry))



        Label:



        round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)



        Label placement (Label -> Placement -> Data defined):



        Coordinate X - x(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



        Coordinate Y -
        y(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



        Rotation -
        line_interpolate_angle(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2)+90



        Label alignment horizontal -
        'Center'






        share|improve this answer












        You can do this with geometry generator in QGIS 3.x using the collect aggregate within the aggregate() function to extract the collected geometry of another layer and use it in an expression.



        Here are the expressions I used to generate this example - the line layer was called Line; you just need to replace that with the name of your line layer.



        enter image description here



        Geometry generator (on point layer):



        shortest_line($geometry,aggregate('Line','collect',$geometry))



        Label:



        round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)



        Label placement (Label -> Placement -> Data defined):



        Coordinate X - x(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



        Coordinate Y -
        y(line_interpolate_point(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2))



        Rotation -
        line_interpolate_angle(shortest_line($geometry,aggregate('Line','collect',$geometry)),round(length(shortest_line($geometry,aggregate('Line','collect',$geometry))),2)/2)+90



        Label alignment horizontal -
        'Center'







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered yesterday









        she_weeds

        1,012313




        1,012313






















            up vote
            3
            down vote













            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


            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.



            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






            share|improve this answer






















            • Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
              – MAP
              2 days ago














            up vote
            3
            down vote













            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


            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.



            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






            share|improve this answer






















            • Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
              – MAP
              2 days ago












            up vote
            3
            down vote










            up vote
            3
            down vote









            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


            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.



            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






            share|improve this answer














            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


            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.



            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







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited 2 days ago

























            answered 2 days ago









            Stefan

            2,55411638




            2,55411638











            • Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
              – MAP
              2 days ago
















            • Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
              – MAP
              2 days ago















            Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
            – MAP
            2 days ago




            Great work! Very nice! But I wasn't looking for a python solution. And that code is not working with PyQt5 of QGIS3.x. But this should help QGIS 2.x users in many cases. Thumbs up!
            – MAP
            2 days ago

















             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fgis.stackexchange.com%2fquestions%2f302886%2fdraw-line-to-next-feature-with-distance-as-label-dynamic%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown






            Popular posts from this blog

            How to check contact read email or not when send email to Individual?

            Displaying single band from multi-band raster using QGIS

            How many registers does an x86_64 CPU actually have?