Descubra o ponto que fica entre duas linhas paralelas

8

Estou enfrentando um problema no ArcGIS. Eu trabalho em um banco de dados de navegação. Em nosso banco de dados, as ruas de pista única são representadas por uma única linha, enquanto uma rua de pista múltipla (rua com divisor no centro) é representada por duas linhas paralelas (linhas vermelhas na imagem).

Eu tenho um shapefile de ponto com alguns pontos caindo dentro da rua com várias pistas e outros fora.

Eu quero criar um script do ArcPy que encontre os pontos que se enquadram nas ruas de várias faixas. ou seja, entre essas linhas paralelas (marcadas na figura).

Eu não sei como conseguir isso, alguém pode me ajudar?

Um problema de rua com várias faixas

Eu fiz alguns exercícios e descobri que a criação de buffer em um lado da linha pode criar dentro do polígono de várias faixas (mostrado na figura).

insira a descrição da imagem aqui

mas agora o problema é que o polígono está realmente cruzando a linha (ou seja, sobrepondo-se ao limite de várias faixas). então ele vai pegar pontos desnecessários. existe alguma maneira de alinhar esse polígono com a linha da rua?

Nota: integrar não funcionará aqui, porque também move a linha da rua. Eu preciso apenas alinhar o polígono ao longo da linha da rua.

Akhil Kumar
fonte
Algo como Medir o azimute da rua - Crie cadeias de linhas de cada ponto em direção ao ângulo Azimute + 90 graus - Conte quantas linhas paralelas essa linha cruza. Se zero ou dois -> do lado de fora, se um -> Você o encontrou. Só de pensar, pode funcionar ou não. Outra idéia é converter a via de mão dupla em polígono e selecionar pontos que se cruzam. O último pode ser complicado de fazer com python. Bem, o primeiro também se as ruas são curvas. Mas com o buffer de um lado, você poderá criar polígonos de rua bastante agradáveis.
user30184
1
você tem uma licença avançada? Seria bastante simples com a ferramenta near.
Radouxju 3/10
Sim, eu tenho licença avançada.
Akhil Kumar
No começo, pensei em pegar um polígono de buffer e depois cruzá-lo. e descubra quais pontos se enquadram nesse polígono cruzado. mas o maior problema é que a distância intermediária não é consistente em todos os lugares da rua. em algum lugar que é apenas 10 metros algo em torno de 20 metros, nesse caso polígono cruzam lógica será falhou
Akhil Kumar
1
Faça um buffer do lado direito de 10 m do lado esquerdo e um buffer do lado esquerdo. Dessa forma, você cobre o alcance de 10 a 20 m. As sobreposições não causam nenhum dano e você também pode mesclar os polígonos primeiro. Ou faça um polígono de buffer de um lado ainda mais amplo e corte-o cruzando com o outro lado. Use a imaginação e brinque.
precisa saber é o seguinte

Respostas:

4

Eu tentaria abaixo do algoritmo arcpy (mesmo manual!)

  1. Encontre a largura adequada das ruas de duas faixas - aqui você pode precisar agrupar ruas com a mesma largura e seguir o procedimento abaixo para cada cluster.
  2. Crie um buffer de ambas as linhas para as duas direções (direita e esquerda) com essa largura (ou um pouco menos que isso - para garantir a área da estrada).
  3. Execute a ferramenta Intersecção para obter a região Sobreposta.
  4. Execute Selecionar por local para selecionar pontos que se enquadram dentro deste polígono.
SIslam
fonte
Eu acho que este é o caminho a percorrer. Encontre uma maneira fácil de unir as linhas de trabalho, seja por buffer ou de alguma forma feche as linhas para criar um único polígono e selecione dentro.
Barrett
2

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.

insira a descrição da imagem aqui

Como se pode ver, existem casos especiais - pontos pretos circulados:

  1. 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.
  2. 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.
  3. 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

FelixIP
fonte
Isso é bastante complexo, uau. Parece que seria muito mais simples criar um polígono a partir das linhas e depois usar a projeção de raios . Determinar se um ponto está em uma linha também deve ser direto.
Paul
1
Se você é capaz de criar polígonos a partir de linhas corretas, não há necessidade de conversão. Selecionar por local serve. Criação de polígonos é um desafio embora
FelixIP
Será que vai funcionar bem em bends- apenas para esclarecimento :)
SIslam
1
@SIslam, ele deve funcionar mesmo com grandes curvas semelhantes ao caso 1 (veja se n == fidNear: continue). Bem, se não há nenhuma estrada 1 pista indo Eu fico pensando que se dissolvem pode ajudar, mas nem sempre.
FelixIP
@Islam Oops! Não será, porque a condição (se n == fidNear: continue) elimina os pontos fora da dobra, mas marca o ponto dentro como um fora. Curva acentuada necessária, porém, raio menor que largura?
FelixIP # 6/15
0

Como as ruas são paralelas, presumi que elas foram criadas com a Copy Parallelferramenta na barra de ferramentas Editar, fazendo com que o par de linhas tenha a mesma direção. Podemos então iterar sobre as coordenadas da primeira linha e adicioná-las a um polígono e, em seguida, iterar no verso da segunda linha. Definitivamente, há uma maneira melhor de abordar os pares de linhas de agarrar; a abordagem OID funciona, mas não é muito bonita.

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)

A partir daí, é uma ligação para Intersect / Select Layer por local / o que você tem. Observe que o Spolígono em forma não é perfeito, pois eu o desenhei à mão livre e há alguns arcos que explode_to_pointsnão são adequados. Basta executar Densifyou equivalente.

insira a descrição da imagem aqui

Paulo
fonte
Esta é dataset rede rodoviária, assim 1 estradas de pista estão ligados a 2 pistas através do nó, ou seja, não há tais coisas como pares de recursos paralelos
FelixIP
Você pode querer expandir a sua solução adicionando dissolvendo por nomes de ruas indivíduo Primeiro (sem m-partes) e considerar os casos de 1 ou 2 linhas, como resultado
FelixIP
@ FelixIP, não estou muito familiarizado com os conjuntos de dados da rede. Minha solução foi principalmente uma prova de conceito de como isso pode ser feito com linhas simples (o OP pode estendê-lo para cobrir mresolução, multipartes etc.). Não sei como recursos como esse são realmente representados em uma rede.
Paul
@ Paul A estrada com o mesmo nome pode ser representada por segmentos 100s sentados em linhas diferentes na tabela. Além disso, a estrada de pista dupla pode se tornar pista única em algum lugar. Dissolver falhará mal se nenhuma das partes não em (1,2), é por isso que eu não vá com a solução de triangulação
FelixIP
1
@AkhilKumar, não importa se eles são aproximadamente paralelos. Isso está rastreando as linhas existentes.
Paul