User:DressyPear4/AdicionaNosSuavizaLinha

From OpenStreetMap Wiki
Jump to navigation Jump to search

Adicionar nós e suavizar linha

Em áreas com ângulos e pouca quantidades de nós, adicionar nós um por um e arrastar para posição correta é uma tarefa trabalhosa, esse código adiciona nós entre pares de nós existentes e cria suavizações em ângulos muito agudo, funciona para linhas e áreas. Pode ser combinado com atalho Shift+Y para simplificar a quantidade de nós caso forem adicionados em excesso.

Como funciona?

  • Selecione uma linha ou polígono
  • Clicar nos botões +/- para adicionar/remover nós
  • Clicar nos botões +/- para ter maior/menor suavização

Demonstração

Imagem.gif, clique para visualizar.

Código

from org.openstreetmap.josm.gui import MainApplication, Notification
from org.openstreetmap.josm.data.osm import Node, Way
from org.openstreetmap.josm.command import SequenceCommand, AddCommand, ChangeNodesCommand, ChangeCommand
from org.openstreetmap.josm.data.projection import ProjectionRegistry
from org.openstreetmap.josm.data.coor import EastNorth, LatLon
from org.openstreetmap.josm.data import UndoRedoHandler
from javax.swing import JPanel, JLabel, JButton, JOptionPane, BoxLayout, Box, UIManager
from java.awt import Dimension, FlowLayout

