बिंदु परत और रेखा परत के बीच निकटतम पड़ोसी? [बन्द है]


37

मैंने कई बार स्टैकओवरफ्लो पर यह सवाल पूछा है और #qgis और #postgis के बीच irc किया है और मैंने इसे कोड करने की कोशिश भी की है या इसे बिना किसी वास्तविक जवाब के पोस्टगिस में अपना स्वयं का कार्यान्वयन किया है।

प्रोग्रामिंग का उपयोग करना (सबसे अधिमानतः अजगर), मैं एक बिंदु परत से एक रेखा खींचना चाहता हूं, इसके प्रक्षेपण के लिए निकटतम रेखा या बहुभुज परत पर।

अब तक मेरा अधिकांश डेटा ESRI के आकार और पोस्टगिस स्वरूपों में है; हालाँकि, मैं पोस्टगिस समाधान से दूर रहूँगा क्योंकि मैं मुख्यतः एक shp + qgis उपयोगकर्ता हूँ।

एक आदर्श समाधान अजगर या इसी तरह के पुस्तकालयों के साथ GDAL / OGR को लागू करना होगा

  • GDAL / OGR पुस्तकालयों का उपयोग करके मुझे कहां से शुरू करना चाहिए? क्या समाधान योजना देना संभव होगा?
  • क्या मैं निकटतम पड़ोसी विश्लेषण करने के लिए NetworkX का उपयोग कर सकता हूं?
  • क्या यह वास्तव में संभव है?

यदि यह आसान है, तो अंक एक अनुमानित बिंदु के बजाय खंड अंत बिंदु से जुड़ सकते हैं


क्या लाइन को सेगमेंट में ऑर्थोनल होने तक सीमित किया जा सकता है?
वोल्फऑर्ड्रेड

@wolfOdrade - कुल मिलाकर, इससे कोई फर्क नहीं पड़ता।
दासौकी

जवाबों:


33

यह सवाल थोड़ा पेचीदा निकला क्योंकि मैंने सोचा था कि यह सही है। कम से कम दूरी के कई कार्यान्वयन हैं, जैसे कि शेपली प्रदान की गई दूरी (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)

Posterity के लिए, ऐसा लगता है कि यह ArcView एक्सटेंशन इस समस्या को काफी अच्छी तरह से संभालता है, एक मृत मंच में लिखे गए एक मृत मंच पर बहुत बुरा है ...


1
मुझे आश्चर्य है कि यदि स्पष्ट गणना से बचने के लिए बहुभुज बिंदुओं को अनुक्रमित करने का एक तरीका है ...
013

@ml यकीन नहीं है कि आप क्या सोच रहे हैं, लेकिन कुछ दृष्टिकोण हैं जो ज्यामिति के आधार पर मदद कर सकते हैं। यदि प्रासंगिक समस्या थी, तो संबंधित निकटतम सेगमेंट निर्धारित करने के लिए कुछ मूल किरण-कास्टिंग कर सकते हैं। उस नस में, इसे सी या पाइरेक्स में ले जाने से चीजों में सुधार होगा।
SCW

मेरा मतलब है कि pairsयह एल्गोरिदमिक रूप से हे (एन) या कुछ है। @ क्रेन्ड समाधान शायद KNN का उपयोग करने के लिए संशोधित किया जा सकता है लेकिन मैं अब तक PostGIS के बिना रहने में कामयाब रहा ...
13'13

मैं अपनी पिछली टिप्पणी को किसी भी समय संपादित नहीं कर सकता :( शायद ST_Closestpoint & ST_Shortestline के साथ निकल्स एवन का समाधान सबसे तेज़ है अगर PostGIS एक विकल्प है।
17:13 बजे

ठीक है, आप सीधे पायथन में एक KNN एल्गोरिथ्म का उपयोग कर सकते हैं । मुझे विश्वास नहीं होता है कि ST_Shortestline KNN का उपयोग करता है, यह सिर्फ itgates के रूप में अच्छी तरह से postgis.refractions.net/documentation/postgis-doxygen/d1/dbf// के
बजे

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

अगर मैं आपको सही समझता हूं कि आप जो कार्यक्षमता पूछ रहे हैं, वह पोस्टगिस में बनाई गई है।

एक बिंदु पर एक लाइन प्राप्त करने के लिए आप ST_Closestpoint (PostGIS 1.5 पर) का उपयोग कर सकते हैं

इसका उपयोग करने के तरीके के बारे में कुछ संकेत आप यहाँ पढ़ सकते हैं: http://blog.jordogskog.no/2010/02/07/how-to-use-the-new-distance-related-functions-in-postgis-part1/

उदाहरण के लिए, एक बहुभुज पर किसी अन्य बहुभुज पर निकटतम बिंदु खोजना भी उपयोगी है।

यदि आप दोनों ज्यामिति पर दो निकटतम बिंदुओं के बीच की रेखा चाहते हैं तो आप ST_Shortestline का उपयोग कर सकते हैं। ST_Closestpoint ST_Shortestline में पहला बिंदु है

दो ज्यामितीयों के बीच ST_Shortestline की लंबाई, ज्यामिति के बीच ST_Distance के समान है।


3

नीचे दी गई टिप्पणी देखें कि कैसे मेरे जवाब को एक विश्वसनीय समाधान नहीं माना जाना चाहिए ... मैं इस मूल पद को यहां छोड़ दूंगा ताकि अन्य लोग समस्या की जांच कर सकें।

यदि मैं प्रश्न को समझता हूं, तो यह सामान्य प्रक्रिया काम करनी चाहिए।

यूक्लिडियन स्थान के भीतर एक बिंदु (x, y या x, y, z के रूप में परिभाषित) और एक पॉलिने के रूप में सबसे छोटा रास्ता खोजने के लिए (जैसा कि x, y या x, y, z के कनेक्टिंग सेट द्वारा परिभाषित):

1) दिए गए उपयोगकर्ता-निर्धारित बिंदु से (मैं इसे pt0 कहूँगा), पॉलीलाइन (pt1) का निकटतम शीर्ष ज्ञात करें। OGRinfo एक पॉलीलाइन के कोने को प्रदूषित कर सकता है, और फिर मानक तरीकों से दूरी की गणना की जा सकती है। उदाहरण के लिए, एक दूरी के कैल्क पर पुनरावृति करें जैसे: दूरी_इन_राडियों = 2 * math.asin (math.sqrt (math.pow ((math.sin (pt0_radians-ptx_radians)) (2)), 2) + math.cos (pt0_radians) * गणित.क्योंकि (ptx_radians) * math.pow ((गणित.पाप ((pt0_radians-ptx_radians) / 2)), 2)))

