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
importsysfromjavax.swingimport(JDialog,JOptionPane,JSpinner,JRadioButton,ButtonGroup,JPanel,JLabel,BoxLayout,JButton,BorderFactory,UIManager,SpinnerNumberModel,Box)fromjava.awtimportBorderLayout,Dimension,FlowLayout,Componentfromorg.openstreetmap.josm.guiimportMainApplication,Notificationfromorg.openstreetmap.josm.commandimportAddCommand,SequenceCommandfromorg.openstreetmap.josm.data.osmimportWay,Nodefromorg.openstreetmap.josm.data.coorimportEastNorthfromorg.openstreetmap.josm.data.projectionimportProjectionRegistryfromorg.openstreetmap.josm.dataimportUndoRedoHandler# -------------------- Diálogo de configuração --------------------classCopyDialog(JDialog):def__init__(self,parent):JDialog.__init__(self,parent,u"Configurar Cópia de Polígonos",True)self.result=Noneself.num_copies=0self.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 verticalself.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 verticalbutton_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(lambdae:self._on_ok())cancel_button.addActionListener(lambdae: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_OPTIONself.num_copies=self.spinner.getValue()self.side=u"esquerda"ifself.rb_left.isSelected()elseu"direita"self.dispose()# -------------------- Funções de cópia --------------------defcalculate_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()returndx,dyexceptExceptionase:returnNone,Nonedefcopy_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)fori,ninenumerate(original_nodes):ifi==num_nodes-1andway.isClosed()andn==original_nodes[0]:new_nodes_list.append(new_nodes_list[0])continueen=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))returncommandsdefduplicate_polygons(ways,num_copies,side):iflen(ways)!=2:Notification(u"Selecione exatamente 2 polígonos.").setIcon(UIManager.getIcon("OptionPane.warningIcon")).show()returnifnotways[0].isClosed()ornotways[1].isClosed():Notification(u"Ambos os objetos selecionados devem ser polígonos fechados (vias fechadas).").setIcon(UIManager.getIcon("OptionPane.errorIcon")).show()returnproj=ProjectionRegistry.getProjection()dx,dy=calculate_offset_vector(ways[0],ways[1],proj)ifside==u"esquerda":source_poly=ways[0]dx,dy=-dx,-dyelse:source_poly=ways[1]commands=[]foriinrange(num_copies):delta_en=(dx*(i+1),dy*(i+1))commands.extend(copy_way_with_offset(source_poly,delta_en,proj))ifcommands: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 --------------------defrun_script():ways=[oforoinMainApplication.getLayerManager().getEditDataSet().getSelected()ifisinstance(o,Way)]iflen(ways)!=2:Notification(u"Selecione exatamente dois polígonos para continuar.").setIcon(UIManager.getIcon("OptionPane.warningIcon")).show()returndialog=CopyDialog(MainApplication.getMainFrame())dialog.setVisible(True)ifdialog.result==JOptionPane.OK_OPTION:duplicate_polygons(ways,dialog.num_copies,dialog.side)run_script()