Trouver le point qui se situe entre deux lignes parallèles

8

Je rencontre un problème dans ArcGIS. Je travaille sur une base de données de navigation. Dans notre base de données, les rues à voie unique sont représentées par une seule ligne, tandis qu'une rue à plusieurs voies (rue avec séparateur au centre) est représentée par deux lignes parallèles (lignes rouges sur l'image).

J'ai un fichier de formes de points avec certains points tombant à l'intérieur d'une rue à plusieurs voies et d'autres à l'extérieur.

Je veux créer un script ArcPy qui trouverait les points qui se trouvent dans les rues à plusieurs voies. c'est-à-dire entre ces lignes parallèles (marquées sur l'image).

Je ne sais pas comment y parvenir, quelqu'un peut-il m'aider?

Un problème de rue à plusieurs voies

J'ai fait un peu d'exercice dessus et j'ai trouvé que la création d'un tampon sur un côté de la ligne peut créer à l'intérieur du polygone à plusieurs voies (montré sur l'image).

entrez la description de l'image ici

mais maintenant le problème est que le polygone traverse réellement la ligne (c'est-à-dire qu'il chevauche la frontière à plusieurs voies). il capturera donc des points inutiles. existe-t-il un moyen d'aligner ce polygone sur la ligne de rue?

Remarque: l'intégration ne fonctionnera pas ici, car elle déplace également la ligne de rue. j'ai juste besoin d'aligner le polygone le long de la ligne de rue.

Akhil Kumar
la source
Quelque chose comme Mesurer l'azimut de la rue - Créez des lignes linéaires de chaque point vers l'angle Azimut + 90 degrés - Comptez le nombre de vos lignes parallèles que cette ligne intersecte. Si zéro ou deux -> à l'extérieur, si un -> Vous l'avez trouvé. Penser juste, peut fonctionner ou non. Une autre idée est de convertir la rue à double sens en polygone et de sélectionner les points qui se croisent. Ce dernier peut être délicat à faire avec python. Eh bien, le premier aussi si les rues sont courbes. Mais avec un tampon simple face, vous pourrez peut-être créer de très beaux polygones de rue.
user30184
1
avez-vous une licence avancée? Ce serait assez simple avec l'outil proche.
radouxju
oui j'ai une licence avancée.
Akhil Kumar
Au début, j'ai pensé à prendre un polygone tampon et à recouper ces polygones. et découvrez quels points tombent dans ce polygone intersecté. mais le plus gros problème est que la distance intermédiaire n'est pas uniforme partout dans la rue. quelque part, il ne fait que 10 mètres quelque part autour de 20 mètres, dans ce cas, la logique d'intersection de polygones sera défaillante
Akhil Kumar
1
Faire un tampon du côté droit à 10 m du côté gauche et un tampon du côté gauche du côté droit. De cette façon, vous couvrirez une portée de 10 à 20 m. Les chevauchements ne font aucun mal et vous pouvez également fusionner les polygones en premier. Ou élargissez encore le polygone tampon d'un côté et coupez-le en le coupant de l'autre côté. Faites preuve d'imagination et jouez.
user30184

Réponses:

4

J'essaierais ci-dessous l'algorithme arcpy (même manuel!)

  1. Trouvez la bonne largeur des rues à deux voies - ici, vous devrez peut-être regrouper des rues de même largeur et suivre la procédure ci-dessous pour chaque groupe.
  2. Créez un tampon les deux lignes dans les deux sens (droite et gauche) avec cette largeur (ou un peu moins que cela - pour assurer la zone de la route).
  3. Exécutez l'outil Intersection pour obtenir la région chevauchée.
  4. Exécutez Sélectionner par emplacement pour sélectionner les points qui se trouvent à l'intérieur de ce polygone.
SIslam
la source
Je pense que c'est la voie à suivre. Trouvez un moyen facile de joindre le dessin au trait, soit par tampon, soit en quelque sorte fermez les lignes pour créer un seul polygone, puis sélectionnez-le.
Barrett
2

Je dirais que c'est un exercice géométrique.

CODE PSEUDO:

  • Pour chaque point (point noir), trouvez la route la plus proche et trouvez la projection du point sur cette route (point rouge).
  • Tracez une ligne courte (en pointillés) dans la direction opposée à partir du point noir
  • Trouvez s'il y a une intersection entre la ligne courte et la route du même nom, l'étoile bleue. S'il y en a un, le point noir est celui que nous recherchons.

