User:DressyPear4/RepeticaoPontosDaLinha

From OpenStreetMap Wiki
Jump to navigation Jump to search

Criar cópias de polígonos (Pontos da linha)

Cria cópias de polígonos posicionando nos pontos de uma linha usada como referência

Como funciona?

  • Selecione uma linha como referência
    • A cópia é sempre feita até o penúltimo ponto, sempre adicione um a mais
  • Selecione o polígono a ser copiado
  • Selecione um nó do polígono para que as cópias mantenham o ângulo original
  • Selecione dois nós do polígono para que as cópias sejam criadas em paralelo com a linha

Demonstração

Código

from org.openstreetmap.josm.gui import MainApplication, Notification
from org.openstreetmap.josm.data.osm import Way, Node
from org.openstreetmap.josm.command import AddCommand, SequenceCommand
from org.openstreetmap.josm.data.coor import LatLon
from org.openstreetmap.josm.data.UndoRedoHandler import getInstance
from javax.swing import JOptionPane, UIManager
from java.util import LinkedList
import math

def calcular_angulo(dx, dy):
    return math.atan2(dy, dx)

def deslocar_em_metros(lat, lon, dx_m, dy_m):
    dlat = dy_m / 111320.0
    dlon = dx_m / (111320.0 * math.cos(math.radians(lat)))
    return lat + dlat, lon + dlon

def vetor_em_metros(n1, n2):
    lat1, lon1 = n1.getCoor().lat(), n1.getCoor().lon()
    lat2, lon2 = n2.getCoor().lat(), n2.getCoor().lon()
    dx = (lon2 - lon1) * 111320 * math.cos(math.radians((lat1 + lat2) / 2))
    dy = (lat2 - lat1) * 111320
    return dx, dy

def rotacionar_e_mover_poligono(orig_nodes, n1_base, n2_base, destino1, destino2, rotacionar=True):
    if rotacionar:
        dx_base, dy_base = vetor_em_metros(n1_base, n2_base)
        ang_base = calcular_angulo(dx_base, dy_base)

        dx_dest, dy_dest = vetor_em_metros(destino1, destino2)
        ang_dest = calcular_angulo(dx_dest, dy_dest)

        angulo_rot = ang_dest - ang_base
    else:
        angulo_rot = 0

    base_lat = n1_base.getCoor().lat()
    base_lon = n1_base.getCoor().lon()
    dest_lat = destino1.getCoor().lat()
    dest_lon = destino1.getCoor().lon()

    novos_nos = []
    for n in orig_nodes:
        lat = n.getCoor().lat()
        lon = n.getCoor().lon()

        dx = (lon - base_lon) * 111320 * math.cos(math.radians((lat + base_lat) / 2))
        dy = (lat - base_lat) * 111320

        dxr = dx * math.cos(angulo_rot) - dy * math.sin(angulo_rot)
        dyr = dx * math.sin(angulo_rot) + dy * math.cos(angulo_rot)

        new_lat, new_lon = deslocar_em_metros(dest_lat, dest_lon, dxr, dyr)
        novos_nos.append(Node(LatLon(new_lat, new_lon)))

    return novos_nos

def executar():
    layer = MainApplication.getLayerManager().getEditLayer()
    if not layer:
        Notification(u"Nenhuma camada de edição ativa.")\
        .setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
        .show()
        return

    dataset = layer.data
    selecionados = dataset.getSelected()

    ways = [x for x in selecionados if isinstance(x, Way)]
    nodes = [x for x in selecionados if isinstance(x, Node)]

    if len(ways) != 2:
        Notification(
            u"Selecione:\n - Um polígono fechado"
            "\n - Uma linha aberta"
            u"\n *Um nó, mantem ângulo original"
            u"\n *Dois nós, rotaciona em paralelo com a linha &nbsp")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()
        return

    way1, way2 = ways
    if way1.isClosed():
        poligono = way1
        linha = way2
    else:
        poligono = way2
        linha = way1

    if not poligono.isClosed():
        Notification(u"O polígono precisa estar fechado.")\
        .setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
        .show()
        return

    if not linha.isClosed() and len(linha.getNodes()) < 2:
        Notification(u"A linha deve ter ao menos dois nós.")\
        .setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
        .show()
        return

    rotacionar = True
    if len(nodes) == 2:
        no1, no2 = nodes
        if no1 not in poligono.getNodes() or no2 not in poligono.getNodes():
            Notification(u"Os dois nós devem pertencer ao polígono.")\
            .setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
            .show()
            return
    elif len(nodes) == 1:
        no1 = no2 = nodes[0]
        rotacionar = False
        if no1 not in poligono.getNodes():
            Notification(u"O nó selecionado deve pertencer ao polígono.")\
            .setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
        .show()
            return
    else:
        Notification(
            u"<html>Selecione: Dois nós do poligono para definir rotação &nbsp;ou apenas um nó para copiar sem rotacionar.</html>")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()
        return

    comandos = []
    linha_nos = linha.getNodes()

    for i in range(len(linha_nos) - 1):
        origem = linha_nos[i]
        destino = linha_nos[i + 1]

        novos_nos = rotacionar_e_mover_poligono(poligono.getNodes(), no1, no2, origem, destino, rotacionar)

        if novos_nos[-1].getCoor().equals(novos_nos[0].getCoor()):
            novos_nos.pop()

        for n in novos_nos:
            comandos.append(AddCommand(dataset, n))

        way_novo = Way()
        way_nodes = LinkedList(novos_nos)
        way_nodes.add(novos_nos[0])
        way_novo.setNodes(way_nodes)
        way_novo.putAll(poligono.getKeys())
        comandos.append(AddCommand(dataset, way_novo))

    if comandos:
        getInstance().add(SequenceCommand("Copiar poligono orientado", comandos))
        Notification(u"Polígonos criados com sucesso.")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()

executar()