Usava para determinar espaços entre casas criadas com plugin Gridfy, prático para geometria quadradas ou retangulares mas devido a casas terem formatos irregulares, ferramentas de cópias são mais eficientes.
Como funciona?
Crie um retângulo um pouco maior que o total de grupo de casas
Use o pugin Gridify para determinar a quantidade
Faça a seleção de todo grupo
Escolha um tamanho adequado
Marque "Separar geometrias" para removera mesclagem de nós
Demonstração
Imagem.gif, clique para visualizar.
Código
fromorg.openstreetmap.josm.data.osmimportWay,Nodefromorg.openstreetmap.josm.guiimportMainApplication,Notificationfromorg.openstreetmap.josm.data.coorimportLatLonfromorg.openstreetmap.josm.commandimportChangeCommand,DeleteCommand,AddCommand,SequenceCommandfromorg.openstreetmap.josm.data.UndoRedoHandlerimportgetInstancefromjavax.swingimportJOptionPane,JPanel,JLabel,JTextField,JRadioButton,BoxLayout,UIManagerimportmathdefcalcular_distancia_aproximada(n1,n2):lat1=n1.getCoor().lat()lon1=n1.getCoor().lon()lat2=n2.getCoor().lat()lon2=n2.getCoor().lon()dlat=(lat2-lat1)*111320avg_lat=(lat1+lat2)/2.0dlon=(lon2-lon1)*111320*math.cos(math.radians(avg_lat))returnmath.hypot(dlat,dlon)defcalcular_perimetro(nodes):returnsum(calcular_distancia_aproximada(nodes[i],nodes[i+1])foriinrange(len(nodes)-1))defcalcular_centro(nodes):lat_sum=sum(node.getCoor().lat()fornodeinnodes)lon_sum=sum(node.getCoor().lon()fornodeinnodes)returnLatLon(lat_sum/len(nodes),lon_sum/len(nodes))defaplicar_escala(nodes_originais,centro,fator):novos=[]fornodeinnodes_originais:lat_offset=(node.getCoor().lat()-centro.lat())*fatorlon_offset=(node.getCoor().lon()-centro.lon())*fatornovos.append(LatLon(centro.lat()+lat_offset,centro.lon()+lon_offset))returnnovosdefajustar_perimetro_geometrias():dataset=MainApplication.getLayerManager().getEditDataSet()ifnotdataset:Notification(u"Nenhuma camada de edição ativa!")\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()returnselection=dataset.getSelected()ways=[wayforwayinselectionifisinstance(way,Way)andnotway.isDeleted()]ifnotways:Notification(u"Selecione pelo menos uma geometria válida!")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()returnway_nodes={}perimeter_total=0forwayinways:nodes=[nodefornodeinway.getNodes()ifnotnode.isDeleted()]iflen(nodes)<3:Notification(u"A geometria {} precisa ter pelo menos 3 pontos válidos!".format(way.getId()))\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()returnis_closed=nodes[0]==nodes[-1]ifnotis_closed:nodes=list(nodes)+[nodes[0]]way_nodes[way]=nodesperimeter_total+=calcular_perimetro(nodes)panel=JPanel()panel.setLayout(BoxLayout(panel,BoxLayout.Y_AXIS))panel.add(JLabel(u"Perímetro total atual: {:.2f} m".format(perimeter_total)))panel.add(JLabel(u"Digite o novo perímetro desejado:"))campo_valor=JTextField(10)panel.add(campo_valor)radio=JRadioButton("Separar geometrias antes de escalonar")panel.add(radio)aviso=JLabel(u"<html><span style='color:red'>ATENÇÃO! Não marque essa opção em geometrias que não<br>"u"estiverem grudadas, pois o ID dos nós nao será preservado</span></html>")panel.add(aviso)result=JOptionPane.showConfirmDialog(None,panel,u"Ajustar Perímetro",JOptionPane.OK_CANCEL_OPTION)ifresult!=JOptionPane.OK_OPTION:returntry:new_perimeter=float(campo_valor.getText())except:Notification(u"Valor inserido inválido!")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()returnseparar=radio.isSelected()ifperimeter_total==0:Notification(u"Perímetro atual é zero. Não é possível escalar.")\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()returnfator=math.sqrt(new_perimeter/perimeter_total)precisao_alvo=0.01max_iteracoes=20for_inrange(max_iteracoes):total=0forway,nodesinway_nodes.items():centro=calcular_centro(nodes)ifsepararelsecalcular_centro([nfornodes_inway_nodes.values()forninnodes_])novas_coords=aplicar_escala(nodes,centro,fator)total+=calcular_perimetro([Node(coord)forcoordinnovas_coords])erro=new_perimeter-totalifabs(erro)<=precisao_alvo:breakfator*=math.sqrt(new_perimeter/total)all_commands=[]used_nodes=set()node_replacements={}coord_to_node={}centro_comum=calcular_centro([nfornodes_inway_nodes.values()forninnodes_])forwayinway_nodes:nodes=way_nodes[way]is_closed=nodes[0]==nodes[-1]centro=calcular_centro(nodes)ifsepararelsecentro_comumnovas_coords=aplicar_escala(nodes,centro,fator)novos_nos=[]add_node_commands=[]fori,nodeinenumerate(nodes):ifnode.isDeleted():continuecoord=novas_coords[i]coord_key=(round(coord.lat(),9),round(coord.lon(),9))ifseparar:ifcoord_keyincoord_to_node:novo_node=coord_to_node[coord_key]else:novo_node=Node(coord)novo_node.setModified(True)coord_to_node[coord_key]=novo_nodeadd_node_commands.append(AddCommand(dataset,novo_node))novos_nos.append(novo_node)used_nodes.add(novo_node)node_replacements.setdefault(node,[]).append((way,novo_node))else:node_novo=Node(node)node_novo.setCoor(coord)node_novo.setModified(True)all_commands.append(ChangeCommand(dataset,node,node_novo))novos_nos.append(node)used_nodes.add(node)ifis_closedandlen(novos_nos)>1:novos_nos=novos_nos[:-1]+[novos_nos[0]]iflen(novos_nos)<3:Notification(u"A geometria {} resultou em menos de 3 nós válidos!".format(way.getId()))\
.setIcon(UIManager.getIcon("OptionPane.warningIcon"))\
.show()returnifseparar:novo_way=Way(way)novo_way.setNodes(novos_nos)novo_way.setModified(True)all_commands.extend(add_node_commands)all_commands.append(ChangeCommand(dataset,way,novo_way))way_nodes[way]=novos_noselse:way.setModified(True)nodes_to_delete=set()fornodeindataset.getNodes():ifnode.isDeleted():continuereferrers=[rforrinnode.getReferrers()ifnotr.isDeleted()]iflen(referrers)==0andnotnode.getKeys()andnodenotinused_nodesandnode.getId()<0:nodes_to_delete.add(node)elifnodeinnode_replacementsandnotnode.getKeys():selected_referrers=[rforrinreferrersifrinway_nodes]replaced_in_ways=[r[0]forrinnode_replacements[node]]ifset(selected_referrers).issubset(set(replaced_in_ways)):nodes_to_delete.add(node)fornodeinnodes_to_delete:ifnotnode.isDeleted():all_commands.append(DeleteCommand(dataset,node))ifall_commands:getInstance().add(SequenceCommand("Ajustar perímetro das geometrias",all_commands))Notification(u"Geometrias ajustadas com perímetro final de aproximadamente {:.2f} m".format(new_perimeter))\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()# Executarajustar_perimetro_geometrias()