How can I locate the coordinates of the middle point (midpoint) of a line feature in QGIS?
7 Answers
Since this question was asked, an interpolate function has been added to PyQGIS on the QgsGeometry object.
Here is a quick example of how to use this in the Python console inside of QGIS (Plugins -> Python Console).
layer = iface.activeLayer() #layer selected in your layers panel
feature = layer.selectedFeatures()[0] #the first feature of selected features
geom = feature.geometry() #QgsGeometry representing your line
length = geom.length() #length of geometry in the layer CRS. If EPSG:4326 this will be degrees
point = geom.interpolate(length/2.0) #QgsGeometry representing the mid point
x = point.geometry().x() #X coordinate in layer CRS
y = point.geometry().y() #Y coordinate in layer CRS
An even easier option...
In the processing toolbox there is a QGIS function under Vector Geometry
that is called Interpolate point on line
that is most likely using the interpolate function described above.
For your input layer select the line layer you want to find the midpoints
Warning! Make sure you are using a projected coordinate system appropriate for the area where you are finding midpoints. Geographic coordinate systems like EPSG 4326 (WGS 84) are not accurate for measuring distances, and a world wide projected coordinate system like EPSG 3857 (WGS 84 / Pseudo-Mercator) will not be as accurate as a projected coordinate system for a specific region.
For distance, this is the distance to be interpolated along the line by the given units. If you give a distance of 10 and meters is selected, then a point will be created 10 meters along the line for every line in your layer. What we want here is a calculated value, not a static value like 10. To calculate a value for distance, click on the drop down to the far right (shown circled in red below) and select edit...
In the expression box, put in the expression $length / 2.0
This will calculate half the length of the given line feature in the coordinate reference system for that layer (hence the warning above). Select OK.
Run the Interpolate Point on Line
function, and it will then create a point layer with a point at the midpoint of every line feature in the selected layer, and each midpoint will have the attributes of the original line feature copied over to it.
-
Rokefellar - Does QGIS 3.8 handle the feature = layer line differently? I get index out of range.– GeorgeCCommented Jul 1, 2019 at 13:30
-
1@GeorgeC for layer.selectedFeatures()[0] to work, you literally have to have features selected in QGIS. Get the selection tool and select something. The code above is just a very quick and dirty proof of concept.– TJRCommented Jul 1, 2019 at 19:09
-
I tried this -what would I need to do to get it to run on the whole dataset?– GeorgeCCommented Jul 1, 2019 at 22:40
-
@GeorgeC This is just a proof of concept. What you are asking does not have a trivial answer that can be explained in a comment. You will need to research writing Python scripts in QGIS and possibly ask a more detailed question on this site. You will need to automate looping through all features and figure out what you want your output to be, I'm assuming a point layer?– TJRCommented Jul 2, 2019 at 13:31
-
1@GeorgeC it had been awhile since I had looked at this question, and looking into it again I found
Interpolate Point on Line
that I have described above, that I think will do what you are looking for.– TJRCommented Jul 8, 2019 at 15:20
If you bring your line feature into spatialite, you can use the Line_Interpolate_Point() function to find the mid point.
SELECT AsText(Line_Interpolate_Point(your_line.geomemtry, 0.5))
The second parameter 0.5 indicates the fraction of the line length where the point will be located.
It is a pure geometry problem that can be solved in the Python console
The problem:
Find the midpoint of a segment x1,y1,x2,y2 is easy
x = (x1 + x2)/2
y = (y1 + y2)/2
so in the Python console
def mid(pt1, pt2):
x = (pt1.x() + pt2.x())/2
y = (pt1.y() + pt2.y())/2
return QgsPoint(x,y)
def pair(list):
'''Iterate over pairs in a list '''
for i in range(1, len(list)):
yield list[i-1], list[i]
def create_geometry(point, pr):
# create geometry record
seg = QgsFeature()
seg.setGeometry(QgsGeometry.fromPoint(point))
pr.addFeatures( [seg] )
# memory layer
pt_layer = QgsVectorLayer("Point", "midpoint", "memory")
pr = pt_layer.dataProvider()
for elem in mylayer.selectedFeatures():
line = elem.geometry()
for seg_start, seg_end in pair(line.asPolyline()):
line_start = QgsPoint(seg_start)
line_end = QgsPoint(seg_end)
# midpoint
midpt = mid(line_start, line_end)
# add midpoint point to layer
create_geometry(midpt,pr)
pt_layer.updateExtents()
QgsMapLayerRegistry.instance().addMapLayers([pt_layer])
Result
-
4Just a note that this approach finds the mid point of each line segment, not the line itself. Commented May 14, 2014 at 20:49
-
1@gene I think the following line is missing in your code: mylayer = qgis.utils.iface.activeLayer() Commented Apr 6, 2017 at 7:48
One way I would address this is to use a 2 step approach with the MMQGIS plugin. The first is to install and use the MMQGIS to find the middle point or center of each line.
Then by creating new columns, I can use the longitude and latitude functions from the field calculator to get the coordinates accordingly.
-
I'm using QGIS 3.22 and MMQGIS plugin but I can't see the option to get the midpoint. Need help.– davidCommented Oct 13, 2022 at 15:33
In PyQGIS 3 there are several handy methods available, particularly for:
Straight Lines
Since QGIS 3.0 there is midpoint()
method from the QgsGeometryUtils
class.
Returns a middle point between points pt1 and pt2.
Z value is computed if one of this point have Z. M value is computed if one of this point have M.
# imports
from qgis.core import QgsProject, QgsGeometryUtils
# referring to the original line layer
line_layer = QgsProject.instance().mapLayersByName("lines")[0]
# getting geometry of one single line feature
line_geom = line_layer.getFeature(5).geometry()
# extracting line first and last point
first_vertex = line_geom.vertexAt(0)
last_vertex = line_geom.vertexAt(1)
# finding the midpoint
midpt = QgsGeometryUtils.midpoint(first_vertex, last_vertex)
print(midpt)
it will lead to the following output:
<QgsPoint: Point (6362964.60043694637715816 6156695.3789755143225193)>
The same result can be achieved with the pointOnLineWithDistance()
method.
Returns a point a specified distance toward a second point.
midpt = QgsGeometryUtils.pointOnLineWithDistance(first_vertex, last_vertex, line_geom.length()/2)
Curved Lines
Since QGIS 3.4 there is interpolatePoint()
method of the QgsLineString
class.
Returns an interpolated point on the curve at the specified distance.
If z or m values are present, the output z and m will be interpolated using the existing vertices' z or m values.
If distance is negative, or is greater than the length of the curve, nullptr will be returned.
# imports
from qgis.core import QgsProject, QgsLineString
# referring to the original line layer
line_layer = QgsProject.instance().mapLayersByName("lines")[0]
# getting geometry of one single line feature
line_geom = line_layer.getFeature(4).geometry()
# converting this line feature to a QgsLineString
line_string = QgsLineString(line_geom.asPolyline())
# finding the midpoint
midpt = line_string.interpolatePoint(line_string.length()/2)
print(midpt)
it will lead to the following output:
<QgsPoint: Point (6363044.65252993814647198 6156648.31500245165079832)>
Curved Multi-part Lines
Since QGIS 3.0 there are childCount()
Returns number of child geometries (for geometries with child geometries) or child points (for geometries without child geometries - i.e. curve / point)
and childGeometry()
methods from the QgsAbstractGeometry
class.
Returns pointer to child geometry (for geometries with child geometries - i.e. geom. collection / polygon)
# imports
from qgis.core import QgsProject, QgsLineString, QgsMultiLineString
# referring to the original line layer
line_layer = QgsProject.instance().mapLayersByName("lines")[0]
# getting geometry of one single line feature
line_geom = line_layer.getFeature(3).geometry()
# converting this multi-line feature to a QgsMultiLineString
multi_line_string = QgsMultiLineString()
for line in line_geom.asMultiPolyline():
multi_line_string.addGeometry(QgsLineString(line))
# finding the midpoint for each part
for part in range(multi_line_string.childCount()):
part_geom = multi_line_string.childGeometry(part)
midpt = part_geom.interpolatePoint(part_geom.length()/2)
print(part, midpt)
it will lead to the following output:
0 <QgsPoint: Point (6362897.59699867479503155 6156682.89629040285944939)>
1 <QgsPoint: Point (6363044.65252993814647198 6156648.31500245165079832)>
References:
In QGIS 3.12 I found the Vector->Geometry Tools->Centroids function to be the quickest/easiest way to do this. You can create a temporary or permanent feature of a single line selection or an entire line feature in just a couple of clicks. I do wish that a Snap-To-Midpoint button is added to QGIS Snapping Toolbar soon though!
To get midpoints close to half the length of line objects, the Centroids function in QGIS 3.10.10 doesn't for sure create the midpoint on the line if the line object is not strait. So instead I buffered the road centre lines 5 meter, used Centroids on the polygons created from the buffered lines and got points at very good positions.
QgsMapLayerRegistry.instance().addMapLayer(temp)
toQgsProject.instance().addMapLayer(temp)