O script destaca as funções forward/backward em relações selecionadas, ideal para ver "buracos" onde houve modificações em vias ou em funções que foram invertidas
Como funciona?
Selecione uma relação
Clique em ligar
Destaque em AZUL para vias com função forward/backward
Destaque em AMARELO para vias sem função forward/backward
"Corrigir roles" adicionará a função forward/backward nas vias em amarelo
"Inverter todos os roles" inverterá forward/backward de todas as vias da relação
Inverter da seleção" irá mudar forward/backward apenas da via selecionada
Demonstração
Imagem.gif, clique para visualizar.
Código
fromorg.openstreetmap.josm.guiimportMainApplication,Notificationfromorg.openstreetmap.josm.gui.layerimportMapViewPaintable,LayerManager,OsmDataLayerfromorg.openstreetmap.josm.commandimportSequenceCommand,ChangeRelationMemberRoleCommandfromorg.openstreetmap.josm.data.osmimportRelationMember,Wayfromorg.openstreetmap.josm.dataimportUndoRedoHandlerfromjavax.swingimportJOptionPane,JDialog,JButton,JPanel,SwingUtilities,BoxLayout,Box,JLabelfromjavax.swing.borderimportEmptyBorderfromjava.awtimportColor,BasicStroke,BorderLayout,AlphaComposite,Componentfromjava.awt.geomimportLine2D,AffineTransformfromjava.awt.eventimportWindowAdapterimportmath# Variáveis globais para gerenciar o estado do script e UIcurrent_arrows=Nonetoggle_button=Nonefix_roles_button=Noneinvert_roles_button=Noneinvert_selected_way_role_button=Nonedialog=Nonelayer_change_listener=NoneclassBusRouteArrows(MapViewPaintable):def__init__(self,relation):self.relation=relationdefpaint(self,g,mv,bbox):ifnotself.relationornotself.relation.isUsable():returng.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.75))g.setStroke(BasicStroke(4,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND))forminself.relation.getMembers():ifm.isWay():way=m.getWay()ifnotway.isUsable():continuenodes=way.getNodes()role=m.getRole()ifrolenotin[u"forward",u"backward"]:g.setColor(Color.YELLOW)else:g.setColor(Color(0,100,255))is_backward=(role==u"backward")ifis_backward:nodes=list(reversed(nodes))foriinrange(len(nodes)-1):p1=mv.getPoint(nodes[i].getEastNorth())p2=mv.getPoint(nodes[i+1].getEastNorth())g.draw(Line2D.Float(p1.x,p1.y,p2.x,p2.y))mid_x=(p1.x+p2.x)/2mid_y=(p1.y+p2.y)/2self._draw_arrow_head(g,mid_x,mid_y,p1.x,p1.y,p2.x,p2.y)def_draw_arrow_head(self,g,x,y,x1,y1,x2,y2):arrow_size=15angle=math.atan2(y2-y1,x2-x1)g.setStroke(BasicStroke(4,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND))transform=AffineTransform()transform.translate(x,y)transform.rotate(angle)arrow_head=[(arrow_size,0),(0,-arrow_size/2),(0,arrow_size/2)]g2=g.create()g2.transform(transform)g2.draw(Line2D.Float(arrow_head[0][0],arrow_head[0][1],arrow_head[1][0],arrow_head[1][1]))g2.draw(Line2D.Float(arrow_head[0][0],arrow_head[0][1],arrow_head[2][0],arrow_head[2][1]))g2.dispose()deftoggle_arrows():globalcurrent_arrows,toggle_button,fix_roles_button,invert_roles_button,invert_selected_way_role_buttonds=MainApplication.getLayerManager().getEditDataSet()ifnotds:Notification(u"Nenhum dataset ativo. Crie ou abra uma camada de dados.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnifcurrent_arrows:_remove_arrows()returnsel=list(ds.getSelectedRelations())ifnotsel:Notification(u"Nenhuma relação selecionada.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnrelation=sel[0]mv=MainApplication.getMap().mapViewcurrent_arrows=BusRouteArrows(relation)mv.addTemporaryLayer(current_arrows)mv.repaint()iftoggle_button:toggle_button.setText(u"Desligar")iffix_roles_button:fix_roles_button.setEnabled(True)ifinvert_roles_button:invert_roles_button.setEnabled(True)ifinvert_selected_way_role_button:invert_selected_way_role_button.setEnabled(True)Notification(u"Setas ativadas para relação: <b>%s</b>"%(relation.get(u"name")oru"sem nome")).setIcon(JOptionPane.INFORMATION_MESSAGE).show()def_remove_arrows():globalcurrent_arrows,toggle_button,fix_roles_button,invert_roles_button,invert_selected_way_role_button,dialog,layer_change_listenerifcurrent_arrows:mv=MainApplication.getMap().mapViewmv.removeTemporaryLayer(current_arrows)current_arrows=Nonemv.repaint()iftoggle_button:toggle_button.setText(u"Ligar")iffix_roles_button:fix_roles_button.setEnabled(False)ifinvert_roles_button:invert_roles_button.setEnabled(False)ifinvert_selected_way_role_button:invert_selected_way_role_button.setEnabled(False)Notification(u"Setas desativadas.").setIcon(JOptionPane.INFORMATION_MESSAGE).show()ifdialog:try:MainApplication.getLayerManager().removeLayerChangeListener(layer_change_listener)except:passdialog.dispose()globals()['dialog']=Noneglobals()['layer_change_listener']=Nonedeffix_roles():globalcurrent_arrowsifnotcurrent_arrows:Notification(u"Ative as setas primeiro para corrigir os roles.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnrelation=current_arrows.relationcommands=[]fori,memberinenumerate(relation.getMembers()):ifnotmember.getRole():commands.append(ChangeRelationMemberRoleCommand(relation,i,"forward"))ifcommands:command_sequence=SequenceCommand(u"Corrigir roles da rota",commands)UndoRedoHandler.getInstance().add(command_sequence)MainApplication.getMap().mapView.repaint()Notification(u"Roles corrigidos com sucesso!").setIcon(JOptionPane.INFORMATION_MESSAGE).show()else:Notification(u"Nenhum role precisou ser corrigido.").setIcon(JOptionPane.INFORMATION_MESSAGE).show()definvert_roles():globalcurrent_arrowsifnotcurrent_arrows:Notification(u"Ative as setas primeiro para inverter os roles.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnrelation=current_arrows.relationcommands=[]has_invertible_roles=Falsefori,memberinenumerate(relation.getMembers()):role=member.getRole()ifrole==u"forward":commands.append(ChangeRelationMemberRoleCommand(relation,i,"backward"))has_invertible_roles=Trueelifrole==u"backward":commands.append(ChangeRelationMemberRoleCommand(relation,i,"forward"))has_invertible_roles=Trueifhas_invertible_roles:command_sequence=SequenceCommand(u"Inverter todos os roles da rota",commands)UndoRedoHandler.getInstance().add(command_sequence)MainApplication.getMap().mapView.repaint()Notification(u"Roles invertidos com sucesso!").setIcon(JOptionPane.INFORMATION_MESSAGE).show()else:Notification(u"Nenhum role de 'forward' ou 'backward' encontrado para inverter.").setIcon(JOptionPane.WARNING_MESSAGE).show()definvert_selected_way_role():globalcurrent_arrowsifnotcurrent_arrows:Notification(u"Ative as setas primeiro.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnds=MainApplication.getLayerManager().getEditDataSet()selected_objects=ds.getSelected()ifnotselected_objects:Notification(u"Nenhum objeto selecionado. Por favor, selecione uma via.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnsel_ways=[oforoinselected_objectsifisinstance(o,Way)]iflen(sel_ways)==0:Notification(u"O objeto selecionado não é uma via. Por favor, selecione uma via.").setIcon(JOptionPane.WARNING_MESSAGE).show()returniflen(sel_ways)>1:Notification(u"Mais de uma via selecionada. Selecione apenas uma via.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnselected_way=sel_ways[0]relation=current_arrows.relationmember_found=Falsefori,memberinenumerate(relation.getMembers()):ifmember.getMember()==selected_way:role=member.getRole()new_role=Noneifrole=="forward":new_role="backward"elifrole=="backward":new_role="forward"else:Notification(u"A via selecionada não tem um role 'forward' ou 'backward'.").setIcon(JOptionPane.WARNING_MESSAGE).show()returncommand=ChangeRelationMemberRoleCommand(relation,i,new_role)UndoRedoHandler.getInstance().add(command)MainApplication.getMap().mapView.repaint()Notification(u"Role da via selecionada invertido para '%s'."%new_role).setIcon(JOptionPane.INFORMATION_MESSAGE).show()member_found=Truebreakifnotmember_found:Notification(u"A via selecionada não é membro da relação de rota ativa.").setIcon(JOptionPane.WARNING_MESSAGE).show()defshow_toggle_window(relation_name):globaltoggle_button,fix_roles_button,invert_roles_button,invert_selected_way_role_button,dialog,layer_change_listenerifdialoganddialog.isShowing():dialog.requestFocus()returndialog_title=u"Direção da Rota"dialog=JDialog(MainApplication.getMainFrame(),dialog_title,False)panel=JPanel()panel.setBorder(EmptyBorder(0,10,10,10))panel.setLayout(BoxLayout(panel,BoxLayout.Y_AXIS))relation_label=JLabel(u"<html><b><br>%s</b></html>"%relation_name)relation_label.setAlignmentX(Component.CENTER_ALIGNMENT)panel.add(relation_label)panel.add(Box.createVerticalStrut(5))toggle_button=JButton(u"Ligar",actionPerformed=lambdae:toggle_arrows())toggle_button.setAlignmentX(Component.CENTER_ALIGNMENT)fix_roles_button=JButton(u"Corrigir Roles",actionPerformed=lambdae:fix_roles())fix_roles_button.setAlignmentX(Component.CENTER_ALIGNMENT)invert_roles_button=JButton(u"Inverter Todos os Roles",actionPerformed=lambdae:invert_roles())invert_roles_button.setAlignmentX(Component.CENTER_ALIGNMENT)invert_selected_way_role_button=JButton(u"Inverter da Seleção",actionPerformed=lambdae:invert_selected_way_role())invert_selected_way_role_button.setAlignmentX(Component.CENTER_ALIGNMENT)fix_roles_button.setEnabled(False)invert_roles_button.setEnabled(False)invert_selected_way_role_button.setEnabled(False)panel.add(toggle_button)panel.add(Box.createVerticalStrut(5))panel.add(fix_roles_button)panel.add(Box.createVerticalStrut(5))panel.add(invert_roles_button)panel.add(Box.createVerticalStrut(5))panel.add(invert_selected_way_role_button)dialog.add(panel,BorderLayout.CENTER)dialog.setSize(300,200)dialog.setLocationRelativeTo(MainApplication.getMainFrame())# Listeners para eventos de janela e camadaclassMyWindowListener(WindowAdapter):defwindowClosing(self,e):_remove_arrows()classMyLayerChangeListener(LayerManager.LayerChangeListener):deflayerAdded(self,e):passdeflayerRemoving(self,e):removed_layer=e.getRemovedLayer()ifcurrent_arrowsandisinstance(removed_layer,OsmDataLayer):ifcurrent_arrows.relationandhasattr(current_arrows.relation,'get_layer')andcurrent_arrows.relation.get_layer()==removed_layer:_remove_arrows()deflayerOrderChanged(self,e):passdialog.addWindowListener(MyWindowListener())ifnotlayer_change_listener:layer_change_listener=MyLayerChangeListener()MainApplication.getLayerManager().addLayerChangeListener(layer_change_listener)dialog.setVisible(True)defstart_bus_arrows():globaldialogifdialoganddialog.isShowing():dialog.requestFocus()returnifnotMainApplication.isDisplayingMapView():Notification(u"Nenhuma camada ativa.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnds=MainApplication.getLayerManager().getEditDataSet()ifnotds:Notification(u"Nenhum dataset ativo.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnsel=list(ds.getSelectedRelations())ifnotsel:Notification(u"Nenhuma relação selecionada.").setIcon(JOptionPane.WARNING_MESSAGE).show()returnrelation=sel[0]relation_name=relation.get(u"name")oru"(sem nome)"show_toggle_window(relation_name)SwingUtilities.invokeLater(start_bus_arrows)