User:DressyPear4/RepeticaoDistancia
Jump to navigation
Jump to search
Criar repetições de polígonos (Distância)
Script ideal para quando se quer adicionar geometrias com espaços centrais já definidos, pode-se criar cópias a partir de polígonos simples ou grupo de polígonos.
Como funciona?
- Selecione um polígono ou grupo de polígonos
- Selecione dois nós para definir a direção das cópias
- Em sentido horário, caso contrário as cópias serão criadas na direção oposta
- Selecione a distância em metros
- Selecione a quantidade de repetições
Demonstração
-
Imagem.gif, clique para visualizar.
-
Imagem.gif, clique para visualizar.
Código
from org.openstreetmap.josm.data.osm import Way, Node
from org.openstreetmap.josm.gui import MainApplication, Notification
from org.openstreetmap.josm.data.coor import LatLon
from org.openstreetmap.josm.command import AddCommand, SequenceCommand
from org.openstreetmap.josm.data.UndoRedoHandler import getInstance
from javax.swing import (JOptionPane, JPanel, UIManager, JButton, JTextField, JLabel,
BoxLayout, JSpinner, SpinnerNumberModel, JDialog)
from java.awt import Dimension, Color, GridBagLayout, GridBagConstraints, Insets, BorderLayout
from java.util import ArrayList
import math
import java.awt.event as awte
saved_repetitions = 1
saved_side = "direita"
saved_offset = 10
def validar_selecao():
layer = MainApplication.getLayerManager().getEditLayer()
if layer is None or not hasattr(layer, "data"):
Notification(u"Nenhuma camada de edição ativa.")\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()
return False
dataset = layer.data
selected = dataset.getSelected()
if not selected:
msg = (
u"Selecione dois nós e pelo menos um polígono;"
u"\n A sequência de seleção dos nós deve ser em sentido horário;"
u"\n Os nós servem para indicar a direção, e não precisam pertencer ao polígono."
)
Notification(msg)\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()
return False
selected_nodes = [i for i in selected if isinstance(i, Node)]
selected_ways = [i for i in selected if isinstance(i, Way)]
if len(selected_nodes) < 2:
Notification(u"Selecione dois nós para definir a direção.")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()
return False
if len(selected_ways) < 1:
Notification(u"Selecione pelo menos um polígono.")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()
return False
return True
def agrupar_poligonos_conectados(ways):
grupos = []
visitados = set()
for way in ways:
if way in visitados:
continue
grupo = set()
fila = [way]
while fila:
atual = fila.pop()
if atual in grupo:
continue
grupo.add(atual)
visitados.add(atual)
nos_atual = set(atual.getNodes())
for outro in ways:
if outro in grupo:
continue
if nos_atual.intersection(outro.getNodes()):
fila.append(outro)
grupos.append(grupo)
return grupos
def copiar_tags(origem, destino):
destino.setKeys(origem.getKeys())
def executar_criacao():
global saved_repetitions, saved_side, saved_offset
layer = MainApplication.getLayerManager().getEditLayer()
dataset = layer.data
selected = dataset.getSelected()
selected_nodes = [i for i in selected if isinstance(i, Node)]
selected_ways = [i for i in selected if isinstance(i, Way)]
no1, no2 = selected_nodes[0], selected_nodes[1]
lat1, lon1 = no1.getCoor().lat(), no1.getCoor().lon()
lat2, lon2 = no2.getCoor().lat(), no2.getCoor().lon()
dx = (lon2 - lon1) * 111320 * math.cos(math.radians((lat1 + lat2) / 2))
dy = (lat2 - lat1) * 111320
comprimento = math.hypot(dx, dy)
if comprimento == 0:
Notification(u"Os dois nós de referência são coincidentes.")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()
return
nx = -dy / comprimento
ny = dx / comprimento
sinal = -1 if saved_side == "esquerda" else 1
grupos = agrupar_poligonos_conectados(selected_ways)
comandos = []
for grupo in grupos:
todos_nos = set()
for way in grupo:
todos_nos.update(way.getNodes())
min_proj = max_proj = None
for node in todos_nos:
lat = node.getCoor().lat()
lon = node.getCoor().lon()
dxn = (lon - lon1) * 111320 * math.cos(math.radians((lat + lat1) / 2))
dyn = (lat - lat1) * 111320
proj = dxn * nx + dyn * ny
if min_proj is None or proj < min_proj:
min_proj = proj
if max_proj is None or proj > max_proj:
max_proj = proj
largura = abs(max_proj - min_proj)
if largura == 0:
largura = saved_offset
deslocamento_base = largura + saved_offset
for rep in range(saved_repetitions):
deslocamento = deslocamento_base * (rep + 1) * sinal
dlon = (nx * deslocamento) / (111320 * math.cos(math.radians((lat1 + lat2) / 2)))
dlat = (ny * deslocamento) / 111320
cache = {}
node_map = {}
for node in todos_nos:
lat = node.getCoor().lat() + dlat
lon = node.getCoor().lon() + dlon
key = (round(lat, 8), round(lon, 8))
if key in cache:
novo_no = cache[key]
else:
novo_no = Node(LatLon(lat, lon))
comandos.append(AddCommand(dataset, novo_no))
cache[key] = novo_no
node_map[node] = novo_no
for way in grupo:
novos_nos = [node_map[n] for n in way.getNodes()]
new_way = Way()
new_way.setNodes(ArrayList(novos_nos))
copiar_tags(way, new_way)
comandos.append(AddCommand(dataset, new_way))
if comandos:
getInstance().add(SequenceCommand(u"Cópia de polígono deslocado", comandos))
def abrir_dialogo():
global saved_repetitions, saved_offset
# 1. Configuração do JDialog
parent = MainApplication.getMainFrame()
dialog = JDialog(parent, u"Configurar Cópia de Polígono", True)
dialog.setLayout(BorderLayout())
# 2. Painel de Conteúdo (GridBagLayout)
panel = JPanel()
panel.setLayout(GridBagLayout())
c = GridBagConstraints()
c.insets = Insets(5, 5, 5, 5)
# Label deslocamento extra (m)
c.gridx = 0
c.gridy = 0
c.anchor = GridBagConstraints.EAST
panel.add(JLabel("Deslocamento (m):"), c)
# Campo deslocamento
c.gridx = 1
c.gridy = 0
c.anchor = GridBagConstraints.WEST
deslocamento_field = JTextField(str(saved_offset), 5)
deslocamento_field.setPreferredSize(Dimension(80, 25))
panel.add(deslocamento_field, c)
# Label quantidade repeticoes
c.gridx = 0
c.gridy = 1
c.anchor = GridBagConstraints.EAST
panel.add(JLabel(u"Quantidade de repetições:"), c)
# Spinner para repeticoes
c.gridx = 1
c.gridy = 1
c.anchor = GridBagConstraints.WEST
spinner_model = SpinnerNumberModel(saved_repetitions, 1, 100, 1)
repeticoes_spinner = JSpinner(spinner_model)
repeticoes_spinner.setPreferredSize(Dimension(80, 25))
panel.add(repeticoes_spinner, c)
# Texto informativo
c.gridx = 0
c.gridy = 2
c.gridwidth = 2
c.anchor = GridBagConstraints.CENTER
info_label = JLabel(
"<html><div style='text-align: center; font-size: 9px;'><i>"
u"<span style='color: #66cc66;'>*A direção fica invertida quando os nós <br>"
u"são selecionados em sentido anti-horário.<br>"
u"<span style='color: #ff4444;'>*Se o deslocamento for = 0, selecione todos os nós <br>"
u"e use a validação para remover duplicados."
"</i></div></html>"
)
panel.add(info_label, c)
# 3. Painel de Botões (Rodapé)
button_panel = JPanel()
icon_ok = UIManager.getIcon("OptionPane.yesIcon")
icon_cancel = UIManager.getIcon("OptionPane.noIcon")
btn_criar = JButton(u"Criar", icon_ok)
btn_cancelar = JButton(u"Cancelar", icon_cancel)
button_panel.add(btn_criar)
button_panel.add(btn_cancelar)
# --- ACTION LISTENERS
def on_criar(e):
try:
# Captura e salva os novos valores
global saved_offset, saved_repetitions
saved_offset = float(deslocamento_field.getText())
saved_repetitions = int(repeticoes_spinner.getValue())
executar_criacao()
Notification(u"Cópia concluída.")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()
dialog.dispose()
except ValueError:
Notification(u"Valores inválidos inseridos.")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()
def on_cancelar(e):
dialog.dispose()
btn_criar.addActionListener(on_criar)
btn_cancelar.addActionListener(on_cancelar)
# 4. Monta e Exibe o Diálogo
dialog.add(panel, BorderLayout.CENTER)
dialog.add(button_panel, BorderLayout.SOUTH)
dialog.pack()
dialog.setLocationRelativeTo(parent)
dialog.setVisible(True)
# --- EXECUÇÃO PRINCIPAL ---
if validar_selecao():
abrir_dialogo()