Mover nós individuais ou em grupos (passo de 1m ou 10m) mantendo sempre o mesmo espaço entre eles, pode ser usado para fazer o deslocamento de poligonos quando selecionado todos, ou alterar o tamanho.
Como funciona?
Selecione quantidade de nós desejada
Escolha entre deslocamento paralelo ou perpendicular
Todos os nós do poligono - desloca
Apenas parte dos nós - altera geometria
Demonstração
Imagem.gif, clique para visualizar.
Código
fromorg.openstreetmap.josm.guiimportMainApplication,Notificationfromorg.openstreetmap.josm.commandimportMoveCommand,SequenceCommandfromorg.openstreetmap.josm.data.coorimportEastNorthfromorg.openstreetmap.josm.dataimportUndoRedoHandlerfromorg.openstreetmap.josm.data.osmimportWayfromjavax.swingimport(JPanel,JLabel,JButton,BoxLayout,JRadioButton,ButtonGroup,UIManager,JDialog,BorderFactory)fromjava.awtimportDimension,BorderLayout,Fontimportjava.awt.eventasawteimportmathinitial_positions={}defgroup_nodes_by_way(selected_nodes):groups={}forninselected_nodes:forrefinn.getReferrers():ifisinstance(ref,Way):ifrefnotingroups:groups[ref]=set()groups[ref].add(n)returngroupsdefget_sorted_nodes(nodes,way):way_nodes=way.getNodes()node_indices={n:ifori,ninenumerate(way_nodes)ifninnodes}sorted_nodes=sorted(nodes,key=lambdan:node_indices.get(n,-1))iflen(sorted_nodes)>=2andway.isClosed():diffs=[node_indices[sorted_nodes[i+1]]-node_indices[sorted_nodes[i]]foriinrange(len(sorted_nodes)-1)]ifdiffsanddiffs[0]<0:sorted_nodes.reverse()returnsorted_nodesdefget_dominant_edge_vector(nodes):iflen(nodes)<2:return(1,0)max_len=0best_dx,best_dy=1,0foriinrange(len(nodes)-1):p0=nodes[i].getEastNorth()p1=nodes[i+1].getEastNorth()dx=p1.east()-p0.east()dy=p1.north()-p0.north()length=math.hypot(dx,dy)iflength>max_len:max_len=lengthbest_dx,best_dy=dx,dyifmax_len==0:return(1,0)dx,dy=best_dx/max_len,best_dy/max_lenifdx<0:dx,dy=-dx,-dyreturn(dx,dy)defmover_nos(passo,direcao,dialog_state):globalinitial_positionsdataset=MainApplication.getLayerManager().getEditDataSet()ifnotdataset:returnselected_nodes=list(dataset.getSelectedNodes())ifnotselected_nodes:returngroups=group_nodes_by_way(selected_nodes)cmds=[]forway,nodesingroups.items():sorted_nodes=get_sorted_nodes(list(nodes),way)dx,dy=get_dominant_edge_vector(sorted_nodes)ifdirecao=="dy":dx,dy=dy,-dxdelta_x=dx*passodelta_y=dy*passoforninnodes:ifnnotininitial_positions:initial_positions[n]=n.getEastNorth()cmds.append(MoveCommand(n,delta_x,delta_y))ifcmds:UndoRedoHandler.getInstance().add(SequenceCommand(u"Mover nós",cmds))dialog_state['has_moved']=Truedefmain():dataset=MainApplication.getLayerManager().getEditDataSet()ifnotdataset:Notification(u"Nenhuma camada de edição ativa.")\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()returnifnotdataset.getSelectedNodes():Notification(u"Por favor, selecione pelo menos um nó.")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()returndialog_state={'has_moved':False}panel=JPanel()panel.setLayout(BoxLayout(panel,BoxLayout.Y_AXIS))rb_1=JRadioButton("Passo: 1")rb_10=JRadioButton("Passo: 10")rb_1.setSelected(True)original_font=rb_1.getFont()bold_font=Font(original_font.getName(),Font.BOLD,original_font.getSize())rb_1.setFont(bold_font)rb_10.setFont(bold_font)g=ButtonGroup()g.add(rb_1)g.add(rb_10)radio_panel=JPanel()radio_panel.add(rb_1)radio_panel.add(rb_10)panel.add(radio_panel)defget_passo():return10.0ifrb_10.isSelected()else1.0defcreate_control_panel(label_text,direcao):control_panel=JPanel()control_panel.setLayout(BoxLayout(control_panel,BoxLayout.Y_AXIS))padding=BorderFactory.createEmptyBorder(5,5,5,5)etched_border=BorderFactory.createEtchedBorder()control_panel.setBorder(BorderFactory.createCompoundBorder(padding,etched_border))label_panel=JPanel()label=JLabel(label_text)label_panel.add(label)button_panel=JPanel()btn_minus=JButton("-")btn_plus=JButton("+")btn_minus.setPreferredSize(Dimension(50,25))btn_plus.setPreferredSize(Dimension(50,25))defaction_listener(e):passo=get_passo()ife.getSource()==btn_pluselse-get_passo()mover_nos(passo,direcao,dialog_state)btn_minus.addActionListener(action_listener)btn_plus.addActionListener(action_listener)button_panel.add(btn_minus)button_panel.add(btn_plus)control_panel.add(label_panel)control_panel.add(button_panel)returncontrol_panelbutton_definitions=[(u"Paralelo","dx"),(u"Perpendicular","dy")]forlabel,direcaoinbutton_definitions:panel.add(create_control_panel(label,direcao))footer=JPanel()btn_aceitar=JButton("Aceitar")btn_cancelar=JButton("Cancelar")footer.add(btn_aceitar)footer.add(btn_cancelar)parent=MainApplication.getMainFrame()dialog=JDialog(parent,u"Mover Nós",False)classDragListener(awte.MouseAdapter):def__init__(self):self.p=NonedefmousePressed(self,e):self.p=e.getPoint()defmouseDragged(self,e):ifself.p:loc=dialog.getLocation()dialog.setLocation(loc.x+e.getX()-self.p.x,loc.y+e.getY()-self.p.y)drag_listener=DragListener()dialog.addMouseListener(drag_listener)dialog.addMouseMotionListener(drag_listener)content_panel=JPanel(BorderLayout())content_panel.add(panel,BorderLayout.CENTER)content_panel.add(footer,BorderLayout.SOUTH)dialog.setContentPane(content_panel)dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)dialog.pack()dialog.setLocationRelativeTo(parent)defon_aceitar(e):ifdialog_state['has_moved']:Notification(u"Nós movidos com sucesso!")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()else:Notification(u"Nenhum nó foi movido.")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()dialog.dispose()defon_cancelar(e):globalinitial_positionsifdialog_state['has_moved']:cmds=[]fornode,original_posininitial_positions.items():current_pos=node.getEastNorth()dx=original_pos.east()-current_pos.east()dy=original_pos.north()-current_pos.north()ifabs(dx)>1e-9orabs(dy)>1e-9:cmds.append(MoveCommand(node,dx,dy))ifcmds:UndoRedoHandler.getInstance().add(SequenceCommand(u"Cancelar movimento de nós",cmds))Notification(u"Ação cancelada pelo usuário.")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()dialog.dispose()btn_aceitar.addActionListener(on_aceitar)btn_cancelar.addActionListener(on_cancelar)dialog.setVisible(True)main()