ポイントレイヤーとラインレイヤーの最近傍 [閉まっている]


37

#qgisと#postgisの間のstackoverflowとircでこの質問を数回しました。また、実際の答えなしにそれをpostgisにコーディングまたは実装しようとしました。

プログラミング(できればpython)を使用して、ポイントレイヤーから、ラインレイヤーまたはポリゴンレイヤーの最も近いラインに投影するまで、ラインを描画します。

現在のところ、私のデータのほとんどはESRIの形状とpostgis形式です。ただし、私は主にshp + qgisユーザーなので、postgisソリューションには近づかない方がいいでしょう。

理想的な解決策は、Pythonまたは同様のライブラリでGDAL / OGRを実装することです

  • GDAL / OGRライブラリを使用して、どこから始めるべきですか?ソリューションプランを提供することは可能でしょうか?
  • NetworkXを使用して最近傍分析を実行できますか?
  • これは実際に可能ですか?

簡単な場合、ポイントは投影ポイントではなくセグメントのエンドポイントに接続できます


線分を線分に直交するように制限できますか?
ウルフオドラデ

@wolfOdrade-全体的には、問題ではありません。
dassouki

回答:


33

この質問は、私が正しいと思ったよりも少し難しいことが判明しました。Shapelyが提供する距離など、最短距離自体には多くの実装があります(GEOSから)。ただし、交差点自体を提供するソリューションはほとんどありませんが、距離のみが提供されます。

私の最初の試みは、ポイントとポリゴンの間の距離でポイントをバッファリングし、交差を探しましたが、丸めエラーにより、正確な答えが得られませんでした。

これらの方程式に基づいて、Shapelyを使用した完全なソリューションを次に示します

#!/usr/bin/env python
from shapely.geometry import Point, Polygon
from math import sqrt
from sys import maxint

# define our polygon of interest, and the point we'd like to test
# for the nearest location
polygon = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
point = Point(0.5, 1.5)

# pairs iterator:
# http://stackoverflow.com/questions/1257413/1257446#1257446
def pairs(lst):
    i = iter(lst)
    first = prev = i.next()
    for item in i:
        yield prev, item
        prev = item
    yield item, first

# these methods rewritten from the C version of Paul Bourke's
# geometry computations:
# http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
def magnitude(p1, p2):
    vect_x = p2.x - p1.x
    vect_y = p2.y - p1.y
    return sqrt(vect_x**2 + vect_y**2)

def intersect_point_to_line(point, line_start, line_end):
    line_magnitude =  magnitude(line_end, line_start)
    u = ((point.x - line_start.x) * (line_end.x - line_start.x) +
         (point.y - line_start.y) * (line_end.y - line_start.y)) \
         / (line_magnitude ** 2)

    # closest point does not fall within the line segment, 
    # take the shorter distance to an endpoint
    if u < 0.00001 or u > 1:
        ix = magnitude(point, line_start)
        iy = magnitude(point, line_end)
        if ix > iy:
            return line_end
        else:
            return line_start
    else:
        ix = line_start.x + u * (line_end.x - line_start.x)
        iy = line_start.y + u * (line_end.y - line_start.y)
        return Point([ix, iy])

nearest_point = None
min_dist = maxint

for seg_start, seg_end in pairs(list(polygon.exterior.coords)[:-1]):
    line_start = Point(seg_start)
    line_end = Point(seg_end)

    intersection_point = intersect_point_to_line(point, line_start, line_end)
    cur_dist =  magnitude(point, intersection_point)

    if cur_dist < min_dist:
        min_dist = cur_dist
        nearest_point = intersection_point

print "Closest point found at: %s, with a distance of %.2f units." % \
   (nearest_point, min_dist)

後世のために、このArcView拡張機能はこの問題を非常にうまく処理しているように見えますが、死んだ言語で書かれた死んだプラットフォームではあまりにも悪いです...


1
明示的な列挙を回避するために、ポリゴンポイントにインデックスを付ける方法があるのだろうか?
-mlt

@mltは、あなたが何を考えているのか正確にはわかりませんが、ジオメトリによっては役立つアプローチがいくつかあります。パフォーマンスが問題である場合、いくつかの基本的なレイキャスティングを実行して、関連する最も近いセグメントを決定できます。その流れで、これをCまたはPyrexに移動すると、物事が改善されます。
scw

私は意味pairs、それはアルゴリズムO(n)のか何かです。@eprandソリューションはおそらくKNNを使用するように変更できますが、私はこれまでPostGISなしで生きていました...-
mlt

