This commit is contained in:
2025-06-15 23:10:17 +02:00
parent 5dd8869caf
commit 74bf60101c
16 changed files with 426 additions and 2577 deletions

View File

@@ -92,10 +92,24 @@ class OSMImporter:
'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
}
print("1. Create Transportations")
FreeCADGui.updateGui()
self.create_transportation()
print("2. Create Buildings")
FreeCADGui.updateGui()
self.create_buildings()
print("3. Create Power Infrastructure")
FreeCADGui.updateGui()
self.create_power_infrastructure()
print("4. Create Vegetation")
FreeCADGui.updateGui()
self.create_vegetation()
print("5. Create Water Bodies")
FreeCADGui.updateGui()
self.create_water_bodies()
def create_transportation(self):
@@ -118,27 +132,27 @@ class OSMImporter:
'secondary': 5.0,
'tertiary': 4.0
}.get(highway_type, 3.0)
self.create_road(nodes, width, highway_type, transport_layer)
self.create_road(nodes, width, highway_type, transport_layer, tags['name'] if 'name' in tags else "")
# Vías férreas
if 'railway' in tags:
self.create_railway(nodes, transport_layer)
self.create_railway(nodes, transport_layer, tags['name'] if 'name' in tags else "")
def create_road(self, nodes, width, road_type, layer):
def create_road(self, nodes, width, road_type, layer, name=""):
points = [n for n in nodes]
polyline = Draft.make_wire(points, closed=False, face=False)
polyline.Label = f"Road_{road_type}"
polyline.Label = f"Road_{name if (name != '') else road_type}"
polyline.ViewObject.LineWidth = 2.0
polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5))
polyline.addProperty("App::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
layer.addObject(polyline)
def create_railway(self, nodes, layer):
def create_railway(self, nodes, layer, name=""):
points = [n for n in nodes]
rail_line = Draft.make_wire(points, closed=False, face=False)
rail_line.Label = "Railway"
rail_line.Label = f"{name if (name != '') else 'Railway'}"
rail_line.ViewObject.LineWidth = 1.5
rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
layer.addObject(rail_line)
@@ -149,6 +163,8 @@ class OSMImporter:
if 'building' not in data['tags']:
continue
print(data)
tags = data['tags']
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
@@ -205,9 +221,12 @@ class OSMImporter:
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
if 'power' in tags:
print("\n\n")
print(tags)
feature_type = tags['power']
if feature_type == 'line':
print("3.1. Create Power Lines")
FreeCADGui.updateGui()
self.create_power_line(
nodes=nodes,
tags=tags,
@@ -215,6 +234,8 @@ class OSMImporter:
)
elif feature_type == 'substation':
print("3.1. Create substations")
FreeCADGui.updateGui()
self.create_substation(
way_id=way_id,
tags=tags,
@@ -223,11 +244,17 @@ class OSMImporter:
)
elif feature_type == 'tower':
print("3.1. Create power towers")
FreeCADGui.updateGui()
self.create_power_tower(
position=nodes[0] if nodes else None,
tags=tags,
layer=power_layer
)
'''self.create_power_tower_1(
way=way_id,
layer=power_layer
)'''
def create_power_line(self, nodes, tags, layer):
"""Crea líneas de transmisión eléctrica con propiedades técnicas"""
@@ -244,10 +271,10 @@ class OSMImporter:
return
wire = Draft.make_wire(points, closed=False, face=False)
wire.Label = f"Power_Line_{voltage}V"
wire.Label = f"Power_Line_{int(voltage/1000000)}kV"
# Propiedades visuales
wire.ViewObject.LineWidth = 1 + (voltage / 100000)
wire.ViewObject.LineWidth = 6 #'''1 + (voltage / 100000)'''
color = self.feature_colors['power']['line']
wire.ViewObject.ShapeColor = color
@@ -260,7 +287,7 @@ class OSMImporter:
layer.addObject(wire)
# Añadir torres si es overhead
if line_type == 'overhead':
'''if line_type == 'overhead':
distance_between_towers = 150 # metros por defecto
if 'distance_between_towers' in tags:
try:
@@ -273,7 +300,7 @@ class OSMImporter:
voltage=voltage,
distance=distance_between_towers,
layer=layer
)
)'''
except Exception as e:
FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")
@@ -318,7 +345,8 @@ class OSMImporter:
# Unir componentes
tower = base.fuse(mast)
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Power_Tower")
layer.addObject(tower_obj)
tower_obj.Shape = tower
tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']
@@ -354,6 +382,164 @@ class OSMImporter:
cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
layer.addObject(cable)
def create_power_tower_1(self, way, layer):
"""Crea una torre eléctrica según especificaciones OSM"""
tags = way['tags']
nodes = [self.nodes[ref] for ref in way['nodes'] if ref in self.nodes]
if not nodes:
return
try:
# Obtener parámetros principales
structure_type = tags.get('structure', 'lattice')
material = tags.get('material', 'steel')
height = float(tags.get('height', 30.0)) # Altura en metros
voltage = self.parse_voltage(tags.get('voltage', '132000')) # 132kV por defecto
lines = int(tags.get('lines', '3'))
operator = tags.get('operator', '')
tower_name = tags.get('name', f"Tower_{way['id']}")
# Calcular posición (usar primer nodo)
position = FreeCAD.Vector(*nodes[0])
# Configurar dimensiones basadas en parámetros
base_size = self.calculate_base_size(structure_type, height)
crossarm_length = self.calculate_crossarm_length(voltage, lines)
color = self.get_material_color(material)
# Crear geometría según tipo de estructura
if structure_type == 'lattice':
tower = self.create_lattice_tower(height, base_size, crossarm_length)
elif structure_type == 'tubular':
tower = self.create_tubular_tower(height, base_size, crossarm_length)
elif structure_type == 'portal':
tower = self.create_portal_tower(height, base_size, crossarm_length)
else:
tower = self.create_default_tower(height, base_size, crossarm_length)
# Crear objeto en FreeCAD
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
tower_obj.Shape = tower
tower_obj.ViewObject.ShapeColor = color
tower_obj.Placement.Base = position
# Añadir propiedades técnicas
tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Voltaje nominal (V)").Voltage = voltage
tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Altura total (m)").Height = height
tower_obj.addProperty("App::PropertyString", "StructureType", "Technical",
"Tipo de estructura").StructureType = structure_type
tower_obj.addProperty("App::PropertyString", "Material", "Technical",
"Material de construcción").Material = material
tower_obj.addProperty("App::PropertyInteger", "Lines", "Technical", "Número de circuitos").Lines = lines
if operator:
tower_obj.addProperty("App::PropertyString", "Operator", "General", "Operador").Operator = operator
# Añadir cables si existen nodos de conexión
if len(nodes) >= 2:
connection_points = [FreeCAD.Vector(*n) for n in nodes]
self.create_power_lines_between_towers(connection_points, voltage, layer)
except Exception as e:
FreeCAD.Console.PrintError(f"Error creando torre {way['id']}: {str(e)}\n")
def create_lattice_tower(self, height, base_size, crossarm_length):
"""Crea torre de celosía tipo armazón"""
# Base
base = Part.makeBox(base_size, base_size, 3.0)
# Patas principales
leg_profile = Part.makeBox(0.5, 0.5, height)
legs = []
for x in [-base_size / 2, base_size / 2]:
for y in [-base_size / 2, base_size / 2]:
leg = leg_profile.copy()
leg.translate(FreeCAD.Vector(x, y, 3.0))
legs.append(leg)
# Travesaños horizontales
horizontal_bars = []
for z in [10.0, height / 2, height - 5.0]:
bar = Part.makeBox(base_size + 1.0, 0.3, 0.3, FreeCAD.Vector(-(base_size + 1) / 2, -0.15, z))
horizontal_bars.append(bar)
# Crucetas
crossarms = self.create_crossarms(height, crossarm_length)
# Unir todas las partes
tower = base.multiFuse(legs + horizontal_bars + crossarms)
return tower.removeSplitter()
def create_crossarms(self, height, length):
"""Crea crucetas para líneas eléctricas"""
crossarms = []
positions = [
(height - 5.0, 0), # Superior
(height - 15.0, 22.5), # Media con ángulo
(height - 25.0, -22.5) # Inferior con ángulo
]
for z, angle in positions:
crossarm = Part.makeBox(length, 0.3, 0.3)
crossarm.rotate(FreeCAD.Vector(0, 0, z), FreeCAD.Vector(0, 0, 1), angle)
crossarm.translate(FreeCAD.Vector(-length / 2, -0.15, z))
crossarms.append(crossarm)
return crossarms
def calculate_base_size(self, structure_type, height):
"""Calcula tamaño de base según tipo de estructura y altura"""
base_sizes = {
'lattice': 4.0 + (height * 0.1),
'tubular': 3.0 + (height * 0.05),
'portal': 6.0,
'default': 3.0
}
return base_sizes.get(structure_type, base_sizes['default'])
def calculate_crossarm_length(self, voltage, lines):
"""Calcula longitud de crucetas según voltaje y número de circuitos"""
return (voltage / 100000) * 8.0 + (lines * 2.0)
def get_material_color(self, material):
"""Devuelve color según material de construcción"""
colors = {
'steel': (0.65, 0.65, 0.7),
'concrete': (0.8, 0.8, 0.8),
'wood': (0.5, 0.3, 0.2),
'aluminum': (0.9, 0.9, 0.9),
'default': (0.5, 0.5, 0.5)
}
return colors.get(material.lower(), colors['default'])
def create_power_lines_between_towers(self, points, voltage, layer):
"""Crea cables entre torres"""
cable_thickness = 0.1 + (voltage / 500000)
for i in range(len(points) - 1):
start = points[i]
end = points[i + 1]
# Crear cable curvo (catenaria)
cable = self.create_catenary(start, end, sag=5.0)
cable_obj = layer.addObject("Part::Feature", f"Power_Line_{i}")
cable_obj.Shape = cable
cable_obj.ViewObject.LineWidth = cable_thickness * 1000 # Convertir a mm
cable_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)
def create_catenary(self, start, end, sag=5.0):
"""Crea curva de catenaria para cables eléctricos"""
mid_point = (start + end) / 2
mid_point.z -= sag
arc = Part.Arc(
start,
mid_point,
end
)
return Part.Edge(arc.toShape())
def create_substation(self, way_id, tags, nodes, layer):
"""Crea subestaciones con todos los componentes detallados"""
try: