User:DressyPear4/RepeticaoSelecao

From OpenStreetMap Wiki
Jump to navigation Jump to search

Criar cópias de polígonos (Seleção)

Continuar uma sequência de polígonos mantendo mesmo ângulo, sentido de direção e espaço entre eles.

Como funciona?

  • Selecione dois polígonos para ser calculado espaço entre eles
  • Escolha a quantidade de cópias
  • Selecione a direção de cópias
    • Deve ser selecionado em sentido horário, caso contrário a direção ficará invertida

Demonstração

Imagem.gif, clique para visualizar.

Código

import sys
from javax.swing import (JDialog, JOptionPane, JSpinner, JRadioButton, ButtonGroup, JPanel,
                         JLabel, BoxLayout, JButton, BorderFactory, UIManager, SpinnerNumberModel, Box)
from java.awt import BorderLayout, Dimension, FlowLayout, Component
from org.openstreetmap.josm.gui import MainApplication, Notification
from org.openstreetmap.josm.command import AddCommand, SequenceCommand
from org.openstreetmap.josm.data.osm import Way, Node
from org.openstreetmap.josm.data.coor import EastNorth
from org.openstreetmap.josm.data.projection import ProjectionRegistry
from org.openstreetmap.josm.data import UndoRedoHandler

# -------------------- Diálogo de configuração --------------------
class CopyDialog(JDialog):
    def __init__(self, parent):
        JDialog.__init__(self, parent, u"Configurar Cópia de Polígonos", True)
        self.result = None
        self.num_copies = 0
        self.side = u"direita"
        self._setup_ui()
        self.pack()
        self.setLocationRelativeTo(parent)

    def _setup_ui(self):
        panel = JPanel()
        panel.setLayout(BoxLayout(panel, BoxLayout.Y_AXIS))
        panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))

        label = JLabel(u"Quantidade de cópias:")
        label.setAlignmentX(Component.CENTER_ALIGNMENT)
        panel.add(label)

        self.spinner = JSpinner(SpinnerNumberModel(1, 1, 100, 1))
        self.spinner.setMaximumSize(Dimension(60, 25))
        self.spinner.setAlignmentX(Component.CENTER_ALIGNMENT)
        panel.add(self.spinner)

        panel.add(Box.createRigidArea(Dimension(0, 10)))  # espaço vertical

        self.rb_left = JRadioButton(u"Esquerda")
        self.rb_right = JRadioButton(u"Direita", True)
        bg = ButtonGroup()
        bg.add(self.rb_left)
        bg.add(self.rb_right)
        self.rb_left.setAlignmentX(Component.CENTER_ALIGNMENT)
        self.rb_right.setAlignmentX(Component.CENTER_ALIGNMENT)
        panel.add(self.rb_left)
        panel.add(self.rb_right)
        panel.add(Box.createRigidArea(Dimension(0, 10)))  # espaço vertical

        button_panel = JPanel()
        button_panel.setLayout(FlowLayout(FlowLayout.CENTER, 15, 5))

        yes_icon = UIManager.getIcon("OptionPane.yesIcon")
        no_icon = UIManager.getIcon("OptionPane.noIcon")

        ok_button = JButton(u"OK", yes_icon)
        cancel_button = JButton(u"Cancelar", no_icon)

        ok_button.setPreferredSize(Dimension(105, 30))
        cancel_button.setPreferredSize(Dimension(105, 30))

        ok_button.addActionListener(lambda e: self._on_ok())
        cancel_button.addActionListener(lambda e: self.dispose())

        button_panel.add(ok_button)
        button_panel.add(cancel_button)

        self.getContentPane().setLayout(BorderLayout())
        self.getContentPane().add(panel, BorderLayout.CENTER)
        self.getContentPane().add(button_panel, BorderLayout.SOUTH)

    def _on_ok(self):
        self.result = JOptionPane.OK_OPTION
        self.num_copies = self.spinner.getValue()
        self.side = u"esquerda" if self.rb_left.isSelected() else u"direita"
        self.dispose()

# -------------------- Funções de cópia --------------------

def calculate_offset_vector(way1, way2, proj):
    """Calcula o vetor de deslocamento entre o centro de dois polígonos."""
    try:
        center1 = way1.getNodes()[0].getEastNorth()
        center2 = way2.getNodes()[0].getEastNorth()
        dx = center2.east() - center1.east()
        dy = center2.north() - center1.north()
        return dx, dy
    except Exception as e:
        return None, None

def copy_way_with_offset(way, delta_en, proj):
    """Copia um polígono com deslocamento e retorna uma lista de comandos."""
    ds = MainApplication.getLayerManager().getEditDataSet()
    commands = []

    new_nodes_list = []
    original_nodes = way.getNodes()
    num_nodes = len(original_nodes)

    for i, n in enumerate(original_nodes):
        if i == num_nodes - 1 and way.isClosed() and n == original_nodes[0]:
            new_nodes_list.append(new_nodes_list[0])
            continue

        en = n.getEastNorth()
        new_en = EastNorth(en.east() + delta_en[0], en.north() + delta_en[1])
        new_node = Node(proj.eastNorth2latlon(new_en))
        commands.append(AddCommand(ds, new_node))
        new_nodes_list.append(new_node)

    new_way = Way()
    new_way.setNodes(new_nodes_list)
    new_way.setKeys(way.getKeys())
    commands.append(AddCommand(ds, new_way))

    return commands

def duplicate_polygons(ways, num_copies, side):
    if len(ways) != 2:
        Notification(u"Selecione exatamente 2 polígonos.").setIcon(UIManager.getIcon("OptionPane.warningIcon")).show()
        return

    if not ways[0].isClosed() or not ways[1].isClosed():
        Notification(u"Ambos os objetos selecionados devem ser polígonos fechados (vias fechadas).").setIcon(UIManager.getIcon("OptionPane.errorIcon")).show()
        return

    proj = ProjectionRegistry.getProjection()
    dx, dy = calculate_offset_vector(ways[0], ways[1], proj)
    
    if side == u"esquerda":
        source_poly = ways[0]
        dx, dy = -dx, -dy
    else:
        source_poly = ways[1]
    
    commands = []
    
    for i in range(num_copies):
        delta_en = (dx * (i+1), dy * (i+1))
        
        commands.extend(copy_way_with_offset(source_poly, delta_en, proj))

    if commands:
        seq = SequenceCommand(u"Cópia de polígonos", commands)
        UndoRedoHandler.getInstance().add(seq)
        Notification(u"%d cópia(s) de polígonos criada(s) com sucesso." % num_copies).setIcon(UIManager.getIcon("OptionPane.informationIcon")).show()

# -------------------- Execução do script --------------------

def run_script():
    ways = [o for o in MainApplication.getLayerManager().getEditDataSet().getSelected() if isinstance(o, Way)]

    if len(ways) != 2:
        Notification(u"Selecione exatamente dois polígonos para continuar.").setIcon(UIManager.getIcon("OptionPane.warningIcon")).show()
        return

    dialog = CopyDialog(MainApplication.getMainFrame())
    dialog.setVisible(True)

    if dialog.result == JOptionPane.OK_OPTION:
        duplicate_polygons(ways, dialog.num_copies, dialog.side)

run_script()