以前のコメントはもう編集できません:(おそらく、PostGISがオプションの場合、NicklasAvénとST_ClosestpointおよびST_Shortestlineのソリューションは最速です。
mlt

右、PythonでKNNアルゴリズムを直接使用できます。私はST_ShortestlineがKNN使用していることを信じていない、それだけで、同様の私の読書に基づいて反復しpostgis.refractions.net/documentation/postgis-doxygen/d1/dbf/...
SCW

8

PostGIS回答(複数線ストリングの場合、線ストリングの場合、st_geometryn関数を削除します)

select t2.gid as point_gid, t1.gid as line_gid, 
st_makeline(t2.geom,st_line_interpolate_point(st_geometryn(t1.geom,1),st_line_locate_point(st_geometryn(t1.geom,1),t2.geom))) as geom
from your_line_layer t1, your_point_layer t2, 
(
select gid as point_gid, 
(select gid 
from your_line_layer
order by st_distance(your_line_layer.geom, your_point_layer.geom)
limit 1 ) as line_gid
from your_point_layer
) as t3
where t1.gid = t3.line_gid
and t2.gid = t3.point_gid

4

これは少し古いですが、今日この問題の解決策を探していました(ポイント->行)。この関連する問題に出くわした最も簡単な解決策は次のとおりです。

>>> from shapely.geometry import Point, LineString
>>> line = LineString([(0, 0), (1, 1), (2, 2)])
>>> point = Point(0.3, 0.7)
>>> point
POINT (0.3000000000000000 0.7000000000000000)
>>> line.interpolate(line.project(point))
POINT (0.5000000000000000 0.5000000000000000)

4

私があなたを正しく理解していれば、あなたが求めている機能はPostGISに組み込まれています。

ラインに投影されたポイントを取得するには、ST_Closestpointを使用できます(PostGIS 1.5で)

それを使用する方法についてのヒントは、ここで読むことができます: http //blog.jordogskog.no/2010/02/07/how-to-use-the-new-distance-related-functions-in-postgis-part1/

また、たとえば、ポリゴン上の別のポリゴンに最も近い点を見つけるためにも使用できます。

両方のジオメトリの2つの最も近いポイント間の線が必要な場合は、ST_Shortestlineを使用できます。ST_Closestpointは、ST_Shortestlineの最初のポイントです

2つのジオメトリ間のST_Shortestlineの長さは、ジオメトリ間のST_Distanceと同じです。


3

私の答えが信頼できる解決策と見なされるべきではないという方法については、以下のコメントをご覧ください。

質問が理解できれば、この一般的な手順は機能するはずです。

ユークリッド空間内のポイント(x、yまたはx、y、zで定義)とポリイン(x、yまたはx、y、zの接続セットで定義)との間の最短経路を見つけるには:

1)指定されたユーザー定義ポイント(pt0と呼びます)から、ポリライン(pt1)の最も近い頂点を見つけます。OGRinfoはポリラインの頂点をポーリングでき、標準の方法で距離を計算できます。たとえば、distance_in_radians = 2 * math.asin(math.sqrt(math.pow((math.sin((pt0_radians-ptx_radians)/ 2))、2)+ math.cos(pt0_radians)のような距離計算を繰り返します。 * math.cos(ptx_radians)* math.pow((math.sin((pt0_radians-ptx_radians)/ 2))、2)))

2)関連する最小距離値(d1)および(pt1)を保存する

3)pt1から派生する2つのセグメントを見てください(ogrinfoラインストリングでは、これらは前と後の頂点になります)。これらの頂点(n2およびn3)を記録します。

4)各セグメントに対してy = mx + b式を作成します

5)これらの2つの式のそれぞれについて、ポイント(pt0)を垂線に関連付けます

6)距離と交差点の計算(d2およびd3; pt2、pt3)

7)3つの距離(d1、d2、d3)を比較して最短にします。関連するノード(pt1、pt2、またはpt3)へのpt0が最短リンクです。

それは意識の答えの流れです-うまくいけば、問題と解決策の私の心の絵が合っています。


これは一般に機能しません。例:ポイント=(1,1)、ライン=((0,2)、(0,3)、(3,0)、(2,0))。それをスケッチすると、ライン上の「最も近い」頂点がポイントに最も近いパスに隣接していないことがわかります...これを処理する唯一の方法は、すべてのセグメントをチェックすることです少し最適化してください)。HTH。
トム

3

上記のヒントとソリューションから作成された2.0を超えるQGIS用のPythonスクリプトを次に示します。合理的な量のポイントとラインで問題なく動作します。しかし、私は大量のオブジェクトでそれを試しませんでした。

もちろん、それはアイドル状態でコピーするか、他の「pythonicソリューション」ではなく、「closest.point.py」として保存する必要がありました。

QGISツールボックスで、スクリプト、ツールを選択し、スクリプトを追加して選択します。

