[{"data":1,"prerenderedAt":1774},["ShallowReactive",2],{"doc:\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fcalculate-distance-between-features-pyqgis":3},{"id":4,"title":5,"body":6,"description":1763,"extension":1764,"meta":1765,"navigation":251,"path":1770,"seo":1771,"stem":1772,"__hash__":1773},"docs\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fcalculate-distance-between-features-pyqgis\u002Findex.md","Calculate the Distance Between Features in PyQGIS",{"type":7,"value":8,"toc":1751},"minimark",[9,13,31,40,175,180,210,214,219,382,388,399,452,467,471,477,583,609,612,616,623,793,1015,1038,1041,1158,1178,1182,1185,1266,1478,1494,1498,1504,1570,1581,1585,1641,1645,1654,1658,1671,1683,1692,1698,1710,1720,1724,1747],[10,11,5],"h1",{"id":12},"calculate-the-distance-between-features-in-pyqgis",[14,15,16,17,21,22,25,26,30],"p",{},"\"How far apart are these?\" has three different correct answers depending on what you mean and where the data is. ",[18,19,20],"code",{},"QgsGeometry.distance()"," measures in map units on a flat plane. ",[18,23,24],{},"QgsDistanceArea"," measures on the ellipsoid in real metres. And the distance between two ",[27,28,29],"em",{},"features"," is the shortest distance between any pair of their points, which for polygons is zero as soon as they touch — not the distance between their centres.",[14,32,33,34,39],{},"This page is a focused recipe within ",[35,36,38],"a",{"href":37},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002F","Geometry Operations and Spatial Predicates in PyQGIS",". It covers the planar and ellipsoidal measurements, what \"distance between features\" actually returns for lines and polygons, and how to find nearest neighbours without a quadratic scan.",[14,41,42],{},[43,44,49,53,57,64,73,122,151],"svg",{"viewBox":45,"role":46,"ariaLabel":47,"xmlns":48},"0 0 760 254","img","Three distance interpretations between the same pair of polygons: centroid to centroid, edge to edge, and zero when they touch","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[50,51,52],"title",{},"Which distance does the question mean?",[54,55,56],"desc",{},"The same two polygons are shown three times. In the first, a line joins their centroids, giving the longest of the three values. In the second, a shorter line joins their nearest edges, which is what QgsGeometry.distance returns. In the third the polygons share a boundary and the distance is zero even though their centroids are far apart.",[58,59],"rect",{"x":60,"y":60,"width":61,"height":62,"fill":63},"0","760","254","#f6f3ea",[65,66,72],"text",{"x":67,"y":68,"style":69,"fill":70,"textAnchor":71},"380","26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","distance() is edge to edge — not centre to centre",[74,75,76,86,92,99,103,109,113,116],"g",{},[58,77],{"x":78,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":84,"style":85},"12","44","236","192","10","#fffdf7","#b45309","stroke-width:2.5",[65,87,91],{"x":88,"y":89,"style":90,"fill":84,"textAnchor":71},"130","68","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","centroid to centroid",[93,94],"path",{"d":95,"fill":96,"fillOpacity":97,"stroke":96,"style":98},"M36 172 L36 96 L104 96 L104 172 Z","#2563eb",0.16,"stroke-width:2",[93,100],{"d":101,"fill":102,"fillOpacity":97,"stroke":102,"style":98},"M164 190 L164 118 L228 118 L228 190 Z","#0f766e",[104,105],"circle",{"cx":106,"cy":107,"r":108,"fill":84},"70","134","5",[104,110],{"cx":111,"cy":112,"r":108,"fill":84},"196","154",[114,115],"line",{"x1":106,"y1":107,"x2":111,"y2":112,"stroke":84,"style":85},[65,117,121],{"x":88,"y":118,"style":119,"fill":120,"textAnchor":71},"222","text-anchor:middle;font-size:11px;font-family:sans-serif","#2f3b35","you must compute this yourself",[74,123,124,128,131,134,137,143,146,148],{},[58,125],{"x":126,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":127,"style":85},"262","#15803d",[65,129,130],{"x":67,"y":89,"style":90,"fill":127,"textAnchor":71},"nearest edges",[93,132],{"d":133,"fill":96,"fillOpacity":97,"stroke":96,"style":98},"M286 172 L286 96 L354 96 L354 172 Z",[93,135],{"d":136,"fill":102,"fillOpacity":97,"stroke":102,"style":98},"M414 190 L414 118 L478 118 L478 190 Z",[114,138],{"x1":139,"y1":140,"x2":141,"y2":140,"stroke":127,"style":142},"354","145","414","stroke-width:3",[104,144],{"cx":139,"cy":140,"r":145,"fill":127},"4",[104,147],{"cx":141,"cy":140,"r":145,"fill":127},[65,149,150],{"x":67,"y":118,"style":119,"fill":120,"textAnchor":71},"what distance() returns",[74,152,153,156,160,163,166,172],{},[58,154],{"x":155,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":96,"style":85},"512",[65,157,159],{"x":158,"y":89,"style":90,"fill":96,"textAnchor":71},"630","touching",[93,161],{"d":162,"fill":96,"fillOpacity":97,"stroke":96,"style":98},"M540 172 L540 96 L620 96 L620 172 Z",[93,164],{"d":165,"fill":102,"fillOpacity":97,"stroke":102,"style":98},"M620 190 L620 118 L712 118 L712 190 Z",[114,167],{"x1":168,"y1":169,"x2":168,"y2":170,"stroke":84,"style":171},"620","118","172","stroke-width:4",[65,173,174],{"x":158,"y":118,"style":119,"fill":96,"textAnchor":71},"distance() returns 0.0",[176,177,179],"h2",{"id":178},"prerequisites","Prerequisites",[181,182,183,191,202],"ul",{},[184,185,186,190],"li",{},[187,188,189],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[184,192,193,194,197,198,201],{},"Both layers ",[187,195,196],{},"in the same CRS",". ",[18,199,200],{},"distance()"," compares raw coordinates and does not transform.",[184,203,204,205,209],{},"A projected CRS if you want the planar answer to mean metres — see ",[35,206,208],{"href":207},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002F","Coordinate Reference Systems in PyQGIS",".",[176,211,213],{"id":212},"the-planar-measurement","The planar measurement",[14,215,216,218],{},[18,217,200],{}," is the shortest distance between any point of one geometry and any point of the other, expressed in map units.",[220,221,226],"pre",{"className":222,"code":223,"language":224,"meta":225,"style":225},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject\n\nschools = QgsProject.instance().mapLayersByName(\"schools\")[0]\nparks = QgsProject.instance().mapLayersByName(\"parks\")[0]\n\nschool = next(schools.getFeatures()).geometry()\npark = next(parks.getFeatures()).geometry()\n\nprint(round(school.distance(park), 1), \"map units\")\nprint(round(school.centroid().distance(park.centroid()), 1), \"centroid to centroid\")\n","python","",[18,227,228,246,253,278,297,302,316,329,334,361],{"__ignoreMap":225},[229,230,232,236,240,243],"span",{"class":114,"line":231},1,[229,233,235],{"class":234},"snl16","from",[229,237,239],{"class":238},"s95oV"," qgis.core ",[229,241,242],{"class":234},"import",[229,244,245],{"class":238}," QgsProject\n",[229,247,249],{"class":114,"line":248},2,[229,250,252],{"emptyLinePlaceholder":251},true,"\n",[229,254,256,259,262,265,269,272,275],{"class":114,"line":255},3,[229,257,258],{"class":238},"schools ",[229,260,261],{"class":234},"=",[229,263,264],{"class":238}," QgsProject.instance().mapLayersByName(",[229,266,268],{"class":267},"sU2Wk","\"schools\"",[229,270,271],{"class":238},")[",[229,273,60],{"class":274},"sDLfK",[229,276,277],{"class":238},"]\n",[229,279,281,284,286,288,291,293,295],{"class":114,"line":280},4,[229,282,283],{"class":238},"parks ",[229,285,261],{"class":234},[229,287,264],{"class":238},[229,289,290],{"class":267},"\"parks\"",[229,292,271],{"class":238},[229,294,60],{"class":274},[229,296,277],{"class":238},[229,298,300],{"class":114,"line":299},5,[229,301,252],{"emptyLinePlaceholder":251},[229,303,305,308,310,313],{"class":114,"line":304},6,[229,306,307],{"class":238},"school ",[229,309,261],{"class":234},[229,311,312],{"class":274}," next",[229,314,315],{"class":238},"(schools.getFeatures()).geometry()\n",[229,317,319,322,324,326],{"class":114,"line":318},7,[229,320,321],{"class":238},"park ",[229,323,261],{"class":234},[229,325,312],{"class":274},[229,327,328],{"class":238},"(parks.getFeatures()).geometry()\n",[229,330,332],{"class":114,"line":331},8,[229,333,252],{"emptyLinePlaceholder":251},[229,335,337,340,343,346,349,352,355,358],{"class":114,"line":336},9,[229,338,339],{"class":274},"print",[229,341,342],{"class":238},"(",[229,344,345],{"class":274},"round",[229,347,348],{"class":238},"(school.distance(park), ",[229,350,351],{"class":274},"1",[229,353,354],{"class":238},"), ",[229,356,357],{"class":267},"\"map units\"",[229,359,360],{"class":238},")\n",[229,362,364,366,368,370,373,375,377,380],{"class":114,"line":363},10,[229,365,339],{"class":274},[229,367,342],{"class":238},[229,369,345],{"class":274},[229,371,372],{"class":238},"(school.centroid().distance(park.centroid()), ",[229,374,351],{"class":274},[229,376,354],{"class":238},[229,378,379],{"class":267},"\"centroid to centroid\"",[229,381,360],{"class":238},[14,383,384,387],{},[187,385,386],{},"Breakdown:"," For two points this is ordinary Euclidean distance. For a point and a polygon it is the distance to the nearest boundary point, and zero if the point is inside. For two polygons that touch or overlap it is exactly zero. If what you meant was centre to centre, take the centroids first — the second line shows how, and the two values are usually very different.",[14,389,390,391,394,395,398],{},"To know ",[27,392,393],{},"where"," the closest approach is, ",[18,396,397],{},"shortestLine()"," returns the connecting segment, which is useful both for measurement and for drawing:",[220,400,402],{"className":222,"code":401,"language":224,"meta":225,"style":225},"connector = school.shortestLine(park)\nprint(connector.asWkt(precision=1))\nprint(round(connector.length(), 1), \"same value as distance()\")\n",[18,403,404,414,432],{"__ignoreMap":225},[229,405,406,409,411],{"class":114,"line":231},[229,407,408],{"class":238},"connector ",[229,410,261],{"class":234},[229,412,413],{"class":238}," school.shortestLine(park)\n",[229,415,416,418,421,425,427,429],{"class":114,"line":248},[229,417,339],{"class":274},[229,419,420],{"class":238},"(connector.asWkt(",[229,422,424],{"class":423},"s9osk","precision",[229,426,261],{"class":234},[229,428,351],{"class":274},[229,430,431],{"class":238},"))\n",[229,433,434,436,438,440,443,445,447,450],{"class":114,"line":255},[229,435,339],{"class":274},[229,437,342],{"class":238},[229,439,345],{"class":274},[229,441,442],{"class":238},"(connector.length(), ",[229,444,351],{"class":274},[229,446,354],{"class":238},[229,448,449],{"class":267},"\"same value as distance()\"",[229,451,360],{"class":238},[14,453,454,456,457,459,460,463,464,466],{},[187,455,386],{}," ",[18,458,397],{}," returns a two-point ",[18,461,462],{},"QgsGeometry"," whose length equals ",[18,465,200],{},". Adding it to a memory layer makes the measurement visible on the map, which is how the built-in measure tool presents its result.",[176,468,470],{"id":469},"the-ellipsoidal-measurement","The ellipsoidal measurement",[14,472,473,474,476],{},"Planar distance is only meaningful in a projected CRS, and even there it ignores the curvature of the Earth over long spans. ",[18,475,24],{}," does the real calculation.",[220,478,480],{"className":222,"code":479,"language":224,"meta":225,"style":225},"from qgis.core import QgsDistanceArea, QgsProject, QgsUnitTypes\n\ncalculator = QgsDistanceArea()\ncalculator.setSourceCrs(schools.crs(), QgsProject.instance().transformContext())\ncalculator.setEllipsoid(QgsProject.instance().ellipsoid())\n\nmetres = calculator.measureLine(school.asPoint(), park.centroid().asPoint())\nkm = calculator.convertLengthMeasurement(metres, QgsUnitTypes.DistanceUnit.DistanceKilometers)\nprint(f\"{metres:,.1f} m  ({km:.3f} km)\")\n",[18,481,482,493,497,507,512,517,521,531,541],{"__ignoreMap":225},[229,483,484,486,488,490],{"class":114,"line":231},[229,485,235],{"class":234},[229,487,239],{"class":238},[229,489,242],{"class":234},[229,491,492],{"class":238}," QgsDistanceArea, QgsProject, QgsUnitTypes\n",[229,494,495],{"class":114,"line":248},[229,496,252],{"emptyLinePlaceholder":251},[229,498,499,502,504],{"class":114,"line":255},[229,500,501],{"class":238},"calculator ",[229,503,261],{"class":234},[229,505,506],{"class":238}," QgsDistanceArea()\n",[229,508,509],{"class":114,"line":280},[229,510,511],{"class":238},"calculator.setSourceCrs(schools.crs(), QgsProject.instance().transformContext())\n",[229,513,514],{"class":114,"line":299},[229,515,516],{"class":238},"calculator.setEllipsoid(QgsProject.instance().ellipsoid())\n",[229,518,519],{"class":114,"line":304},[229,520,252],{"emptyLinePlaceholder":251},[229,522,523,526,528],{"class":114,"line":318},[229,524,525],{"class":238},"metres ",[229,527,261],{"class":234},[229,529,530],{"class":238}," calculator.measureLine(school.asPoint(), park.centroid().asPoint())\n",[229,532,533,536,538],{"class":114,"line":331},[229,534,535],{"class":238},"km ",[229,537,261],{"class":234},[229,539,540],{"class":238}," calculator.convertLengthMeasurement(metres, QgsUnitTypes.DistanceUnit.DistanceKilometers)\n",[229,542,543,545,547,550,553,556,559,562,565,568,570,573,576,578,581],{"class":114,"line":336},[229,544,339],{"class":274},[229,546,342],{"class":238},[229,548,549],{"class":234},"f",[229,551,552],{"class":267},"\"",[229,554,555],{"class":274},"{",[229,557,558],{"class":238},"metres",[229,560,561],{"class":234},":,.1f",[229,563,564],{"class":274},"}",[229,566,567],{"class":267}," m  (",[229,569,555],{"class":274},[229,571,572],{"class":238},"km",[229,574,575],{"class":234},":.3f",[229,577,564],{"class":274},[229,579,580],{"class":267}," km)\"",[229,582,360],{"class":238},[14,584,585,456,587,590,591,594,595,197,597,600,601,604,605,608],{},[187,586,386],{},[18,588,589],{},"setSourceCrs()"," tells the calculator what the incoming coordinates mean, and ",[18,592,593],{},"setEllipsoid()"," is what switches it from planar to ellipsoidal arithmetic — without that second call it silently returns the same planar number as ",[18,596,200],{},[18,598,599],{},"measureLine()"," takes ",[18,602,603],{},"QgsPointXY"," objects and always returns metres regardless of the source CRS, which is exactly what makes it safe to use on lat\u002Flon data. ",[18,606,607],{},"convertLengthMeasurement()"," handles unit conversion so no magic constants appear in your code.",[14,610,611],{},"The difference matters at scale. Over a few hundred metres in a well-chosen UTM zone, planar and ellipsoidal agree to within centimetres. Over hundreds of kilometres, or near the edge of a projection's valid band, the divergence becomes metres and then tens of metres.",[176,613,615],{"id":614},"find-the-nearest-feature","Find the nearest feature",[14,617,618,619,622],{},"The naive nearest-neighbour search compares every pair. A ",[18,620,621],{},"QgsSpatialIndex"," answers it directly, because nearest-neighbour is one of the queries an R-tree is built for.",[14,624,625],{},[43,626,629,632,635,638,641,646,650,691,722,725,729,732,736,766,779,788,790],{"viewBox":627,"role":46,"ariaLabel":628,"xmlns":48},"0 0 760 246","Nearest neighbour by brute force compared with a spatial index query, where the index returns the three closest candidate identifiers directly",[50,630,631],{},"Brute-force scan versus nearestNeighbor()",[54,633,634],{},"On the left, a query point is joined by lines to all fourteen candidate features, each requiring a distance calculation. On the right, the spatial index returns three nearest candidate identifiers immediately, and only those three are measured exactly to pick the winner.",[58,636],{"x":60,"y":60,"width":61,"height":637,"fill":63},"246",[65,639,640],{"x":67,"y":68,"style":69,"fill":70,"textAnchor":71},"The index narrows the field before any distance is computed",[58,642],{"x":643,"y":79,"width":644,"height":645,"rx":82,"fill":83,"stroke":84,"style":85},"16","356","188",[65,647,649],{"x":648,"y":89,"style":90,"fill":84,"textAnchor":71},"194","every pair measured",[74,651,654,659,662,665,668,671,674,676,678,680,682,685,687,689],{"stroke":84,"style":652,"opacity":653},"stroke-width:1","0.55",[114,655],{"x1":648,"y1":656,"x2":657,"y2":658},"150","62","96",[114,660],{"x1":648,"y1":656,"x2":661,"y2":658},"112",[114,663],{"x1":648,"y1":656,"x2":664,"y2":658},"162",[114,666],{"x1":648,"y1":656,"x2":667,"y2":658},"230",[114,669],{"x1":648,"y1":656,"x2":670,"y2":658},"280",[114,672],{"x1":648,"y1":656,"x2":673,"y2":658},"330",[114,675],{"x1":648,"y1":656,"x2":657,"y2":656},[114,677],{"x1":648,"y1":656,"x2":661,"y2":656},[114,679],{"x1":648,"y1":656,"x2":670,"y2":656},[114,681],{"x1":648,"y1":656,"x2":673,"y2":656},[114,683],{"x1":648,"y1":656,"x2":657,"y2":684},"204",[114,686],{"x1":648,"y1":656,"x2":88,"y2":684},[114,688],{"x1":648,"y1":656,"x2":126,"y2":684},[114,690],{"x1":648,"y1":656,"x2":673,"y2":684},[74,692,694,696,698,700,702,704,706,708,710,712,714,716,718,720],{"fill":693},"#59645f",[104,695],{"cx":657,"cy":658,"r":108},[104,697],{"cx":661,"cy":658,"r":108},[104,699],{"cx":664,"cy":658,"r":108},[104,701],{"cx":667,"cy":658,"r":108},[104,703],{"cx":670,"cy":658,"r":108},[104,705],{"cx":673,"cy":658,"r":108},[104,707],{"cx":657,"cy":656,"r":108},[104,709],{"cx":661,"cy":656,"r":108},[104,711],{"cx":670,"cy":656,"r":108},[104,713],{"cx":673,"cy":656,"r":108},[104,715],{"cx":657,"cy":684,"r":108},[104,717],{"cx":88,"cy":684,"r":108},[104,719],{"cx":126,"cy":684,"r":108},[104,721],{"cx":673,"cy":684,"r":108},[104,723],{"cx":648,"cy":656,"r":724,"fill":96},"8",[65,726,728],{"x":648,"y":727,"style":119,"fill":120,"textAnchor":71},"224","14 distance calculations",[58,730],{"x":731,"y":79,"width":644,"height":645,"rx":82,"fill":83,"stroke":127,"style":85},"388",[65,733,735],{"x":734,"y":89,"style":90,"fill":127,"textAnchor":71},"566","index.nearestNeighbor(pt, 3)",[74,737,738,741,744,747,750,753,755,757,759,761,764],{"fill":693},[104,739],{"cx":740,"cy":658,"r":108},"434",[104,742],{"cx":743,"cy":658,"r":108},"484",[104,745],{"cx":746,"cy":658,"r":108},"602",[104,748],{"cx":749,"cy":658,"r":108},"652",[104,751],{"cx":752,"cy":658,"r":108},"702",[104,754],{"cx":740,"cy":656,"r":108},[104,756],{"cx":743,"cy":656,"r":108},[104,758],{"cx":752,"cy":656,"r":108},[104,760],{"cx":740,"cy":684,"r":108},[104,762],{"cx":763,"cy":684,"r":108},"502",[104,765],{"cx":752,"cy":684,"r":108},[74,767,768,773,775],{"fill":127},[104,769],{"cx":770,"cy":771,"r":772},"534","122","6",[104,774],{"cx":746,"cy":656,"r":772},[104,776],{"cx":777,"cy":778,"r":772},"546","186",[74,780,782,784,786],{"stroke":127,"style":781},"stroke-width:1.8",[114,783],{"x1":734,"y1":656,"x2":770,"y2":771},[114,785],{"x1":734,"y1":656,"x2":746,"y2":656},[114,787],{"x1":734,"y1":656,"x2":777,"y2":778},[104,789],{"cx":734,"cy":656,"r":724,"fill":96},[65,791,792],{"x":734,"y":727,"style":119,"fill":120,"textAnchor":71},"3 distance calculations",[220,794,796],{"className":222,"code":795,"language":224,"meta":225,"style":225},"from qgis.core import QgsSpatialIndex, QgsProject\n\nparks = QgsProject.instance().mapLayersByName(\"parks\")[0]\nschools = QgsProject.instance().mapLayersByName(\"schools\")[0]\n\nindex = QgsSpatialIndex(parks.getFeatures(), flags=QgsSpatialIndex.FlagStoreFeatureGeometries)\n\nfor school in schools.getFeatures():\n    geometry = school.geometry()\n    candidates = index.nearestNeighbor(geometry, 3)\n    best_id, best_distance = None, float(\"inf\")\n    for park_id in candidates:\n        d = geometry.distance(index.geometry(park_id))\n        if d \u003C best_distance:\n            best_id, best_distance = park_id, d\n    print(school[\"name\"], \"->\", best_id, round(best_distance, 1))\n",[18,797,798,809,813,829,845,849,867,871,885,895,910,934,948,959,974,985],{"__ignoreMap":225},[229,799,800,802,804,806],{"class":114,"line":231},[229,801,235],{"class":234},[229,803,239],{"class":238},[229,805,242],{"class":234},[229,807,808],{"class":238}," QgsSpatialIndex, QgsProject\n",[229,810,811],{"class":114,"line":248},[229,812,252],{"emptyLinePlaceholder":251},[229,814,815,817,819,821,823,825,827],{"class":114,"line":255},[229,816,283],{"class":238},[229,818,261],{"class":234},[229,820,264],{"class":238},[229,822,290],{"class":267},[229,824,271],{"class":238},[229,826,60],{"class":274},[229,828,277],{"class":238},[229,830,831,833,835,837,839,841,843],{"class":114,"line":280},[229,832,258],{"class":238},[229,834,261],{"class":234},[229,836,264],{"class":238},[229,838,268],{"class":267},[229,840,271],{"class":238},[229,842,60],{"class":274},[229,844,277],{"class":238},[229,846,847],{"class":114,"line":299},[229,848,252],{"emptyLinePlaceholder":251},[229,850,851,854,856,859,862,864],{"class":114,"line":304},[229,852,853],{"class":238},"index ",[229,855,261],{"class":234},[229,857,858],{"class":238}," QgsSpatialIndex(parks.getFeatures(), ",[229,860,861],{"class":423},"flags",[229,863,261],{"class":234},[229,865,866],{"class":238},"QgsSpatialIndex.FlagStoreFeatureGeometries)\n",[229,868,869],{"class":114,"line":318},[229,870,252],{"emptyLinePlaceholder":251},[229,872,873,876,879,882],{"class":114,"line":331},[229,874,875],{"class":234},"for",[229,877,878],{"class":238}," school ",[229,880,881],{"class":234},"in",[229,883,884],{"class":238}," schools.getFeatures():\n",[229,886,887,890,892],{"class":114,"line":336},[229,888,889],{"class":238},"    geometry ",[229,891,261],{"class":234},[229,893,894],{"class":238}," school.geometry()\n",[229,896,897,900,902,905,908],{"class":114,"line":363},[229,898,899],{"class":238},"    candidates ",[229,901,261],{"class":234},[229,903,904],{"class":238}," index.nearestNeighbor(geometry, ",[229,906,907],{"class":274},"3",[229,909,360],{"class":238},[229,911,913,916,918,921,924,927,929,932],{"class":114,"line":912},11,[229,914,915],{"class":238},"    best_id, best_distance ",[229,917,261],{"class":234},[229,919,920],{"class":274}," None",[229,922,923],{"class":238},", ",[229,925,926],{"class":274},"float",[229,928,342],{"class":238},[229,930,931],{"class":267},"\"inf\"",[229,933,360],{"class":238},[229,935,937,940,943,945],{"class":114,"line":936},12,[229,938,939],{"class":234},"    for",[229,941,942],{"class":238}," park_id ",[229,944,881],{"class":234},[229,946,947],{"class":238}," candidates:\n",[229,949,951,954,956],{"class":114,"line":950},13,[229,952,953],{"class":238},"        d ",[229,955,261],{"class":234},[229,957,958],{"class":238}," geometry.distance(index.geometry(park_id))\n",[229,960,962,965,968,971],{"class":114,"line":961},14,[229,963,964],{"class":234},"        if",[229,966,967],{"class":238}," d ",[229,969,970],{"class":234},"\u003C",[229,972,973],{"class":238}," best_distance:\n",[229,975,977,980,982],{"class":114,"line":976},15,[229,978,979],{"class":238},"            best_id, best_distance ",[229,981,261],{"class":234},[229,983,984],{"class":238}," park_id, d\n",[229,986,988,991,994,997,1000,1003,1006,1008,1011,1013],{"class":114,"line":987},16,[229,989,990],{"class":274},"    print",[229,992,993],{"class":238},"(school[",[229,995,996],{"class":267},"\"name\"",[229,998,999],{"class":238},"], ",[229,1001,1002],{"class":267},"\"->\"",[229,1004,1005],{"class":238},", best_id, ",[229,1007,345],{"class":274},[229,1009,1010],{"class":238},"(best_distance, ",[229,1012,351],{"class":274},[229,1014,431],{"class":238},[14,1016,1017,456,1019,1022,1023,1026,1027,1030,1031,1034,1035,1037],{},[187,1018,386],{},[18,1020,1021],{},"FlagStoreFeatureGeometries"," makes the index keep the geometries so ",[18,1024,1025],{},"index.geometry(id)"," can return them without a second provider read — a large saving inside a loop, at the cost of memory. ",[18,1028,1029],{},"nearestNeighbor(geometry, 3)"," returns candidate IDs ranked by ",[27,1032,1033],{},"bounding-box"," proximity, which is an approximation, so the exact ",[18,1036,200],{}," still decides the winner. Asking for three rather than one is the guard against that approximation: the box-nearest feature is not always the truly nearest, particularly for long thin shapes.",[14,1039,1040],{},"For a whole-layer answer, the Processing algorithm does all of this and writes a joined output:",[220,1042,1044],{"className":222,"code":1043,"language":224,"meta":225,"style":225},"import processing\n\njoined = processing.run(\"native:joinbynearest\", {\n    \"INPUT\": \"\u002Fdata\u002Fschools.gpkg|layername=schools\",\n    \"INPUT_2\": \"\u002Fdata\u002Fparks.gpkg|layername=parks\",\n    \"FIELDS_TO_COPY\": [\"park_name\"],\n    \"NEIGHBORS\": 1,\n    \"MAX_DISTANCE\": 2000,\n    \"OUTPUT\": \"\u002Fdata\u002Foutput\u002Fschools_nearest_park.gpkg\",\n})[\"OUTPUT\"]\n",[18,1045,1046,1053,1057,1073,1087,1099,1113,1124,1136,1148],{"__ignoreMap":225},[229,1047,1048,1050],{"class":114,"line":231},[229,1049,242],{"class":234},[229,1051,1052],{"class":238}," processing\n",[229,1054,1055],{"class":114,"line":248},[229,1056,252],{"emptyLinePlaceholder":251},[229,1058,1059,1062,1064,1067,1070],{"class":114,"line":255},[229,1060,1061],{"class":238},"joined ",[229,1063,261],{"class":234},[229,1065,1066],{"class":238}," processing.run(",[229,1068,1069],{"class":267},"\"native:joinbynearest\"",[229,1071,1072],{"class":238},", {\n",[229,1074,1075,1078,1081,1084],{"class":114,"line":280},[229,1076,1077],{"class":267},"    \"INPUT\"",[229,1079,1080],{"class":238},": ",[229,1082,1083],{"class":267},"\"\u002Fdata\u002Fschools.gpkg|layername=schools\"",[229,1085,1086],{"class":238},",\n",[229,1088,1089,1092,1094,1097],{"class":114,"line":299},[229,1090,1091],{"class":267},"    \"INPUT_2\"",[229,1093,1080],{"class":238},[229,1095,1096],{"class":267},"\"\u002Fdata\u002Fparks.gpkg|layername=parks\"",[229,1098,1086],{"class":238},[229,1100,1101,1104,1107,1110],{"class":114,"line":304},[229,1102,1103],{"class":267},"    \"FIELDS_TO_COPY\"",[229,1105,1106],{"class":238},": [",[229,1108,1109],{"class":267},"\"park_name\"",[229,1111,1112],{"class":238},"],\n",[229,1114,1115,1118,1120,1122],{"class":114,"line":318},[229,1116,1117],{"class":267},"    \"NEIGHBORS\"",[229,1119,1080],{"class":238},[229,1121,351],{"class":274},[229,1123,1086],{"class":238},[229,1125,1126,1129,1131,1134],{"class":114,"line":331},[229,1127,1128],{"class":267},"    \"MAX_DISTANCE\"",[229,1130,1080],{"class":238},[229,1132,1133],{"class":274},"2000",[229,1135,1086],{"class":238},[229,1137,1138,1141,1143,1146],{"class":114,"line":336},[229,1139,1140],{"class":267},"    \"OUTPUT\"",[229,1142,1080],{"class":238},[229,1144,1145],{"class":267},"\"\u002Fdata\u002Foutput\u002Fschools_nearest_park.gpkg\"",[229,1147,1086],{"class":238},[229,1149,1150,1153,1156],{"class":114,"line":363},[229,1151,1152],{"class":238},"})[",[229,1154,1155],{"class":267},"\"OUTPUT\"",[229,1157,277],{"class":238},[14,1159,1160,456,1162,1165,1166,1169,1170,1173,1174,1177],{},[187,1161,386],{},[18,1163,1164],{},"native:joinbynearest"," adds a ",[18,1167,1168],{},"distance"," field to the output automatically, along with the copied attributes. ",[18,1171,1172],{},"MAX_DISTANCE"," caps the search, which both speeds it up and leaves genuinely isolated features unjoined rather than matched to something absurdly far away. ",[18,1175,1176],{},"NEIGHBORS"," above 1 produces one output feature per neighbour, which is how you build a \"three nearest\" table.",[176,1179,1181],{"id":1180},"measure-the-length-of-a-line","Measure the length of a line",[14,1183,1184],{},"Line length is the same question in one dimension, and it shows the planar-versus-ellipsoidal divergence more clearly than point distance because the error accumulates along every segment.",[14,1186,1187],{},[43,1188,1191,1194,1197,1200,1203,1207,1212,1217,1223,1229,1232,1235,1241,1244,1248,1251,1253,1256,1259,1262],{"viewBox":1189,"role":46,"ariaLabel":1190,"xmlns":48},"0 0 760 242","A short local line where planar and ellipsoidal lengths agree, and a long transcontinental line where the planar figure falls significantly short of the ellipsoidal one",[50,1192,1193],{},"Where planar length stops being good enough",[54,1195,1196],{},"Two rows. A two kilometre local line shows planar and ellipsoidal lengths agreeing to within a few centimetres. A twelve hundred kilometre route shows the planar figure reading several kilometres short of the ellipsoidal one, with the gap marked as accumulated error.",[58,1198],{"x":60,"y":60,"width":61,"height":1199,"fill":63},"242",[65,1201,1202],{"x":67,"y":68,"style":69,"fill":70,"textAnchor":71},"The error is proportional to the span, not the vertex count",[58,1204],{"x":643,"y":79,"width":1205,"height":1206,"rx":82,"fill":83,"stroke":127,"style":85},"728","86",[65,1208,1211],{"x":1209,"y":89,"style":1210,"fill":127},"40","font-size:12px;font-weight:bold;font-family:sans-serif","a 2 km survey line",[93,1213],{"d":1214,"fill":1215,"stroke":127,"style":1216},"M40 104 L104 92 L168 108 L232 96","none","stroke-width:3;stroke-linejoin:round",[65,1218,1222],{"x":1219,"y":1220,"style":1221,"fill":120},"300","90","font-size:11.5px;font-family:sans-serif","planar (distance \u002F length)",[65,1224,1228],{"x":1225,"y":1220,"style":1226,"fill":120,"textAnchor":1227},"560","text-anchor:end;font-size:11.5px;font-family:sans-serif","end","2 041.36 m",[65,1230,1231],{"x":1219,"y":661,"style":1221,"fill":120},"ellipsoidal (QgsDistanceArea)",[65,1233,1234],{"x":1225,"y":661,"style":1226,"fill":120,"textAnchor":1227},"2 041.41 m",[65,1236,1240],{"x":1237,"y":1238,"style":1239,"fill":127,"textAnchor":1227},"588","102","text-anchor:end;font-size:11.5px;font-weight:bold;font-family:sans-serif","5 cm apart — ignore it",[58,1242],{"x":643,"y":1243,"width":1205,"height":1206,"rx":82,"fill":83,"stroke":84,"style":85},"142",[65,1245,1247],{"x":1209,"y":1246,"style":1210,"fill":84},"166","a 1 200 km route",[93,1249],{"d":1250,"fill":1215,"stroke":84,"style":1216},"M40 202 L104 186 L168 206 L232 184",[65,1252,1222],{"x":1219,"y":645,"style":1221,"fill":120},[65,1254,1255],{"x":1225,"y":645,"style":1226,"fill":120,"textAnchor":1227},"1 194.2 km",[65,1257,1231],{"x":1219,"y":1258,"style":1221,"fill":120},"210",[65,1260,1261],{"x":1225,"y":1258,"style":1226,"fill":120,"textAnchor":1227},"1 201.7 km",[65,1263,1265],{"x":1237,"y":1264,"style":1239,"fill":84,"textAnchor":1227},"200","7.5 km short",[220,1267,1269],{"className":222,"code":1268,"language":224,"meta":225,"style":225},"from qgis.core import QgsDistanceArea, QgsProject, QgsUnitTypes\n\nroutes = QgsProject.instance().mapLayersByName(\"routes\")[0]\n\ncalculator = QgsDistanceArea()\ncalculator.setSourceCrs(routes.crs(), QgsProject.instance().transformContext())\ncalculator.setEllipsoid(QgsProject.instance().ellipsoid())\n\nfor feature in routes.getFeatures():\n    geometry = feature.geometry()\n    planar = geometry.length()\n    ellipsoidal = calculator.measureLength(geometry)\n    km = calculator.convertLengthMeasurement(\n        ellipsoidal, QgsUnitTypes.DistanceUnit.DistanceKilometers\n    )\n    drift = abs(ellipsoidal - planar) \u002F ellipsoidal if ellipsoidal else 0\n    print(f\"{feature['route_id']}: {km:.2f} km  (planar differs by {drift:.2%})\")\n",[18,1270,1271,1281,1285,1303,1307,1315,1320,1324,1328,1340,1349,1359,1369,1379,1384,1389,1425],{"__ignoreMap":225},[229,1272,1273,1275,1277,1279],{"class":114,"line":231},[229,1274,235],{"class":234},[229,1276,239],{"class":238},[229,1278,242],{"class":234},[229,1280,492],{"class":238},[229,1282,1283],{"class":114,"line":248},[229,1284,252],{"emptyLinePlaceholder":251},[229,1286,1287,1290,1292,1294,1297,1299,1301],{"class":114,"line":255},[229,1288,1289],{"class":238},"routes ",[229,1291,261],{"class":234},[229,1293,264],{"class":238},[229,1295,1296],{"class":267},"\"routes\"",[229,1298,271],{"class":238},[229,1300,60],{"class":274},[229,1302,277],{"class":238},[229,1304,1305],{"class":114,"line":280},[229,1306,252],{"emptyLinePlaceholder":251},[229,1308,1309,1311,1313],{"class":114,"line":299},[229,1310,501],{"class":238},[229,1312,261],{"class":234},[229,1314,506],{"class":238},[229,1316,1317],{"class":114,"line":304},[229,1318,1319],{"class":238},"calculator.setSourceCrs(routes.crs(), QgsProject.instance().transformContext())\n",[229,1321,1322],{"class":114,"line":318},[229,1323,516],{"class":238},[229,1325,1326],{"class":114,"line":331},[229,1327,252],{"emptyLinePlaceholder":251},[229,1329,1330,1332,1335,1337],{"class":114,"line":336},[229,1331,875],{"class":234},[229,1333,1334],{"class":238}," feature ",[229,1336,881],{"class":234},[229,1338,1339],{"class":238}," routes.getFeatures():\n",[229,1341,1342,1344,1346],{"class":114,"line":363},[229,1343,889],{"class":238},[229,1345,261],{"class":234},[229,1347,1348],{"class":238}," feature.geometry()\n",[229,1350,1351,1354,1356],{"class":114,"line":912},[229,1352,1353],{"class":238},"    planar ",[229,1355,261],{"class":234},[229,1357,1358],{"class":238}," geometry.length()\n",[229,1360,1361,1364,1366],{"class":114,"line":936},[229,1362,1363],{"class":238},"    ellipsoidal ",[229,1365,261],{"class":234},[229,1367,1368],{"class":238}," calculator.measureLength(geometry)\n",[229,1370,1371,1374,1376],{"class":114,"line":950},[229,1372,1373],{"class":238},"    km ",[229,1375,261],{"class":234},[229,1377,1378],{"class":238}," calculator.convertLengthMeasurement(\n",[229,1380,1381],{"class":114,"line":961},[229,1382,1383],{"class":238},"        ellipsoidal, QgsUnitTypes.DistanceUnit.DistanceKilometers\n",[229,1385,1386],{"class":114,"line":976},[229,1387,1388],{"class":238},"    )\n",[229,1390,1391,1394,1396,1399,1402,1405,1408,1411,1414,1417,1419,1422],{"class":114,"line":987},[229,1392,1393],{"class":238},"    drift ",[229,1395,261],{"class":234},[229,1397,1398],{"class":274}," abs",[229,1400,1401],{"class":238},"(ellipsoidal ",[229,1403,1404],{"class":234},"-",[229,1406,1407],{"class":238}," planar) ",[229,1409,1410],{"class":234},"\u002F",[229,1412,1413],{"class":238}," ellipsoidal ",[229,1415,1416],{"class":234},"if",[229,1418,1413],{"class":238},[229,1420,1421],{"class":234},"else",[229,1423,1424],{"class":274}," 0\n",[229,1426,1428,1430,1432,1434,1436,1438,1441,1444,1447,1449,1451,1453,1455,1458,1460,1463,1465,1468,1471,1473,1476],{"class":114,"line":1427},17,[229,1429,990],{"class":274},[229,1431,342],{"class":238},[229,1433,549],{"class":234},[229,1435,552],{"class":267},[229,1437,555],{"class":274},[229,1439,1440],{"class":238},"feature[",[229,1442,1443],{"class":267},"'route_id'",[229,1445,1446],{"class":238},"]",[229,1448,564],{"class":274},[229,1450,1080],{"class":267},[229,1452,555],{"class":274},[229,1454,572],{"class":238},[229,1456,1457],{"class":234},":.2f",[229,1459,564],{"class":274},[229,1461,1462],{"class":267}," km  (planar differs by ",[229,1464,555],{"class":274},[229,1466,1467],{"class":238},"drift",[229,1469,1470],{"class":234},":.2%",[229,1472,564],{"class":274},[229,1474,1475],{"class":267},")\"",[229,1477,360],{"class":238},[14,1479,1480,456,1482,1485,1486,1489,1490,1493],{},[187,1481,386],{},[18,1483,1484],{},"geometry.length()"," sums the straight-line distances between consecutive vertices in map units — fast, and correct only in a projected CRS over a modest span. ",[18,1487,1488],{},"measureLength()"," does the same walk on the ellipsoid and always returns metres. Reporting the relative drift rather than the absolute difference makes the number comparable across routes of different lengths, and gives you a threshold to act on: under about 0.1 % the planar figure is fine for most purposes, above 1 % it is misleading. Note this measures the length of the ",[27,1491,1492],{},"geometry as digitised"," — it is not a travel distance along a network, which needs a routing algorithm rather than a measurement.",[176,1495,1497],{"id":1496},"qgis-version-compatibility","QGIS version compatibility",[14,1499,1500,1501,1503],{},"The examples target ",[187,1502,189],{}," (Python 3.12).",[1505,1506,1507,1523],"table",{},[1508,1509,1510],"thead",{},[1511,1512,1513,1517,1520],"tr",{},[1514,1515,1516],"th",{},"QGIS version",[1514,1518,1519],{},"Python",[1514,1521,1522],{},"Notes",[1524,1525,1526,1542,1553],"tbody",{},[1511,1527,1528,1532,1535],{},[1529,1530,1531],"td",{},"3.28 LTR",[1529,1533,1534],{},"3.9",[1529,1536,1537,1538,1541],{},"Identical API. ",[18,1539,1540],{},"QgsSpatialIndex.FlagStoreFeatureGeometries"," available since 3.4.",[1511,1543,1544,1547,1550],{},[1529,1545,1546],{},"3.34 LTR",[1529,1548,1549],{},"3.12",[1529,1551,1552],{},"Baseline for this page.",[1511,1554,1555,1558,1560],{},[1529,1556,1557],{},"3.40 \u002F 3.44",[1529,1559,1549],{},[1529,1561,1562,1565,1566,1569],{},[18,1563,1564],{},"QgsUnitTypes.DistanceUnit"," moved under ",[18,1567,1568],{},"Qgis","; the old spelling still resolves.",[14,1571,1572,1574,1575,1577,1578,1580],{},[18,1573,24],{}," and the ",[18,1576,200],{}," \u002F ",[18,1579,397],{}," methods are unchanged across 3.x.",[176,1582,1584],{"id":1583},"troubleshooting","Troubleshooting",[181,1586,1587,1596,1611,1617,1623,1633],{},[184,1588,1589,1592,1593,1595],{},[187,1590,1591],{},"Distances look like small decimals."," The layer is in a geographic CRS, so the answer is in degrees. Reproject, or switch to ",[18,1594,24],{}," with an ellipsoid set.",[184,1597,1598,456,1605,1607,1608,209],{},[187,1599,1600,1602,1603,209],{},[18,1601,24],{}," returns the same number as ",[18,1604,200],{},[18,1606,593],{}," was never called, so it stayed in planar mode. Pass ",[18,1609,1610],{},"QgsProject.instance().ellipsoid()",[184,1612,1613,1616],{},[187,1614,1615],{},"Two adjacent polygons report a distance of zero."," That is correct — they touch. Use centroids if you wanted centre-to-centre separation.",[184,1618,1619,1622],{},[187,1620,1621],{},"The nearest neighbour is obviously wrong."," Only one candidate was requested from the index, and the bounding-box ranking picked a long thin feature. Ask for three to five and measure them exactly.",[184,1624,1625,1628,1629,1632],{},[187,1626,1627],{},"Everything is far apart."," The two layers are in different CRSs. Compare ",[18,1630,1631],{},"crs().authid()"," on both before measuring.",[184,1634,1635,456,1638,1640],{},[187,1636,1637],{},"A nearest join left rows unmatched.",[18,1639,1172],{}," excluded them, which is usually intentional. Raise it or remove it if every feature must match.",[176,1642,1644],{"id":1643},"conclusion","Conclusion",[14,1646,1647,1648,1650,1651,1653],{},"Pick the measurement that matches the question: ",[18,1649,200],{}," for planar edge-to-edge in map units, ",[18,1652,24],{}," with an ellipsoid for real-world metres, and centroids only when you explicitly want centre-to-centre. For nearest-neighbour work, let a spatial index shortlist the candidates and confirm the winner with an exact measurement.",[176,1655,1657],{"id":1656},"frequently-asked-questions","Frequently Asked Questions",[14,1659,1660,1663,1664,1666,1667,1670],{},[187,1661,1662],{},"Why is the distance between two adjacent parcels zero?","\nBecause ",[18,1665,200],{}," measures the shortest gap between the geometries, and two polygons sharing a boundary have no gap. If you want the separation of their centres, measure ",[18,1668,1669],{},"a.centroid().distance(b.centroid())"," instead.",[14,1672,1673,1676,1677,1679,1680,1682],{},[187,1674,1675],{},"When do I need QgsDistanceArea rather than distance()?","\nWhenever the data is in a geographic CRS, or when the span is large enough that the curvature of the Earth matters. ",[18,1678,200],{}," is planar and returns map units; ",[18,1681,24],{}," with an ellipsoid returns real metres.",[14,1684,1685,1688,1689,1691],{},[187,1686,1687],{},"Does nearestNeighbor give the truly nearest feature?","\nNot guaranteed. It ranks candidates by bounding-box proximity, which can mis-order long or irregular shapes. Request several candidates and compare them with an exact ",[18,1690,200],{}," call.",[14,1693,1694,1697],{},[187,1695,1696],{},"Does distance() work between different geometry types?","\nYes. Point to polygon, line to point and polygon to polygon all work, and each returns the shortest gap between any point of one and any point of the other — zero if they touch or overlap.",[14,1699,1700,1703,1706,1707,1709],{},[187,1701,1702],{},"How do I see where the closest approach is?",[18,1704,1705],{},"a.shortestLine(b)"," returns the connecting two-point geometry. Its length equals ",[18,1708,200],{},", and adding it to a memory layer draws the measurement on the map.",[14,1711,1712,1715,1716,1719],{},[187,1713,1714],{},"Can I measure distance along a line rather than straight through?","\nYes — ",[18,1717,1718],{},"lineLocatePoint()"," gives the distance along a line to the nearest point on it, which is what linear referencing needs. Straight-line distance ignores the route entirely.",[176,1721,1723],{"id":1722},"related","Related",[181,1725,1726,1731,1737,1743],{},[184,1727,1728,1730],{},[35,1729,38],{"href":37}," — the guide this recipe belongs to",[184,1732,1733],{},[35,1734,1736],{"href":1735},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fbuild-spatial-index-pyqgis\u002F","Build and Use a Spatial Index in PyQGIS",[184,1738,1739],{},[35,1740,1742],{"href":1741},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fcheck-geometry-intersects-pyqgis\u002F","Test Whether Two Geometries Intersect in PyQGIS",[184,1744,1745],{},[35,1746,208],{"href":207},[1748,1749,1750],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":225,"searchDepth":248,"depth":248,"links":1752},[1753,1754,1755,1756,1757,1758,1759,1760,1761,1762],{"id":178,"depth":248,"text":179},{"id":212,"depth":248,"text":213},{"id":469,"depth":248,"text":470},{"id":614,"depth":248,"text":615},{"id":1180,"depth":248,"text":1181},{"id":1496,"depth":248,"text":1497},{"id":1583,"depth":248,"text":1584},{"id":1643,"depth":248,"text":1644},{"id":1656,"depth":248,"text":1657},{"id":1722,"depth":248,"text":1723},"Measure distance between features in PyQGIS with QgsGeometry.distance and QgsDistanceArea, choose planar or ellipsoidal, and find the nearest neighbour efficiently.","md",{"slug":1766,"type":1767,"breadcrumb":1768,"datePublished":1769,"dateModified":1769},"calculate-distance-between-features-pyqgis","article","Distance Between Features","2026-08-01","\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fcalculate-distance-between-features-pyqgis",{"title":5,"description":1763},"spatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Fcalculate-distance-between-features-pyqgis\u002Findex","E4Ix5Z2Kj5h5v9gmTa_RpEn8yMhkDuOzIiZjELrqmZ0",1785585990477]