Em áreas com ângulos e pouca quantidades de nós, adicionar nós um por um e arrastar para posição correta é uma tarefa trabalhosa, esse código adiciona nós entre pares de nós existentes e cria suavizações em ângulos muito agudo, funciona para linhas e áreas.
Pode ser combinado com atalho ⇧ Shift+Y para simplificar a quantidade de nós caso forem adicionados em excesso.
Como funciona?
Selecione uma linha ou polígono
Clicar nos botões +/- para adicionar/remover nós
Clicar nos botões +/- para ter maior/menor suavização
Demonstração
Imagem.gif, clique para visualizar.
Código
fromorg.openstreetmap.josm.guiimportMainApplication,Notificationfromorg.openstreetmap.josm.data.osmimportNode,Wayfromorg.openstreetmap.josm.commandimportSequenceCommand,AddCommand,ChangeNodesCommand,ChangeCommandfromorg.openstreetmap.josm.data.projectionimportProjectionRegistryfromorg.openstreetmap.josm.data.coorimportEastNorth,LatLonfromorg.openstreetmap.josm.dataimportUndoRedoHandlerfromjavax.swingimportJPanel,JLabel,JButton,JOptionPane,BoxLayout,Box,UIManagerfromjava.awtimportDimension,FlowLayoutdefmain():MAX_NOS=4MAX_ITERACOES=20dataset=MainApplication.getLayerManager().getEditDataSet()ifnotdataset:Notification(u"Nenhuma camada de edição ativa!")\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()returnselection=dataset.getSelected()selected_ways=[oforoinselectionifisinstance(o,Way)]selected_nodes=[oforoinselectionifisinstance(o,Node)]ifnotselected_ways:Notification("Selecione pelo menos uma linha")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()returnprojection=ProjectionRegistry.getProjection()num_inter=[0]iteracoes=[0]pilha_cmds=[]defcontar_nos(ways):total=0forwinways:nodes=w.getNodes()ifw.isClosed()andlen(nodes)>1andnodes[0]==nodes[-1]:total+=len(nodes)-1# ignora o nó duplicado de fechamentoelse:total+=len(nodes)returntotalpanel=JPanel()panel.setLayout(BoxLayout(panel,BoxLayout.Y_AXIS))panel.add(Box.createRigidArea(Dimension(0,5)))info=JLabel(u"Selecionados: {} linha(s), {} nó(s)".format(len(selected_ways),len(selected_nodes)))info.setAlignmentX(0.5)panel.add(info)panel.add(Box.createRigidArea(Dimension(0,10)))label_total_nos=JLabel(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))label_total_nos.setAlignmentX(0.5)panel.add(label_total_nos)panel.add(Box.createRigidArea(Dimension(0,10)))label_nos=JLabel(u"Nós entre pares: 0")label_nos.setAlignmentX(0.5)panel.add(label_nos)botoes_nos=JPanel(FlowLayout())bot_mais_nos=JButton("+")bot_menos_nos=JButton("-")bot_mais_nos.setPreferredSize(Dimension(35,35))bot_menos_nos.setPreferredSize(Dimension(35,35))botoes_nos.add(bot_menos_nos)botoes_nos.add(bot_mais_nos)panel.add(botoes_nos)msg_nos=JLabel("")msg_nos.setAlignmentX(0.5)panel.add(msg_nos)panel.add(Box.createRigidArea(Dimension(0,10)))label_iter=JLabel(u"Iterações de suavização: 0")label_iter.setAlignmentX(0.5)panel.add(label_iter)botoes_iter=JPanel(FlowLayout())bot_mais_iter=JButton("+")bot_menos_iter=JButton("-")bot_mais_iter.setPreferredSize(Dimension(35,35))bot_menos_iter.setPreferredSize(Dimension(35,35))botoes_iter.add(bot_menos_iter)botoes_iter.add(bot_mais_iter)panel.add(botoes_iter)msg_iter=JLabel("")msg_iter.setAlignmentX(0.5)panel.add(msg_iter)panel.add(Box.createRigidArea(Dimension(0,10)))defsuavizar_trecho(nodes,iteracoes_val):fechado=nodes[0]==nodes[-1]for_inrange(iteracoes_val):new_nodes=[]total=len(nodes)foriinrange(total):ifnotfechadoand(i==0ori==total-1):new_nodes.append(nodes[i])continueprev=nodes[(i-1)%total].getCoor()curr=nodes[i].getCoor()next=nodes[(i+1)%total].getCoor()lat=(prev.lat()+curr.lat()+next.lat())/3.0lon=(prev.lon()+curr.lon()+next.lon())/3.0novo=Node()novo.setCoor(LatLon(lat,lon))new_nodes.append(novo)nodes=new_nodesreturnnodesnum_inter_prev=[num_inter[0]]iteracoes_prev=[iteracoes[0]]defaplicar(adicionar_nos):comandos=[]forwayinselected_ways:way_nodes=list(way.getNodes())start_idx,end_idx=0,len(way_nodes)-1trecho=way_nodessel_in_way=[nforninselected_nodesifninway_nodes]iflen(sel_in_way)>=2:idxs=sorted([way_nodes.index(n)forninsel_in_way])start_idx,end_idx=idxs[0],idxs[-1]trecho=way_nodes[start_idx:end_idx+1]iflen(trecho)<2:continueupdated_trecho=[]ifadicionar_nos:foriinrange(len(trecho)-1):n1=trecho[i]n2=trecho[i+1]updated_trecho.append(n1)ifnum_inter[0]>0:en1=n1.getEastNorth()en2=n2.getEastNorth()dx=(en2.east()-en1.east())/(num_inter[0]+1)dy=(en2.north()-en1.north())/(num_inter[0]+1)forjinrange(1,num_inter[0]+1):x=en1.east()+dx*jy=en1.north()+dy*jlatlon=projection.eastNorth2latlon(EastNorth(x,y))novo=Node(latlon)novo.setModified(True)comandos.append(AddCommand(dataset,novo))updated_trecho.append(novo)updated_trecho.append(trecho[-1])else:updated_trecho=trecho[:]new_way_nodes=way_nodes[:start_idx]+updated_trecho+way_nodes[end_idx+1:]comandos.append(ChangeNodesCommand(way,new_way_nodes))ifiteracoes[0]>0:suavizados=suavizar_trecho(updated_trecho,iteracoes[0])foriinrange(len(updated_trecho)):old_node=updated_trecho[i]new_node=suavizados[i]mod_node=Node(old_node)mod_node.setCoor(new_node.getCoor())mod_node.setModified(True)comandos.append(ChangeCommand(dataset,old_node,mod_node))ifcomandos:cmd=SequenceCommand("Inserir e suavizar",comandos)UndoRedoHandler.getInstance().add(cmd)pilha_cmds.append(cmd)defmais_nos(e):ifnum_inter[0]<MAX_NOS:num_inter[0]+=1label_nos.setText(u"Nós entre pares: {}".format(num_inter[0]))msg_nos.setText("")ifnum_inter[0]!=num_inter_prev[0]:aplicar(True)num_inter_prev[0]=num_inter[0]else:msg_nos.setText(u"<html><span style='color:red;'>Limite máximo atingido ({})</span></html>".format(MAX_NOS))label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))defmenos_nos(e):ifnum_inter[0]>0:num_inter[0]-=1label_nos.setText(u"Nós entre pares: {}".format(num_inter[0]))msg_nos.setText("")ifnum_inter[0]!=num_inter_prev[0]:ifpilha_cmds:UndoRedoHandler.getInstance().undo()pilha_cmds.pop()num_inter_prev[0]=num_inter[0]label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))defmais_iter(e):ifiteracoes[0]<MAX_ITERACOES:iteracoes[0]+=1label_iter.setText(u"Iterações de suavização: {}".format(iteracoes[0]))msg_iter.setText("")ifiteracoes[0]!=iteracoes_prev[0]:aplicar(False)iteracoes_prev[0]=iteracoes[0]else:msg_iter.setText(u"<html><span style='color:red;'>Limite máximo atingido ({})</span></html>".format(MAX_ITERACOES))label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))defmenos_iter(e):ifiteracoes[0]>0:iteracoes[0]-=1label_iter.setText(u"Iterações de suavização: {}".format(iteracoes[0]))msg_iter.setText("")ifiteracoes[0]!=iteracoes_prev[0]:ifpilha_cmds:UndoRedoHandler.getInstance().undo()pilha_cmds.pop()iteracoes_prev[0]=iteracoes[0]label_total_nos.setText(u"Total de nós nas linhas: {}".format(contar_nos(selected_ways)))bot_mais_nos.addActionListener(lambdae:mais_nos(e))bot_menos_nos.addActionListener(lambdae:menos_nos(e))bot_mais_iter.addActionListener(lambdae:mais_iter(e))bot_menos_iter.addActionListener(lambdae:menos_iter(e))aplicar(False)result=JOptionPane.showConfirmDialog(None,panel,"Inserir e suavizar",JOptionPane.OK_CANCEL_OPTION,JOptionPane.PLAIN_MESSAGE)ifresult==JOptionPane.CANCEL_OPTION:whilepilha_cmds:UndoRedoHandler.getInstance().undo()pilha_cmds.pop()returnifnum_inter[0]==0anditeracoes[0]==0:Notification("Nada alterado")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()elifnum_inter[0]>0anditeracoes[0]==0:Notification(u"Nós adicionados")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()elifnum_inter[0]==0anditeracoes[0]>0:Notification(u"Suavização aplicada")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()else:Notification(u"Nós adicionados e suavização aplicada com sucesso!")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()main()