def main():
    MAX_NOS = 4
    MAX_ITERACOES = 20

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

    selection = dataset.getSelected()
    selected_ways = [o for o in selection if isinstance(o, Way)]
    selected_nodes = [o for o in selection if isinstance(o, Node)]

    if not selected_ways:
        Notification("Selecione pelo menos uma linha")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()
        return

    projection = ProjectionRegistry.getProjection()

    num_inter = [0]
    iteracoes = [0]
    pilha_cmds = []

    def contar_nos(ways):
        total = 0
        for w in ways:
            nodes = w.getNodes()
            if w.isClosed() and len(nodes) > 1 and nodes[0] == nodes[-1]:
                total += len(nodes) - 1  # ignora o nó duplicado de fechamento
            else:
                total += len(nodes)
        return total

    panel = JPanel()
    panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS))
    panel.add(Box.createRigidArea(Dimension(0, 5)))

    info = JLabel(u"Selecionados: {} linha(s), {} nó(s)".format(len(selected_ways), len(selected_nodes)))
    info.setAlignmentX(0.5)
    panel.add(info)

    panel.add(Box.createRigidArea(Dimension(0, 10)))

    label_total_nos = JLabel(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))
    label_total_nos.setAlignmentX(0.5)
    panel.add(label_total_nos)

    panel.add(Box.createRigidArea(Dimension(0, 10)))

    label_nos = JLabel(u"Nós entre pares: 0")
    label_nos.setAlignmentX(0.5)
    panel.add(label_nos)

    botoes_nos = JPanel(FlowLayout())
    bot_mais_nos = JButton("+")
    bot_menos_nos = JButton("-")
    bot_mais_nos.setPreferredSize(Dimension(35, 35))
    bot_menos_nos.setPreferredSize(Dimension(35, 35))
    botoes_nos.add(bot_menos_nos)
    botoes_nos.add(bot_mais_nos)
    panel.add(botoes_nos)

    msg_nos = JLabel("")
    msg_nos.setAlignmentX(0.5)
    panel.add(msg_nos)

    panel.add(Box.createRigidArea(Dimension(0, 10)))

    label_iter = JLabel(u"Iterações de suavização: 0")
    label_iter.setAlignmentX(0.5)
    panel.add(label_iter)

    botoes_iter = JPanel(FlowLayout())
    bot_mais_iter = JButton("+")
    bot_menos_iter = JButton("-")
    bot_mais_iter.setPreferredSize(Dimension(35, 35))
    bot_menos_iter.setPreferredSize(Dimension(35, 35))
    botoes_iter.add(bot_menos_iter)
    botoes_iter.add(bot_mais_iter)
    panel.add(botoes_iter)

    msg_iter = JLabel("")
    msg_iter.setAlignmentX(0.5)
    panel.add(msg_iter)

    panel.add(Box.createRigidArea(Dimension(0, 10)))

    def suavizar_trecho(nodes, iteracoes_val):
        fechado = nodes[0] == nodes[-1]
        for _ in range(iteracoes_val):
            new_nodes = []
            total = len(nodes)
            for i in range(total):
                if not fechado and (i == 0 or i == total - 1):
                    new_nodes.append(nodes[i])
                    continue
                prev = nodes[(i - 1) % total].getCoor()
                curr = nodes[i].getCoor()
                next = nodes[(i + 1) % total].getCoor()
                lat = (prev.lat() + curr.lat() + next.lat()) / 3.0
                lon = (prev.lon() + curr.lon() + next.lon()) / 3.0
                novo = Node()
                novo.setCoor(LatLon(lat, lon))
                new_nodes.append(novo)
            nodes = new_nodes
        return nodes

    num_inter_prev = [num_inter[0]]
    iteracoes_prev = [iteracoes[0]]

    def aplicar(adicionar_nos):
        comandos = []
        for way in selected_ways:
            way_nodes = list(way.getNodes())
            start_idx, end_idx = 0, len(way_nodes) - 1
            trecho = way_nodes

            sel_in_way = [n for n in selected_nodes if n in way_nodes]
            if len(sel_in_way) >= 2:
                idxs = sorted([way_nodes.index(n) for n in sel_in_way])
                start_idx, end_idx = idxs[0], idxs[-1]
                trecho = way_nodes[start_idx:end_idx + 1]

            if len(trecho) < 2:
                continue

            updated_trecho = []
            if adicionar_nos:
                for i in range(len(trecho) - 1):
                    n1 = trecho[i]
                    n2 = trecho[i + 1]
                    updated_trecho.append(n1)
                    if num_inter[0] > 0:
                        en1 = n1.getEastNorth()
                        en2 = n2.getEastNorth()
                        dx = (en2.east() - en1.east()) / (num_inter[0] + 1)
                        dy = (en2.north() - en1.north()) / (num_inter[0] + 1)
                        for j in range(1, num_inter[0] + 1):
                            x = en1.east() + dx * j
                            y = en1.north() + dy * j
                            latlon = projection.eastNorth2latlon(EastNorth(x, y))
                            novo = Node(latlon)
                            novo.setModified(True)
                            comandos.append(AddCommand(dataset, novo))
                            updated_trecho.append(novo)
                updated_trecho.append(trecho[-1])
            else:
                updated_trecho = trecho[:]

            new_way_nodes = way_nodes[:start_idx] + updated_trecho + way_nodes[end_idx + 1:]
            comandos.append(ChangeNodesCommand(way, new_way_nodes))

            if iteracoes[0] > 0:
                suavizados = suavizar_trecho(updated_trecho, iteracoes[0])
                for i in range(len(updated_trecho)):
                    old_node = updated_trecho[i]
                    new_node = suavizados[i]
                    mod_node = Node(old_node)
                    mod_node.setCoor(new_node.getCoor())
                    mod_node.setModified(True)
                    comandos.append(ChangeCommand(dataset, old_node, mod_node))

        if comandos:
            cmd = SequenceCommand("Inserir e suavizar", comandos)
            UndoRedoHandler.getInstance().add(cmd)
            pilha_cmds.append(cmd)

    def mais_nos(e):
        if num_inter[0] < MAX_NOS:
            num_inter[0] += 1
            label_nos.setText(u"Nós entre pares: {}".format(num_inter[0]))
            msg_nos.setText("")
            if num_inter[0] != num_inter_prev[0]:
                aplicar(True)
                num_inter_prev[0] = num_inter[0]
        else:
            msg_nos.setText(u"<html><span style='color:red;'>Limite máximo atingido ({})</span></html>".format(MAX_NOS))
        label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))

    def menos_nos(e):
        if num_inter[0] > 0:
            num_inter[0] -= 1
            label_nos.setText(u"Nós entre pares: {}".format(num_inter[0]))
            msg_nos.setText("")
            if num_inter[0] != num_inter_prev[0]:
                if pilha_cmds:
                    UndoRedoHandler.getInstance().undo()
                    pilha_cmds.pop()
                num_inter_prev[0] = num_inter[0]
        label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))

    def mais_iter(e):
        if iteracoes[0] < MAX_ITERACOES:
            iteracoes[0] += 1
            label_iter.setText(u"Iterações de suavização: {}".format(iteracoes[0]))
            msg_iter.setText("")
            if iteracoes[0] != iteracoes_prev[0]:
                aplicar(False)
                iteracoes_prev[0] = iteracoes[0]
        else:
            msg_iter.setText(u"<html><span style='color:red;'>Limite máximo atingido ({})</span></html>".format(MAX_ITERACOES))
        label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))

    def menos_iter(e):
        if iteracoes[0] > 0:
            iteracoes[0] -= 1
            label_iter.setText(u"Iterações de suavização: {}".format(iteracoes[0]))
            msg_iter.setText("")
            if iteracoes[0] != iteracoes_prev[0]:
                if pilha_cmds:
                    UndoRedoHandler.getInstance().undo()
                    pilha_cmds.pop()
                iteracoes_prev[0] = iteracoes[0]
        label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))

    bot_mais_nos.addActionListener(lambda e: mais_nos(e))
    bot_menos_nos.addActionListener(lambda e: menos_nos(e))
    bot_mais_iter.addActionListener(lambda e: mais_iter(e))
    bot_menos_iter.addActionListener(lambda e: menos_iter(e))

    aplicar(False)

    result = JOptionPane.showConfirmDialog(None, panel, "Inserir e suavizar", 
    JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE)

    if result == JOptionPane.CANCEL_OPTION:
        while pilha_cmds:
            UndoRedoHandler.getInstance().undo()
            pilha_cmds.pop()
        return

    if num_inter[0] == 0 and iteracoes[0] == 0:
        Notification("Nada alterado")\
        .setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
        .show()
    elif num_inter[0] > 0 and iteracoes[0] == 0:
        Notification(u"Nós adicionados")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()
    elif num_inter[0] == 0 and iteracoes[0] > 0:
        Notification(u"Suavização aplicada")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()
    else:
        Notification(u"Nós adicionados e suavização aplicada com sucesso!")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()

main()