12

I have a MultiLineStringZM layer in an SQLite database, and I'm trying to visualize the measures or m-values at the vertexes. I've tried looking up information on how to do this in QGIS, and all I've gathered is that this is not possible directly from the LineString layer and that the points need to be extracted to a separate layer.

I've used Vector > Geometry Tools > Extract nodes to create a multipoint layer representing the vertexes of my MultLineString layer, but the process loses the m-values of the vertexes. I need the m-values preserved by either saving the m-value as an attribute of the point, or something else?

Internally we have a command line tool that converts LineStrings to a point shapefile with the m-values stored as an attribute on each point. I've used that to verify that there are m-values assigned to the vertexes, and I could use that if I have to, but if possible it would be nice if this could be done directly inside of QGIS.

Repeating what I've said above, but re-emphasizing the fact that we do have a command line tool that can achieve the results that I'm looking for that uses the GDAL libraries. Hence, a solution showing just a partial answer in PyQGIS is not the answer I'm looking for. I'm looking for a built-in tool, a plugin ready-made for QGIS, or a complete script that can extract (not create/generate) and visualize m-values from a MultiLineStringZM or LineStringZM geometry.

6
  • You could use the LRS plugin to get the m values. You'd need to extract the nodes, then get the measures from the linestring using the LRS plugin or distance along line tools.
    – jbalk
    Jan 17, 2017 at 4:00
  • @jbalk I've tried the LRS and QChainage plugins, and both of those plugins seem to be set up to generate measures at regular intervals, not for using existing measures, unless I'm missing something and I'm just using the plugins incorrectly.
    – TJR
    Jan 17, 2017 at 14:59
  • From the LRS plugin page: -- The plugin supports calibration, creation of punctual and linear events and calculation of measures for points -- Here's the website blazek.github.io/lrs Ask a question about the LRS plugin on this site if you can't figure it out.
    – jbalk
    Jan 17, 2017 at 23:54
  • It looks like you can't do anything with the LRS plugin until you calibrate it, and in order to calibrate it, you need a point layer with measures stored as an attribute, which is exactly what I'm trying to get from my MultiLineStringZM, so I don't think it will be helpful in this situation.
    – TJR
    Jan 20, 2017 at 14:19
  • You could create points every 1000m along your line to use for calibration. Or look at distance along line tools in the SAGA and GRASS toolboxes within QGIS to get the m values.
    – jbalk
    Jan 22, 2017 at 23:53

2 Answers 2

7

With QGIS 3.0 or newer this task is trivial. In the "Processing Toolbox" (Open with ctrl+alt+t or Processing -> Toolbox) search for "Extract vertices" and run that algorithm.

Select your M or ZM line or polygon geometry as the Input layer, and run.

The vertices will be extracted with M and Z values intact depending on what is in the original geometry.

If the M value is needed as a field in the attribute table, then the field calculator can be used with an expression like m($geometry)or just using the "Extract M values" algorithm available in the Processing toolbox.

6

From what I can find there doesn't appear to be an existing solution for this exact situation, but I still wanted to be able to do this in QGIS, so I took the plunge into python scripting.

A guide for writing processing algorithms can be found here https://docs.qgis.org/2.18/en/docs/user_manual/processing/scripts.html

To use this code open up the Processing toolbox, then expand Scripts, then expand Tools. Select "Create new script" and copy and paste the code below into the script window (use caution when copying and pasting python code since whitespace is syntactically significant. If you are having problems put the code into a text editor that shows whitespace and make sure that it copied correctly). Save it wherever you want and there is an execute script button at the top of the window. After you save it you can "Add script from file" and permanently have the script under "User scripts".

When the processing window comes up select the layer that contains the vector geometry and select run. The script behaves the same way as "Extract Nodes" except that it adds a column called MValues and or ZValues depending on what is available in the input geometry.

##input_layer=vector
##output_layer=output vector

from qgis.core import QgsWKBTypes, QgsField, QgsVectorFileWriter, QgsFeature, QgsGeometry
from PyQt4.QtCore import QVariant

def addVertices( geometry, writer, inFeature ):
    coordinateSequence = geometry.coordinateSequence()
    for rings in coordinateSequence:
        for points in rings:
            for point in points:
                feature = QgsFeature( fields )
                feature.setGeometry( QgsGeometry( point ) )
                type = point.wkbType()
                attributes = inFeature.attributes()
                if QgsWKBTypes.hasM( type ):
                    attributes.append( point.m() )
                if QgsWKBTypes.hasZ( type ):
                    attributes.append(point.z())
                feature.setAttributes( attributes )
                writer.addFeature( feature )
    return

inlayer = processing.getObject( input_layer )
provider = inlayer.dataProvider()
fields = provider.fields()
geomType = QgsWKBTypes.Type(inlayer.wkbType())
outputGeomType = QgsWKBTypes.Point

if QgsWKBTypes.hasM( geomType ):
    outputGeomType = QgsWKBTypes.addM( outputGeomType )
    fields.append( QgsField( "MValue", QVariant.Double ) )

if QgsWKBTypes.hasZ( geomType ):
    outputGeomType = QgsWKBTypes.addZ( outputGeomType )
    fields.append( QgsField( "ZValue", QVariant.Double ) )

layer_options = 'SHPT=' + QgsWKBTypes.displayString(outputGeomType)
writer = QgsVectorFileWriter( output_layer, 'UTF-8', fields,  outputGeomType , inlayer.crs(), layerOptions=[layer_options] )

features = inlayer.getFeatures()
featureCount = inlayer.featureCount()
featureIndex = 0

for f in features:
    percent = ( featureIndex/float( featureCount ) ) * 100
    progress.setPercentage( percent )
    g = f.geometry().geometry()
    addVertices( g, writer, f )
    featureIndex +=1

del writer
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.