Eu diria que este é um exercício geométrico.
PSEUDO-CÓDIGO:
- Para cada ponto (ponto preto), encontre a estrada mais próxima e localize a projeção do ponto nessa estrada (ponto vermelho).
- Desenhe uma linha curta (tracejada) na direção oposta, começando no ponto preto
- Descubra se há interseção entre a linha curta e o mesmo nome da estrada, estrela azul. Se houver, o ponto preto é o que procuramos.
Como se pode ver, existem casos especiais - pontos pretos circulados:
- Estrada muito sinuosa de 1 linha. Isso pode ser eliminado por a) trabalhando apenas com estradas de 2 linhas ou b) certificando-se de que os FIDs das estradas que cruzam ponto vermelho e estrela sejam diferentes. No entanto, se a estrada dobrada tiver uma junção com outra estrada de 1 linha, isso pode não funcionar.
- O ponto preto está localizado na extensão da estrada de 1 linha exatamente perpendicular. Nesse caso, existe a possibilidade de uma estrada de 1 faixa ser escolhida como o vizinho mais próximo.
- Ponto preto fica na linha.
Todos os casos acima são muito improváveis, no entanto, parece que a opção mais segura é trabalhar apenas com duas estradas de linha, ou seja, exportá-las para uma classe de recurso separada. O caso 3 é engraçado, vamos deixar ao acaso, porque a menor distância da linha nunca é zero, portanto, a direção 'oposta' do raio que conecta 2 pontos pode ser encontrada.
Implementação do 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()
Existe outra solução possível, talvez mais elegante. Envolve triangulação. Informe-me se for de seu interesse e atualizarei minha resposta