2) संबद्ध न्यूनतम दूरी मूल्य (d1) और (pt1) को स्टोर करें

3) pt1 से दूर होने वाले दो खंडों को देखें (ओग्रीनो लिनेस्ट्रिंग में, ये पहले और बाद में लंबवत होंगे)। इन कोने (n2 और n3) को रिकॉर्ड करें।

4) प्रत्येक सेगमेंट के लिए y = mx + b फॉर्मूला बनाएं

5) उन दो सूत्रों में से प्रत्येक के लिए अपनी बात (pt0) संबंधित करें

6) दूरी और चौराहों की गणना (d2 और d3; pt2, pt3)

7) कम से कम तीन दूरी (d1, d2, d3) की तुलना करें। संबद्ध नोड (pt1, pt2, या pt3) के लिए आपका pt0 सबसे छोटा लिंक है।

यह चेतना जवाब की एक धारा है - उम्मीद है, समस्या और समाधान की मेरी मानसिक तस्वीर फिट बैठती है।


यह सामान्य रूप से काम नहीं करेगा। ईजी पॉइंट = (1,1), लाइन = ((0,2), (0,3), (3,0), (2,0))। यदि आप इसे स्केच करते हैं, तो आप देख सकते हैं कि लाइन पर "निकटतम" वर्टीकल उस खंड से सटे नहीं हैं जो बिंदु के सबसे पास से गुजरता है ... मुझे लगता है कि इसे संभालने का एकमात्र तरीका हर सेगमेंट की जांच करना है (संभवतः बचने के लिए बाउंडिंग बॉक्स का उपयोग करना। इसे थोड़ा अनुकूलित करें)। HTH।
टॉम

3

यहाँ QGIS> 2.0 के लिए एक पायथन लिपि दी गई है जो ऊपर दिए गए संकेत और समाधान से बनाई गई है। यह उचित मात्रा में बिंदुओं और रेखाओं के लिए काम करता है। लेकिन मैंने भारी मात्रा में वस्तुओं के साथ इसकी कोशिश नहीं की।

बेशक इसे निष्क्रिय या जो कुछ भी कम "पाइथोनिक समाधान" में कॉपी किया जाना था और इसे "निकटतम.प्रेमो" के रूप में सहेजें।

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मान सेट करता है कि कितने निकटतम निघेबर वापस किए गए हैं। वास्तव में प्रत्येक बिंदु को प्रत्येक पंक्ति वस्तु के लिए कम से कम दूरी पर अनुमानित किया जाना चाहिए; और मिली न्यूनतम दूरी सही रेखा और अनुमानित बिंदु के रूप में निकटतम पड़ोसियों की तलाश करेगी। लूपिंग समय को कम करने के लिए, निकटतम पड़ोसी कमांड का उपयोग किया जाता है। एक counterSelecमान को घटाकर 1 को चुनने पर "पहले" ऑब्जेक्ट मिले (यह बॉक्स को अधिक सटीक रूप से बाउंड कर रहा है) वापस आ जाएगा और यह सही नहीं हो सकता है। अलग-अलग लाइन आकार की वस्तुएं चुनने के लिए बाध्य हो सकती हैं 3 या 5, या सबसे कम दूरी की वस्तुओं को निर्धारित करने के लिए। मूल्य जितना अधिक होगा, उतना ही अधिक समय लगेगा। सैकड़ों अंकों और रेखाओं के साथ यह 3 या 5 निकटतम पड़ोसी के साथ बहुत धीमी गति से शुरू होता है, हजारों के साथ इस तरह के मूल्यों के साथ बग हो सकता है।


3

आपके हितों और उपयोग के मामले के आधार पर, "मैप मैचिंग एल्गोरिदम" पर ध्यान देना उपयोगी हो सकता है। उदाहरण के लिए, OSM विकी पर एक रोडमेकर परियोजना है: http://wiki.openstreetmap.org/wiki/Roadmatcher


यह यात्रा की मांग और पूर्वानुमान के लिए है। आमतौर पर हम क्षेत्रों को यातायात विश्लेषण क्षेत्रों (बहुभुज) में विभाजित करते हैं और हम उस क्षेत्र में सभी यातायात के "डमी" प्रवर्तक के रूप में बहुभुज के केंद्रक को स्थापित करते हैं। हम तो निकटतम सड़कों के लिए उस बिंदु से एक्स या वाई "डमी सड़क संपर्क" लाइनों आकर्षित और यातायात उन डमी लिंक पर और पर वास्तविक सड़क परत है कि क्षेत्र से समान रूप से वितरित
dassouki

आह, तो आपका लक्ष्य इस "डमी रोड लिंक" के निर्माण को स्वचालित करना है?
UnderDark

वास्तव में :) या डमी लिंक (ओं)
dassouki
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.