User:DressyPear4/Elipse

From OpenStreetMap Wiki
Jump to navigation Jump to search

Criar uma elipse

JOSM já possui uma função para criar círculos, atalho Shift+O. Partindo do mesmo princípio, um código para criar uma elipse onde um círculo não seria adequado.

Como funciona?

  • Adicione uma linha com exatamente 3 nós
  • Selecione apenas os nós dessa linha
  • Escolha a quantidade de nós
    • Quanto maior a elipse, mais nós devem ser adicionados

Demonstração

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, DeleteCommand
from org.openstreetmap.josm.data import UndoRedoHandler
from javax.swing import JOptionPane, JPanel, JLabel, JSpinner, SpinnerNumberModel, UIManager
import math

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

    sel = layer.data.getSelectedNodes()
    if len(sel) != 3:
        Notification(u"Selecione exatamente 3 nós.")\
        .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
        .show()
        return

    # Interface com spinner
    panel = JPanel()
    panel.add(JLabel(u"Número de pontos na elipse:"))
    spinner = JSpinner(SpinnerNumberModel(30, 12, 360, 5))
    panel.add(spinner)
    option = JOptionPane.showConfirmDialog(None, panel, u"Parâmetros da Elipse", JOptionPane.OK_CANCEL_OPTION)
    if option != JOptionPane.OK_OPTION:
        return
    num_pontos = spinner.getValue()

    n1, n2, n3 = sel
    p1 = (n1.getCoor().lon(), n1.getCoor().lat())
    p2 = (n2.getCoor().lon(), n2.getCoor().lat())
    p3 = (n3.getCoor().lon(), n3.getCoor().lat())

    # Centro da elipse
    cx = (p1[0] + p3[0]) / 2
    cy = (p1[1] + p3[1]) / 2

    dx = p3[0] - p1[0]
    dy = p3[1] - p1[1]
    a = math.hypot(dx, dy) / 2
    angle = math.atan2(dy, dx)

    def dist_perp(px, py, x1, y1, x2, y2):
        num = abs((x2 - x1)*(y1 - py) - (x1 - px)*(y2 - y1))
        den = math.hypot(x2 - x1, y2 - y1)
        return num / den

    b = dist_perp(p2[0], p2[1], p1[0], p1[1], p3[0], p3[1])

    pontos = []
    for i in range(num_pontos):
        t = 2 * math.pi * i / num_pontos
        ex = a * math.cos(t)
        ey = b * math.sin(t)
        rx = ex * math.cos(angle) - ey * math.sin(angle)
        ry = ex * math.sin(angle) + ey * math.cos(angle)
        lon = cx + rx
        lat = cy + ry
        pontos.append(Node(LatLon(lat, lon)))

    cmds = []

    for n in pontos:
        cmds.append(AddCommand(layer.data, n))

    way = Way()
    for n in pontos:
        way.addNode(n)
    way.addNode(pontos[0])  # Fecha a elipse

    cmds.append(AddCommand(layer.data, way))

    # Deletar caminho com exatamente os 3 nos
    for w in layer.data.getWays():
        nodes = w.getNodes()
        if all(n in nodes for n in [n1, n2, n3]) and len(nodes) == 3:
            cmds.append(DeleteCommand(layer.data, w))

    # Deletar os nos selecionados
    cmds.append(DeleteCommand(layer.data, sel))

    UndoRedoHandler.getInstance().add(SequenceCommand("Criar elipse", cmds))
    layer.data.setSelected(way)

    Notification(u"Elipse criada com sucesso!")\
    .setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
    .show()

criar_elipse_de_tres_pontos()