This commit is contained in:
2026-02-15 20:23:52 +01:00
parent 4476afc1a2
commit 111df89033
10 changed files with 552 additions and 75 deletions

View File

@@ -43,53 +43,74 @@ class OSMImporter:
self.ssl_context = ssl.create_default_context(cafile=certifi.where())
def transform_from_latlon(self, coordinates):
"""Transforma coordenadas lat/lon a coordenadas FreeCAD"""
if not coordinates:
return []
points = ImportElevation.getElevationFromOE(coordinates)
pts = [FreeCAD.Vector(p.x, p.y, p.z).sub(self.Origin) for p in points]
return pts
def get_osm_data(self, bbox):
query = f"""
[out:xml][bbox:{bbox}];
(
way["building"];
way["highway"];
way["railway"];
way["power"="line"];
way["power"="substation"];
way["natural"="water"];
way["landuse"="forest"];
node["natural"="tree"];
);
(._;>;);
out body;
"""
req = urllib.request.Request(
self.overpass_url,
data=query.encode('utf-8'),
headers={'User-Agent': 'FreeCAD-OSM-Importer/1.0'},
method='POST'
)
return urllib.request.urlopen(req, context=self.ssl_context, timeout=160).read()
""" Obtiene datos de OpenStreetMap """
# Modificar la consulta en get_osm_data para incluir más tipos de agua:
query = f"""[out:xml][bbox:{bbox}];
(
way["building"];
way["highway"];
way["railway"];
way["power"="line"];
way["power"="substation"];
way["natural"="water"];
way["waterway"];
way["waterway"="river"];
way["waterway"="stream"];
way["waterway"="canal"];
way["landuse"="basin"];
way["landuse"="reservoir"];
node["natural"="tree"];
way["landuse"="forest"];
way["landuse"="farmland"];
);
(._;>;);
out body;
"""
try:
req = urllib.request.Request(
self.overpass_url,
data=query.encode('utf-8'),
#headers={'User-Agent': 'FreeCAD-OSM-Importer/1.0'},
method='POST'
)
response = urllib.request.urlopen(req, context=self.ssl_context, timeout=160)
return response.read()
except Exception as e:
print(f"Error obteniendo datos OSM: {str(e)}")
return None
def create_layer(self, name):
"""Crea o obtiene una capa en el documento"""
if not FreeCAD.ActiveDocument.getObject(name):
return FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", name)
return FreeCAD.ActiveDocument.getObject(name)
def process_osm_data(self, osm_data):
"""Procesa los datos XML de OSM"""
if not osm_data:
print("No hay datos OSM para procesar")
return
root = ET.fromstring(osm_data)
# Primero, recolectar todos los nodos
print(f"Procesando {len(root.findall('node'))} nodos...")
# Almacenar nodos transformados
coordinates = [[float(node.attrib['lat']), float(node.attrib['lon'])] for node in root.findall('node')]
coordinates = self.transform_from_latlon(coordinates)
for i, node in enumerate(root.findall('node')):
self. nodes[node.attrib['id']] = coordinates[i]
'''return
for node in root.findall('node'):
self.nodes[node.attrib['id']] = self.transform_from_latlon(
float(node.attrib['lat']),
float(node.attrib['lon'])
)'''
self.nodes[node.attrib['id']] = coordinates[i]
# Procesar ways
for way in root.findall('way'):
@@ -166,7 +187,7 @@ class OSMImporter:
def create_buildings(self):
building_layer = self.create_layer("Buildings")
for way_id, data in self.ways_data.items():
print(data)
#print(data)
if 'building' not in data['tags']:
continue
@@ -226,11 +247,11 @@ 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)
#print("\n\n")
#print(tags)
feature_type = tags['power']
if feature_type == 'line':
print("3.1. Create Power Lines")
#print("3.1. Create Power Lines")
FreeCADGui.updateGui()
self.create_power_line(
nodes=nodes,
@@ -239,7 +260,7 @@ class OSMImporter:
)
elif feature_type == 'substation':
print("3.1. Create substations")
#print("3.1. Create substations")
FreeCADGui.updateGui()
self.create_substation(
way_id=way_id,
@@ -249,7 +270,7 @@ class OSMImporter:
)
elif feature_type == 'tower':
print("3.1. Create power towers")
#print("3.1. Create power towers")
FreeCADGui.updateGui()
self.create_power_tower(
position=nodes[0] if nodes else None,
@@ -562,13 +583,15 @@ class OSMImporter:
if polygon_points[0] != polygon_points[-1]:
polygon_points.append(polygon_points[0])
# 3. Base del terreno
base_height = 0.3
try:
base_shape = Part.makePolygon(polygon_points)
base_face = Part.Face(base_shape)
base_extrude = base_face.extrude(FreeCAD.Vector(0, 0, base_height))
base_obj = layer.addObject("Part::Feature", f"{name}_Base")
base_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", f"{name}_Base")
layer.addObject(base_obj)
base_obj.Shape = base_extrude
base_obj.ViewObject.ShapeColor = (0.2, 0.2, 0.2)
except Exception as e:
@@ -583,7 +606,8 @@ class OSMImporter:
fence_shape = Part.makePolygon(fence_points)
fence_face = Part.Face(fence_shape)
fence_extrude = fence_face.extrude(FreeCAD.Vector(0, 0, 2.8))
fence_obj = layer.addObject("Part::Feature", f"{name}_Fence")
fence_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", f"{name}_Fence")
layer.addObject(fence_obj)
fence_obj.Shape = fence_extrude
fence_obj.ViewObject.ShapeColor = (0.4, 0.4, 0.4)
except Exception as e:
@@ -599,14 +623,15 @@ class OSMImporter:
building_shape = Part.makePolygon(building_points)
building_face = Part.Face(building_shape)
building_extrude = building_face.extrude(FreeCAD.Vector(0, 0, building_height))
building_obj = layer.addObject("Part::Feature", f"{name}_Building")
building_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", f"{name}_Building")
layer.addObject(building_obj)
building_obj.Shape = building_extrude
building_obj.ViewObject.ShapeColor = (0.7, 0.7, 0.7)
except Exception as e:
FreeCAD.Console.PrintWarning(f"Error edificio {way_id}: {str(e)}\n")
# 6. Transformadores
try:
'''try:
num_transformers = int(tags.get('transformers', 1))
for i in range(num_transformers):
transformer_pos = self.calculate_equipment_position(
@@ -618,11 +643,11 @@ class OSMImporter:
transformer = self.create_transformer(
position=transformer_pos,
voltage=voltage,
tech_type=tags.get('substation:type', 'outdoor')
technology=tags.get('substation:type', 'outdoor')
)
layer.addObject(transformer)
except Exception as e:
FreeCAD.Console.PrintWarning(f"Error transformadores {way_id}: {str(e)}\n")
FreeCAD.Console.PrintWarning(f"Error transformadores {way_id}: {str(e)}\n")'''
# 7. Torre de seccionamiento para alta tensión
if substation_type == 'transmission' and voltage >= 110000:
@@ -637,7 +662,8 @@ class OSMImporter:
FreeCAD.Console.PrintWarning(f"Error torre {way_id}: {str(e)}\n")
# 8. Propiedades técnicas
substation_data = layer.addObject("App::FeaturePython", f"{name}_Data")
substation_data = FreeCAD.ActiveDocument.addObject("App::FeaturePython", f"{name}_Data")
layer.addObject(substation_data)
props = {
"Voltage": voltage,
"Type": substation_type,
@@ -651,7 +677,8 @@ class OSMImporter:
else:
substation_data.addProperty(
"App::PropertyFloat" if isinstance(value, float) else "App::PropertyString",
prop, "Technical").setValue(value)
prop, "Technical")
setattr(substation_data, prop, value)
except Exception as e:
FreeCAD.Console.PrintError(f"Error crítico en subestación {way_id}: {str(e)}\n")
@@ -900,9 +927,9 @@ class OSMImporter:
if face.isInside(rand_point, 0.1, True):
return rand_point
def create_water_bodies(self):
def create_water_bodies_old(self):
water_layer = self.create_layer("Water")
print(self.ways_data)
for way_id, data in self.ways_data.items():
if 'natural' in data['tags'] and data['tags']['natural'] == 'water':
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
@@ -915,3 +942,80 @@ class OSMImporter:
water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin )
water.ViewObject.ShapeColor = self.feature_colors['water']
def create_water_bodies(self):
water_layer = self.create_layer("Water")
for way_id, data in self.ways_data.items():
tags = data['tags']
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
if len(nodes) < 2:
continue
# ===== 1) RÍOS / CANALES (líneas) =====
name = self.get_osm_name(tags, tags["waterway"])
if 'waterway' in tags:
if len(nodes) < 2:
continue
try:
width = self.parse_width(tags, default=2.0)
points = [FreeCAD.Vector(n.x, n.y, n.z) for n in nodes]
wire = Draft.make_wire(points, closed=False, face=False)
wire.Label = f"{name} ({tags['waterway']})"
wire.ViewObject.LineWidth = max(1, int(width * 0.5))
wire.ViewObject.ShapeColor = self.feature_colors['water']
water_layer.addObject(wire)
except Exception as e:
print(f"Error creando waterway {way_id}: {e}")
continue # importante
# ===== 2) LAGOS / EMBALSES (polígonos) =====
is_area_water = (
tags.get('natural') == 'water' or
tags.get('landuse') in ['reservoir', 'basin'] or
tags.get('water') is not None
)
if not is_area_water or len(nodes) < 3:
continue
try:
polygon_points = [FreeCAD.Vector(n.x, n.y, n.z) for n in nodes]
if polygon_points[0] != polygon_points[-1]:
polygon_points.append(polygon_points[0])
polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon)
water = FreeCAD.ActiveDocument.addObject("Part::Feature", f"Water_{way_id}")
water.Shape = face
water.ViewObject.ShapeColor = self.feature_colors['water']
water.Label = f"{name} ({tags['waterway']})"
water_layer.addObject(water)
except Exception as e:
print(f"Error creando área de agua {way_id}: {e}")
def get_osm_name(self, tags, fallback=""):
for key in ["name", "name:es", "name:en", "alt_name", "ref"]:
if key in tags and tags[key].strip():
return tags[key]
return fallback
def parse_width(self, tags, default=2.0):
for key in ["width", "est_width"]:
if key in tags:
try:
w = tags[key].replace("m", "").strip()
return float(w)
except:
pass
return default