##Vector=group
##CLosest_Point_V2=name
##Couche_de_Points=vector
##Couche_de_Lignes=vector

"""
This script intent to provide a count as for the SQL Funciton CLosestPoint
Ce script vise a recréer dans QGIS la Focntion SQL : CLosest Point
It rely on the solutions provided in "Nearest neighbor between a point layer and a line layer"
  http://gis.stackexchange.com/questions/396/nearest-pojected-point-from-a-point-                               layer-on-a-line-or-polygon-outer-ring-layer
V2 du  8 aout 2016
jean-christophe.baudin@onema.fr
"""
from qgis.core import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os
import sys
import unicodedata 
from osgeo import ogr
from math import sqrt
from sys import maxint

from processing import *

def magnitude(p1, p2):
    if p1==p2: return 1
    else:
        vect_x = p2.x() - p1.x()
        vect_y = p2.y() - p1.y()
        return sqrt(vect_x**2 + vect_y**2)

def intersect_point_to_line(point, line_start, line_end):
    line_magnitude =  magnitude(line_end, line_start)
    u = ((point.x()-line_start.x())*(line_end.x()-line_start.x())+(point.y()-line_start.y())*(line_end.y()-line_start.y()))/(line_magnitude**2)
    # closest point does not fall within the line segment, 
    # take the shorter distance to an endpoint
    if u < 0.0001 or u > 1:
        ix = magnitude(point, line_start)
        iy = magnitude(point, line_end)
        if ix > iy:
            return line_end
        else:
            return line_start
    else:
        ix = line_start.x() + u * (line_end.x() - line_start.x())
        iy = line_start.y() + u * (line_end.y() - line_start.y())
        return QgsPoint(ix, iy)

layerP = processing.getObject(Couche_de_Points)
providerP = layerP.dataProvider()
fieldsP = providerP.fields()
inFeatP = QgsFeature()

layerL = processing.getObject(Couche_de_Lignes)
providerL = layerL.dataProvider()
fieldsL = providerL.fields()
inFeatL = QgsFeature()

counterP = counterL= nElement=0

for featP in layerP.selectedFeatures():
    counterP+=1
if counterP==0:
    QMessageBox.information(None,"information:","Choose at least one point from point layer_"+ str(layerP.name())) 

indexLine=QgsSpatialIndex()
for featL in layerL.selectedFeatures():
    indexLine.insertFeature(featL)
    counterL+=1
if counterL==0:
    QMessageBox.information(None,"information:","Choose at least one line from point layer_"+ str(layerL.name()))
    #QMessageBox.information(None,"DEBUGindex:",str(indexBerge))     
ClosestP=QgsVectorLayer("Point", "Projected_ Points_From_"+ str(layerP.name()), "memory")
QgsMapLayerRegistry.instance().addMapLayer(ClosestP)
prClosestP = ClosestP.dataProvider()

for f in fieldsP:
    znameField= f.name()
    Type= str(f.typeName())
    if Type == 'Integer': prClosestP.addAttributes([ QgsField( znameField, QVariant.Int)])
    if Type == 'Real': prClosestP.addAttributes([ QgsField( znameField, QVariant.Double)])
    if Type == 'String': prClosestP.addAttributes([ QgsField( znameField, QVariant.String)])
    else : prClosestP.addAttributes([ QgsField( znameField, QVariant.String)])
prClosestP.addAttributes([QgsField("DistanceP", QVariant.Double),
                                        QgsField("XDep", QVariant.Double),
                                        QgsField("YDep", QVariant.Double),
                                        QgsField("XProj", QVariant.Double),
                                        QgsField("YProj", QVariant.Double),
                                        QgsField("Xmed", QVariant.Double),
                                        QgsField("Ymed", QVariant.Double)])
featsP = processing.features(layerP)
nFeat = len(featsP)
"""
for inFeatP in featsP:
    progress.setPercentage(int(100 * nElement / nFeatL))
    nElement += 1
    # pour avoir l'attribut d'un objet/feat .... 
    attributs = inFeatP.attributes()
"""

