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.
Comme on peut le voir, il existe des cas particuliers - points noirs encerclés:
- 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.
- 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.
- 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