Em conjuntos residenciais é comum encontrar edificações com um formato de H. Criar um retângulo e depis adicionar nós para fazer a extrusão de cavidades pode deixar os lados de diferentes tamanhos, esse script mantém todos os lados simétricos.
Como funciona?
Selecione um polígono com quatro lados
Use as opções para escolher entre 2 ou 4 cavidades
Use os controles de distância para ajustar largura e profundidade
Demonstração
Imagem.gif, clique para visualizar.
Código
fromorg.openstreetmap.josm.guiimportMainApplication,Notificationfromorg.openstreetmap.josm.data.osmimportNode,Wayfromorg.openstreetmap.josm.commandimportSequenceCommand,AddCommand,DeleteCommandfromorg.openstreetmap.josm.data.projectionimportProjectionRegistryfromorg.openstreetmap.josm.data.coorimportEastNorth,LatLonfromorg.openstreetmap.josm.dataimportUndoRedoHandlerfromjavax.swingimportJPanel,JLabel,JButton,JOptionPane,BoxLayout,Box,UIManager,JRadioButton,ButtonGroupfromjava.awtimportDimension,FlowLayoutimportmath# Variáveis globais para os parâmetros do Polígono e estadoPOLY_WIDTH=[0.0]POLY_HEIGHT=[0.0]CAVITY_X_WIDTH=[0.0]CAVITY_Y_WIDTH=[0.0]CAVITY_X_DEPTH=[0.0]CAVITY_Y_DEPTH=[0.0]CAVITY_DIRECTION_OUT=[False]# False = Para Dentro (Padrão), True = Para Fora# 0: 4 Cavidades (Todos), 1: 2 Cavidades (Laterais - Esquerda/Direita), 2: 2 Cavidades (Horizontal - Cima/Baixo)CAVITY_MODE=[0]preview_cmd=[None]delete_cmd_ref=[None]REFERENCE_TAGS={}center_poly=[None]angle_poly=[0.0]# --- Funções Geométricas ---defrotate_point(p,angle_rad):dx=p.getX()dy=p.getY()cos_a=math.cos(angle_rad)sin_a=math.sin(angle_rad)x_new=dx*cos_a-dy*sin_ay_new=dx*sin_a+dy*cos_areturnEastNorth(x_new,y_new)deflatlon_to_en(latlon):returnProjectionRegistry.getProjection().latlon2eastNorth(latlon)defen_to_latlon(en):returnProjectionRegistry.getProjection().eastNorth2latlon(en)defgenerate_geometry(W,H,CW_X,CW_Y,D_X,D_Y,is_outward,cavity_mode):hw=W/2.0hh=H/2.0half_cw_x=CW_X/2.0half_cw_y=CW_Y/2.0d_sign=1.0ifnotis_outwardelse-1.0base_coords=[EastNorth(hw,hh),EastNorth(-hw,hh),EastNorth(-hw,-hh),EastNorth(hw,-hh)]final_coords=[]foriinrange(len(base_coords)):p1=base_coords[i]p2=base_coords[(i+1)%len(base_coords)]final_coords.append(p1)ifcavity_mode==1and(i==0ori==2):continueifcavity_mode==2and(i==1ori==3):continueis_horizontal=(abs(p1.getY()-p2.getY())<1e-9)ifis_horizontal:y_edge=p1.getY()x_mid=(p1.getX()+p2.getX())/2.0x_start=x_mid-half_cw_xx_end=x_mid+half_cw_xify_edge>0:# TOPOy_depth=y_edge-D_Y*d_signelse:# FUNDOy_depth=y_edge+D_Y*d_signifi==2:# Fundo-Esquerda -> Fundo-Direitafinal_coords.append(EastNorth(x_start,y_edge))final_coords.append(EastNorth(x_start,y_depth))final_coords.append(EastNorth(x_end,y_depth))final_coords.append(EastNorth(x_end,y_edge))elifi==0:# Topo-Direita -> Topo-Esquerdafinal_coords.append(EastNorth(x_end,y_edge))final_coords.append(EastNorth(x_end,y_depth))final_coords.append(EastNorth(x_start,y_depth))final_coords.append(EastNorth(x_start,y_edge))else:x_edge=p1.getX()y_mid=(p1.getY()+p2.getY())/2.0y_start=y_mid-half_cw_yy_end=y_mid+half_cw_yifx_edge>0:# DIREITAx_depth=x_edge-D_X*d_signelse:# ESQUERDAx_depth=x_edge+D_X*d_signifi==3:# Fundo-Direita -> Topo-Direitafinal_coords.append(EastNorth(x_edge,y_start))final_coords.append(EastNorth(x_depth,y_start))final_coords.append(EastNorth(x_depth,y_end))final_coords.append(EastNorth(x_edge,y_end))elifi==1:# Topo-Esquerda -> Fundo-Esquerdafinal_coords.append(EastNorth(x_edge,y_end))final_coords.append(EastNorth(x_depth,y_end))final_coords.append(EastNorth(x_depth,y_start))final_coords.append(EastNorth(x_edge,y_start))returnfinal_coords# --- Desenho e Preview ---defdesenhar_preview():ifpreview_cmd[0]:UndoRedoHandler.getInstance().undo()preview_cmd[0]=Nonedataset=MainApplication.getLayerManager().getEditDataSet()ifnotdatasetorcenter_poly[0]isNone:returnW=max(POLY_WIDTH[0],0.1)H=max(POLY_HEIGHT[0],0.1)CW_X=max(CAVITY_X_WIDTH[0],0.1)CW_Y=max(CAVITY_Y_WIDTH[0],0.1)D_X=max(CAVITY_X_DEPTH[0],0.0)D_Y=max(CAVITY_Y_DEPTH[0],0.0)CW_X=min(CW_X,W*0.9)CW_Y=min(CW_Y,H*0.9)ifnotCAVITY_DIRECTION_OUT[0]:D_X=min(D_X,W*0.4)D_Y=min(D_Y,H*0.4)D_X=max(D_X,0.0)D_Y=max(D_Y,0.0)path_base=generate_geometry(W,H,CW_X,CW_Y,D_X,D_Y,CAVITY_DIRECTION_OUT[0],CAVITY_MODE[0])center_en=center_poly[0]angle_rad=angle_poly[0]path_rotated_only=[rotate_point(p,angle_rad)forpinpath_base]path_final=[]forpinpath_rotated_only:x_final=p.getX()+center_en.getX()y_final=p.getY()+center_en.getY()path_final.append(EastNorth(x_final,y_final))commands=[]nodes=[]foreninpath_final:latlon=en_to_latlon(en)node=Node(latlon)node.setModified(True)commands.append(AddCommand(dataset,node))nodes.append(node)way=Way()way.setNodes(nodes+[nodes[0]])way.setModified(True)# aplica tags copiadas do polígono de referênciatry:ifREFERENCE_TAGS:way.putAll(REFERENCE_TAGS)exceptException:fork,vinREFERENCE_TAGS.items():try:way.put(k,v)exceptException:passcommands.append(AddCommand(dataset,way))ifCAVITY_MODE[0]==0:poly_type=u"20 Lados"else:poly_type=u"12 Lados"action_type=u"Extrusão"ifCAVITY_DIRECTION_OUT[0]elseu"Cavidade"cmd=SequenceCommand(u"Preview Polígono {} ({})".format(poly_type,action_type),commands)UndoRedoHandler.getInstance().add(cmd)preview_cmd[0]=cmdtry:map_view=MainApplication.getMapFrame().getMapView()ifmap_view:map_view.repaint()exceptExceptionase:print(u"Erro ao tentar redesenhar o mapa: ",e)# --- Função Main (GUI Completa) ---defmain():dataset=MainApplication.getLayerManager().getEditDataSet()ifnotdataset:Notification(u"Nenhuma camada ativa")\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()returnselection=[pforpindataset.getSelected()ifisinstance(p,Way)andp.isArea()]iflen(selection)!=1:Notification(u"Selecione um polígono fechado como referência (tamanho e centro)")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()returnpoly=selection[0]# calcula centro, ângulo e dimensões com base no polígono atualcoords=[latlon_to_en(n.getCoor())forninpoly.getNodes()]xs=[c.getX()forcincoords]ys=[c.getY()forcincoords]min_x,max_x=min(xs),max(xs)min_y,max_y=min(ys),max(ys)center_poly[0]=EastNorth((min_x+max_x)/2,(min_y+max_y)/2)iflen(poly.getNodes())>=2:dx=coords[1].getX()-coords[0].getX()dy=coords[1].getY()-coords[0].getY()angle_poly[0]=math.atan2(dy,dx)else:angle_poly[0]=0.0ref_width=max_x-min_xref_height=max_y-min_yPOLY_WIDTH[0]=ref_widthPOLY_HEIGHT[0]=ref_height# Valores IniciaisCAVITY_X_WIDTH[0]=ref_width/3.0CAVITY_Y_WIDTH[0]=ref_height/3.0CAVITY_X_DEPTH[0]=ref_width/10.0CAVITY_Y_DEPTH[0]=ref_height/10.0WIDTH_DEFAULT=POLY_WIDTH[0]HEIGHT_DEFAULT=POLY_HEIGHT[0]CWX_DEFAULT=CAVITY_X_WIDTH[0]CWY_DEFAULT=CAVITY_Y_WIDTH[0]DX_DEFAULT=CAVITY_X_DEPTH[0]DY_DEFAULT=CAVITY_Y_DEPTH[0]# COPIA AS TAGS DO POLÍGONO DE REFERÊNCIA (forma segura)globalREFERENCE_TAGSREFERENCE_TAGS={}try:REFERENCE_TAGS=dict(poly.getKeys())exceptException:try:fortinpoly.getTags():REFERENCE_TAGS[t.getKey()]=t.getValue()exceptException:REFERENCE_TAGS={}# PREPARA E EXECUTA EXCLUSÃO DO POLÍGONO DE REFERÊNCIA (polígono + nós órfãos)ds=poly.getDataSet()nodes_to_delete=[nforninpoly.getNodes()iflen(n.getParentWays())==1]primitives=[poly]+nodes_to_deletetry:cmd_delete=DeleteCommand(ds,primitives)exceptException:try:cmd_delete=DeleteCommand(primitives)exceptExceptionase:Notification(u"Erro ao criar DeleteCommand: {}".format(e))\
.setIcon(UIManager.getIcon("OptionPane.errorIcon"))\
.show()returnUndoRedoHandler.getInstance().add(cmd_delete)delete_cmd_ref[0]=cmd_delete# Valores Iniciais para Radio ButtonsCAVITY_DIRECTION_OUT[0]=FalseCAVITY_MODE[0]=0# Padrão: 4 Cavidades# --- Construção da GUI ---panel=JPanel()panel.setLayout(BoxLayout(panel,BoxLayout.Y_AXIS))# Funções internas da GUI (labels, botões, etc.)defupdate_label():label_largura.setText(u"Largura Total (W): {:.2f}".format(POLY_WIDTH[0]))label_altura.setText(u"Altura Total (H): {:.2f}".format(POLY_HEIGHT[0]))label_cavidade_largura_y.setText(u"Larg. Cav. (Dir/Esq - CWY): {:.1f}".format(CAVITY_Y_WIDTH[0]))label_profundidade_x.setText(u"Prof. Cav. (Dir/Esq - DX): {:.1f}".format(CAVITY_X_DEPTH[0]))label_cavidade_largura_x.setText(u"Larg. Cav. (Cima/Baixo - CWX): {:.1f}".format(CAVITY_X_WIDTH[0]))label_profundidade_y.setText(u"Prof. Cav. (Cima/Baixo - DY): {:.1f}".format(CAVITY_Y_DEPTH[0]))defmake_button(text,callback,size=55):btn=JButton(text)btn.setPreferredSize(Dimension(size,30))btn.addActionListener(callback)returnbtndefchange_float(param,delta,min_val=0.0):new_val=param[0]+deltais_outward=CAVITY_DIRECTION_OUT[0]ifparamisCAVITY_X_WIDTH:max_val=POLY_WIDTH[0]*0.9new_val=min(new_val,max_val)elifparamisCAVITY_Y_WIDTH:max_val=POLY_HEIGHT[0]*0.9new_val=min(new_val,max_val)elifparamisCAVITY_X_DEPTHandnotis_outward:max_val=POLY_WIDTH[0]*0.4new_val=min(new_val,max_val)elifparamisCAVITY_Y_DEPTHandnotis_outward:max_val=POLY_HEIGHT[0]*0.4new_val=min(new_val,max_val)param[0]=max(min_val,round(new_val,1))update_label()desenhar_preview()defhandle_direction_change(e):CAVITY_DIRECTION_OUT[0]=e.getSource().getText()==u"Para Fora (Extrusão)"desenhar_preview()defhandle_count_change(e):selected_text=e.getSource().getText()ifselected_text==u"4 Cavidades (Todos os Lados)":CAVITY_MODE[0]=0elifselected_text==u"2 Cavidades (Lados Direito/Esquerdo)":CAVITY_MODE[0]=1elifselected_text==u"2 Cavidades (Cima/Baixo)":CAVITY_MODE[0]=2desenhar_preview()# --- Seção 0: Opções (Radio Buttons) ---panel.add(JLabel("---------------"))direction_panel=JPanel()direction_panel.setLayout(BoxLayout(direction_panel,BoxLayout.Y_AXIS))title_panel=JPanel(FlowLayout(FlowLayout.LEFT))title_panel.add(JLabel(u"**Direção:**"))direction_panel.add(title_panel)direction_group=ButtonGroup()rb_inward=JRadioButton(u"Para Dentro (Cavidade)",notCAVITY_DIRECTION_OUT[0])rb_outward=JRadioButton(u"Para Fora (Extrusão)",CAVITY_DIRECTION_OUT[0])rb_inward.addActionListener(handle_direction_change)rb_outward.addActionListener(handle_direction_change)direction_group.add(rb_inward)direction_group.add(rb_outward)direction_panel.add(rb_inward)direction_panel.add(rb_outward)panel.add(direction_panel)count_panel=JPanel()count_panel.setLayout(BoxLayout(count_panel,BoxLayout.Y_AXIS))title_panel=JPanel(FlowLayout(FlowLayout.LEFT))title_panel.add(JLabel(u"**Contagem:**"))count_panel.add(title_panel)count_group=ButtonGroup()rb_4cavity=JRadioButton(u"4 Cavidades (Todos os Lados)",CAVITY_MODE[0]==0)rb_2cavity_vert=JRadioButton(u"2 Cavidades (Lados Direito/Esquerdo)",CAVITY_MODE[0]==1)rb_2cavity_horiz=JRadioButton(u"2 Cavidades (Cima/Baixo)",CAVITY_MODE[0]==2)rb_4cavity.addActionListener(handle_count_change)rb_2cavity_vert.addActionListener(handle_count_change)rb_2cavity_horiz.addActionListener(handle_count_change)count_group.add(rb_4cavity)count_group.add(rb_2cavity_vert)count_group.add(rb_2cavity_horiz)count_panel.add(rb_4cavity)count_panel.add(rb_2cavity_vert)count_panel.add(rb_2cavity_horiz)panel.add(count_panel)panel.add(JLabel("-----------------------------------------------"))globallabel_larguralabel_largura=JLabel()row1=JPanel()row1.setLayout(BoxLayout(row1,BoxLayout.X_AXIS))row1.add(label_largura)row1.add(Box.createHorizontalGlue())b1=JPanel(FlowLayout(FlowLayout.RIGHT,5,0))b1.add(make_button("-0.5",lambdae:change_float(POLY_WIDTH,-0.5,0.1)))b1.add(make_button("+0.5",lambdae:change_float(POLY_WIDTH,0.5,0.1)))row1.add(b1)panel.add(row1)globallabel_alturalabel_altura=JLabel()row2=JPanel()row2.setLayout(BoxLayout(row2,BoxLayout.X_AXIS))row2.add(label_altura)row2.add(Box.createHorizontalGlue())b2=JPanel(FlowLayout(FlowLayout.RIGHT,5,0))b2.add(make_button("-0.5",lambdae:change_float(POLY_HEIGHT,-0.5,0.1)))b2.add(make_button("+0.5",lambdae:change_float(POLY_HEIGHT,0.5,0.1)))row2.add(b2)panel.add(row2)panel.add(Box.createRigidArea(Dimension(0,5)))panel.add(JLabel("-----------------------------------------------"))panel.add(Box.createRigidArea(Dimension(0,5)))globallabel_cavidade_largura_ylabel_cavidade_largura_y=JLabel()row4=JPanel()row4.setLayout(BoxLayout(row4,BoxLayout.X_AXIS))row4.add(label_cavidade_largura_y)row4.add(Box.createHorizontalGlue())b4=JPanel(FlowLayout(FlowLayout.RIGHT,5,0))b4.add(make_button("-0.5",lambdae:change_float(CAVITY_Y_WIDTH,-0.5,0.1)))b4.add(make_button("+0.5",lambdae:change_float(CAVITY_Y_WIDTH,0.5,0.1)))row4.add(b4)panel.add(row4)globallabel_profundidade_xlabel_profundidade_x=JLabel()row5=JPanel()row5.setLayout(BoxLayout(row5,BoxLayout.X_AXIS))row5.add(label_profundidade_x)row5.add(Box.createHorizontalGlue())b5=JPanel(FlowLayout(FlowLayout.RIGHT,5,0))b5.add(make_button("-0.5",lambdae:change_float(CAVITY_X_DEPTH,-0.5,0.0)))b5.add(make_button("+0.5",lambdae:change_float(CAVITY_X_DEPTH,0.5,0.0)))row5.add(b5)panel.add(row5)panel.add(Box.createRigidArea(Dimension(0,5)))panel.add(JLabel("-----------------------------------------------"))panel.add(Box.createRigidArea(Dimension(0,5)))globallabel_cavidade_largura_xlabel_cavidade_largura_x=JLabel()row3=JPanel()row3.setLayout(BoxLayout(row3,BoxLayout.X_AXIS))row3.add(label_cavidade_largura_x)row3.add(Box.createHorizontalGlue())b3=JPanel(FlowLayout(FlowLayout.RIGHT,5,0))b3.add(make_button("-0.5",lambdae:change_float(CAVITY_X_WIDTH,-0.5,0.1)))b3.add(make_button("+0.5",lambdae:change_float(CAVITY_X_WIDTH,0.5,0.1)))row3.add(b3)panel.add(row3)globallabel_profundidade_ylabel_profundidade_y=JLabel()row6=JPanel()row6.setLayout(BoxLayout(row6,BoxLayout.X_AXIS))row6.add(label_profundidade_y)row6.add(Box.createHorizontalGlue())b6=JPanel(FlowLayout(FlowLayout.RIGHT,5,0))b6.add(make_button("-0.5",lambdae:change_float(CAVITY_Y_DEPTH,-0.5,0.0)))b6.add(make_button("+0.5",lambdae:change_float(CAVITY_Y_DEPTH,0.5,0.0)))row6.add(b6)panel.add(row6)panel.add(Box.createRigidArea(Dimension(0,10)))btn_reset=JButton(u"Resetar Dimensões")btn_reset.setAlignmentX(0.5)defreset_vals(e):POLY_WIDTH[0]=WIDTH_DEFAULTPOLY_HEIGHT[0]=HEIGHT_DEFAULTCAVITY_X_WIDTH[0]=CWX_DEFAULTCAVITY_Y_WIDTH[0]=CWY_DEFAULTCAVITY_X_DEPTH[0]=DX_DEFAULTCAVITY_Y_DEPTH[0]=DY_DEFAULTCAVITY_DIRECTION_OUT[0]=FalseCAVITY_MODE[0]=0rb_inward.setSelected(True)rb_4cavity.setSelected(True)update_label()desenhar_preview()btn_reset.addActionListener(reset_vals)panel.add(btn_reset)update_label()desenhar_preview()result=JOptionPane.showConfirmDialog(None,panel,u"Desenhar Polígono Modulado (12 ou 20 Lados)",JOptionPane.OK_CANCEL_OPTION,JOptionPane.PLAIN_MESSAGE,)# Finalização: tratar resultadoifresult==JOptionPane.CANCEL_OPTIONorresult==JOptionPane.CLOSED_OPTION:# desfaz preview (se existir)ifpreview_cmd[0]:UndoRedoHandler.getInstance().undo()preview_cmd[0]=None# desfaz deleção do polígono original (se existir)ifdelete_cmd_ref[0]:UndoRedoHandler.getInstance().undo()delete_cmd_ref[0]=NoneNotification(u"Operação cancelada")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()returnpreview_cmd[0]=Nonedelete_cmd_ref[0]=NoneNotification(u"Polígono modulado criado com sucesso")\
.setIcon(UIManager.getIcon("OptionPane.informationIcon"))\
.show()main()