for inFeatP in layerP.selectedFeatures():
    progress.setPercentage(int(100 * nElement / counterL))
    nElement += 1
    attributs=inFeatP.attributes()
    geomP=inFeatP.geometry()
    nearest_point = None
    minVal=0.0
    counterSelec=1
    first= True
    nearestsfids=indexLine.nearestNeighbor(geomP.asPoint(),counterSelec)
    #http://blog.vitu.ch/10212013-1331/advanced-feature-requests-qgis
    #layer.getFeatures( QgsFeatureRequest().setFilterFid( fid ) )
    request = QgsFeatureRequest().setFilterFids( nearestsfids )
    #list = [ feat for feat in CoucheL.getFeatures( request ) ]
    # QMessageBox.information(None,"DEBUGnearestIndex:",str(list))
    NBNodes=0
    Dist=DistT=minValT=Distance=0.0
    for featL in  layerL.getFeatures(request):
        geomL=featL.geometry()
        firstM=True
        geomL2=geomL.asPolyline()
        NBNodes=len(geomL2)
        for i in range(1,NBNodes):
            lineStart,lineEnd=geomL2[i-1],geomL2[i]
            ProjPoint=intersect_point_to_line(geomP.asPoint(),QgsPoint(lineStart),QgsPoint(lineEnd))
            Distance=magnitude(geomP.asPoint(),ProjPoint)
            toto=''
            toto=toto+ 'lineStart :'+ str(lineStart)+ '  lineEnd : '+ str(lineEnd)+ '\n'+ '\n'
            toto=toto+ 'ProjPoint '+ str(ProjPoint)+ '\n'+ '\n'
            toto=toto+ 'Distance '+ str(Distance)
            #QMessageBox.information(None,"DEBUG", toto)
            if firstM:
                minValT,nearest_pointT,firstM = Distance,ProjPoint,False
            else:
                if Distance < minValT:
                    minValT=Distance
                    nearest_pointT=ProjPoint
            #at the end of the loop save the nearest point for a line object
            #min_dist=magnitude(ObjetPoint,PProjMin)
            #QMessageBox.information(None,"DEBUG", " Dist min: "+ str(minValT))
        if first:
            minVal,nearest_point,first = minValT,nearest_pointT,False
        else:
            if minValT < minVal:
                minVal=minValT
                nearest_point=nearest_pointT
                #at loop end give the nearest Projected points on Line nearest Line
    PProjMin=nearest_point
    Geom= QgsGeometry().fromPoint(PProjMin)
    min_dist=minVal
    PX=geomP.asPoint().x()
    PY=geomP.asPoint().y()
    Xmed=(PX+PProjMin.x())/2
    Ymed=(PY+PProjMin.y())/2
    newfeat = QgsFeature()
    newfeat.setGeometry(Geom)
    Values=[]
    #Values.extend(attributs)
    fields=layerP.pendingFields()
    Values=[attributs[i] for i in range(len(fields))]
    Values.append(min_dist)
    Values.append(PX)
    Values.append(PY)
    Values.append(PProjMin.x())
    Values.append(PProjMin.y())
    Values.append(Xmed)
    Values.append(Ymed)
    newfeat.setAttributes(Values)
    ClosestP.startEditing()  
    prClosestP.addFeatures([ newfeat ])
    #prClosestP.updateExtents()
ClosestP.commitChanges()
iface.mapCanvas().refresh()

!!! 警告!!! 次の行コマンドにより、「奇妙な」/間違った投影点が生成される可能性があることに注意してください。

nearestsfids=indexLine.nearestNeighbor(geomP.asPoint(),counterSelec)

そのcounterSelec値は、nearestNeighborがいくつ返されるかを設定します。実際、各ポイントは、各ラインオブジェクトに可能な限り最短距離で投影される必要があります。そして、見つかった最小距離は、求める最近傍として正しいラインと投影点を提供します。ループ時間を短縮するために、最も近い近隣コマンドが使用されます。counterSelec値を1に減らすことを選択すると、一致した「最初の」オブジェクト(より正確に境界ボックス)が返され、正しいオブジェクトではない場合があります。異なる行サイズのオブジェクトは、最短距離を決定するために3または5、またはさらに近いオブジェクトを選択する必要があります。値が大きいほど、時間がかかります。数百のポイントとラインでは、3または5の最近傍で非常に遅くなり始め、数千ではそのような値でバグが発生する可能性があります。


3

興味とユースケースに応じて、「マップマッチングアルゴリズム」を調べると役立つ場合があります。たとえば、OSM wikiにはRoadMatcherプロジェクトがあります:http ://wiki.openstreetmap.org/wiki/Roadmatcher 。


旅行の需要と予測のためです。通常、エリアをトラフィック分析ゾーン(ポリゴン)に分割し、そのゾーン内のすべてのトラフィックの「ダミー」発信元としてポリゴンの重心を確立します。次に、そのポイントから最も近い道路にxまたはyの「ダミー道路リンク」線を描画し、そのゾーンからそれらのダミーリンクと実際の道路レイヤーにトラフィックを均等に分配します
-dassouki

ああ、あなたの目標は、この「ダミーの道路リンク」の作成を自動化することですか?
暗闇

確かに:)またはダミーリンク
dassouki
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.