entrez la description de l'image ici

Comme on peut le voir, il existe des cas particuliers - points noirs encerclés:

  1. Route 1 ligne très sinueuse. Ceci peut être éliminé en a) travaillant uniquement avec des routes à 2 lignes ou b) en s'assurant que les FID des routes qui croisent le point rouge et l'étoile sont différents. Cependant, si la route coudée a une jonction avec une autre route à 1 ligne, cela pourrait ne pas fonctionner.
  2. Le point noir est assis sur le prolongement d'une route à 1 ligne exactement perpendiculaire. Dans ce cas, il est possible que la route à 1 voie soit choisie comme voisin le plus proche.
  3. Le point noir se trouve sur la ligne.

Tous les cas ci-dessus sont très improbables, mais il semble que l'option la plus sûre consiste à travailler uniquement avec des routes à 2 lignes, c'est-à-dire à les exporter vers une classe d'entités distincte. Le cas 3 est drôle, nous allons le laisser au hasard, car la distance la plus courte par rapport à la ligne n'est jamais vraiment nulle, donc la direction `` opposée '' du rayon reliant 2 points peut être trouvée.

Implémentation de Python:

import arcpy, traceback, os, sys
from arcpy import env
env.overwriteoutput=True

# things to change ---------
maxD=30
mxd = arcpy.mapping.MapDocument("CURRENT")
pointLR = arcpy.mapping.ListLayers(mxd,"NODES")[0]
lineLR = arcpy.mapping.ListLayers(mxd,"LINKS")[0]
sjOneToMany=r'D:\scratch\sj2.shp'
RDNAME='street'
# -------------------------
dDest=arcpy.Describe(lineLR)
SR=dDest.spatialReference

try:
    def showPyMessage():
        arcpy.AddMessage(str(time.ctime()) + " - " + message)
    g = arcpy.Geometry()
    geometryList=arcpy.CopyFeatures_management(pointLR,g)
    n=len(geometryList)
    endPoint=arcpy.Point()

    arcpy.SpatialJoin_analysis(pointLR, lineLR,sjOneToMany,"JOIN_ONE_TO_MANY","KEEP_COMMON","","WITHIN_A_DISTANCE",maxD)
    initFidList=(-1,)
    for fid in range(n):
        query='"TARGET_FID" = %s' %str(fid)
        nearTable=arcpy.da.TableToNumPyArray(sjOneToMany,("TARGET_FID","JOIN_FID"),query)
        if len(nearTable)<2:continue
        fidLines=[int(row[1]) for row in nearTable]
        query='"FID" in %s' %str(tuple(fidLines))
        listOfLines={}
        blackPoint=geometryList[fid]
        with arcpy.da.SearchCursor(lineLR,("FID", "Shape@","STREET"),query) as rows:
            dMin=100000
            for row in rows:
                shp=row[1];dCur=blackPoint.distanceTo(shp)
                listOfLines[row[0]]=row[-2:]
                if dCur<dMin:
                    fidNear,lineNear, roadNear=row
                    dMin=dCur
            chainage=lineNear.measureOnLine(blackPoint)
            redPoint=lineNear.positionAlongLine (chainage).firstPoint
            smallD=blackPoint.distanceTo(redPoint)
            fp=blackPoint.firstPoint
            dX=(redPoint.X-fp.X)*(maxD-smallD)/smallD
            dY=(redPoint.Y-fp.Y)*(maxD-smallD)/smallD
            endPoint.X=fp.X-dX;endPoint.Y=fp.Y-dY
            dashLine=arcpy.Polyline(arcpy.Array([fp,endPoint]),SR)

            for n in listOfLines:
                if n==fidNear:continue
                line, road=listOfLines[n]
                if road!=roadNear:continue
                blueStars=dashLine.intersect(line,1)
                if blueStars.partCount==0:continue
                initFidList+=(fid,); break
    query='"FID" in %s' %str(initFidList)
    arcpy.SelectLayerByAttribute_management(pointLR, "NEW_SELECTION", query)
    arcpy.AddMessage ('\n %i point(s) found' %(len(initFidList)-1))
except:
    message = "\n*** PYTHON ERRORS *** "; showPyMessage()
    message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
    message = "Python Error Info: " +  str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()            

Il existe une autre solution possible, peut-être plus élégante. Cela implique une triangulation. Faites-moi savoir si cela vous intéresse et je mettrai à jour ma réponse

FelixIP
la source
C'est assez complexe, wow. Il semble qu'il serait beaucoup plus simple de créer un polygone à partir des lignes, puis d'utiliser le lancer de rayons . Déterminer si un point se trouve sur une ligne doit également être simple.
Paul
1
Si vous êtes capable de créer des polygones à partir de lignes correctes, pas besoin de lancer. Sélectionnez par emplacement fera l'affaire. La création de polygones est un défi
FelixIP
Est-ce que cela fonctionnera bien dans les virages - juste pour des éclaircissements :)
SIslam
1
@SIslam, il devrait fonctionner même avec de grands virages similaires au cas 1 (voir si n == fidNear: continuer). Eh bien, s'il n'y a pas de route à 1 voie. Je continue de penser que la dissolution peut aider, mais pas toujours
FelixIP
@Islam Oups! Ce ne sera pas le cas, car la condition (si n == fidNear: continue) élimine les points assis à l'extérieur du virage, mais marque le point à l'intérieur comme étant assis à l'extérieur. Un virage serré est nécessaire, rayon plus petit que la largeur?
FelixIP
0

Étant donné que les rues sont parallèles, j'ai supposé qu'elles ont été créées avec l' Copy Paralleloutil dans la barre d'outils Modifier, ce qui fait que la paire de lignes a la même direction. Nous pouvons ensuite itérer sur les coordonnées de la première ligne et les ajouter à un polygone, puis itérer sur l' inverse de la deuxième ligne. Il y a certainement une meilleure façon d'approcher les paires de lignes de saisie; l'approche OID fonctionne, mais elle n'est pas très jolie.

import collections
import arcpy

FC = "fc"
points = "points"
pgons = "pgons"
arcpy.env.overwriteOutput = True

def buildpoly(oid_coords):
    #create ddict of the form OID:<x1y1, x2y2, ..., xn-1yn-1, xnyn>
    ddict = collections.defaultdict(list)    
    for k,v in oid_coords:
        ddict[k].append(v)

    line1,line2 = ddict.keys()    

    #Assume that the parallel lines have same direction, so reverse the second
    arr = arcpy.Array()
    arr.extend(arcpy.Point(*pt) for pt in ddict[line1])    
    arr.extend(arcpy.Point(*pt) for pt in ddict[line2][::-1])

    return arcpy.Polygon(arr)

#id is an integer field that pairs parallel lines together
unique = list(set(t[0] for t in arcpy.da.SearchCursor(FC, "id")))
polygons = []
for uni in unique:
    polygons.append(buildpoly([r for r in row] for row in arcpy.da.SearchCursor(FC,
                                                                                ["OID@", "SHAPE@XY"],
                                                                                "id={}".format(uni),
                                                                                explode_to_points=True)))


arcpy.CopyFeatures_management(polygons, pgons)

À partir de là, c'est un appel à Intersect / Select Layer by location / what have you. Notez que le Spolygone formé n'est pas parfait car je l'ai dessiné à main levée et il y a des arcs qui explode_to_pointsne se gèrent pas correctement. Exécutez simplement Densifyou équivalent.

entrez la description de l'image ici

Paul
la source
Il s'agit d'un ensemble de données de réseau routier, donc les routes à 1 voie sont connectées à 2 voies via un nœud, c'est-à-dire qu'il n'y a pas de paires d'entités parallèles
FelixIP
Vous voudrez peut-être étendre votre solution en ajoutant la dissolution par des noms de route individuels (pas de parties m) et prendre en compte les cas de 1 ou 2 lignes en conséquence
FelixIP
@FelixIP, je ne connais pas très bien les jeux de données réseau. Ma solution était principalement une preuve de concept de la façon dont cela peut être fait avec des lignes simples (OP peut l'étendre pour couvrir la mrésolution, le multipart, etc.). Je ne sais pas comment des fonctionnalités comme celle-ci sont réellement représentées dans un réseau.
Paul
@ Paul La route du même nom peut être représentée par des segments de 100 s assis dans différentes rangées du tableau. De plus, une route à double voie pourrait devenir une voie à quelque part. La dissolution échouera gravement si aucune des pièces n'est pas en (1,2), c'est pourquoi je n'ai pas
opté pour une
1
@AkhilKumar, peu importe s'ils sont à peu près parallèles. Ceci trace les lignes existantes.
Paul