Files
PVPlant/Importer/importOSM.py

285 lines
11 KiB
Python
Raw Normal View History

2025-03-11 21:20:18 +01:00
import FreeCAD
import Part
2025-03-28 19:39:06 +06:00
import Draft
2025-03-11 21:20:18 +01:00
from xml.etree import ElementTree as ET
2025-03-28 19:39:06 +06:00
import ssl
import certifi
2025-03-11 21:20:18 +01:00
import urllib.request
2025-03-28 19:39:06 +06:00
import math
import utm
from collections import defaultdict
scale = 1000.0
class OSMImporter:
def __init__(self, origin):
self.Origin = origin
if origin is None:
self.Origin = FreeCAD.Vector(0, 0, 0)
self.overpass_url = "https://overpass-api.de/api/interpreter"
self.nodes = {}
self.ways_data = defaultdict(dict)
self.feature_colors = {
'building': (0.8, 0.8, 0.6),
'highway': {
'motorway': (1.0, 0.4, 0.4),
'trunk': (1.0, 0.6, 0.4),
'primary': (1.0, 0.8, 0.4),
'secondary': (1.0, 1.0, 0.4),
'tertiary': (0.8, 1.0, 0.4),
'residential': (0.6, 0.6, 0.6)
},
'railway': (0.4, 0.4, 0.4),
'power': {
'line': (0.0, 0.0, 0.0),
'tower': (0.3, 0.3, 0.3),
'substation': (0.8, 0.0, 0.0)
},
'vegetation': (0.4, 0.8, 0.4),
'water': (0.4, 0.6, 1.0)
}
def transformFromLatLon(self, lat, lon):
x, y, _, _ = utm.from_latlon(lat, lon)
return (x, y, 0) * 1000
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;
"""
# Configurar contexto SSL seguro
ssl_context = ssl.create_default_context(cafile=certifi.where())
# Modificar tu código de descarga
response = urllib.request.urlopen(
self.overpass_url,
data=query.encode('utf-8'),
context=ssl_context,
timeout=30
)
return response.read()
def create_layer(self, name):
if not FreeCAD.ActiveDocument.getObject(name):
layer = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", name)
return layer
return FreeCAD.ActiveDocument.getObject(name)
def process_osm_data(self, osm_data):
root = ET.fromstring(osm_data)
# Primera pasada: almacenar todos los nodos
for node in root.findall('node'):
'''self.nodes[node.attrib['id']] = (
float(node.attrib['lon']),
float(node.attrib['lat']),
0)'''
self.nodes[node.attrib['id']] = self.transformFromLatLon(
float(node.attrib['lat']),
float(node.attrib['lon'])
)
# Segunda pasada: procesar ways y relaciones
for way in root.findall('way'):
tags = {tag.attrib['k']: tag.attrib['v'] for tag in way.findall('tag')}
nodes = [nd.attrib['ref'] for nd in way.findall('nd')]
self.ways_data[way.attrib['id']] = {'tags': tags, 'nodes': nodes}
self.create_transportation()
self.create_buildings()
self.create_power_infrastructure()
self.create_vegetation()
self.create_water_bodies()
def create_transportation(self):
transport_layer = self.create_layer("Transport")
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
# Carreteras
if 'highway' in tags:
highway_type = tags['highway']
width = {
'motorway': 10.0,
'trunk': 8.0,
'primary': 6.0,
'secondary': 5.0,
'tertiary': 4.0
}.get(highway_type, 3.0)
self.create_road(nodes, width, highway_type, transport_layer)
# Vías férreas
if 'railway' in tags:
self.create_railway(nodes, transport_layer)
def create_road(self, nodes, width, road_type, layer):
points = [FreeCAD.Vector(n[0], n[1], .0) * scale - self.Origin for n in nodes]
polyline = Draft.make_wire(points, closed=False, face=False)
polyline.Label = f"Road_{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):
points = [FreeCAD.Vector(n[0], n[1], .0) * scale - self.Origin for n in nodes]
rail_line = Draft.make_wire(points, closed=False, face=False)
rail_line.Label = "Railway"
rail_line.ViewObject.LineWidth = 1.5
rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
layer.addObject(rail_line)
def create_buildings(self):
building_layer = self.create_layer("Buildings")
for way_id, data in self.ways_data.items():
if 'building' not in data['tags']:
continue
tags = data['tags']
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
if len(nodes) < 3:
continue
# Calcular altura
height = self.get_building_height(tags)
# Crear polígono base
polygon_points = [FreeCAD.Vector(n[0], n[1], .0) * scale - self.Origin for n in nodes]
if polygon_points[0] != polygon_points[-1]:
polygon_points.append(polygon_points[0])
2025-03-11 21:20:18 +01:00
try:
2025-03-28 19:39:06 +06:00
polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon)
extruded = face.extrude(FreeCAD.Vector(0, 0, height) * scale - self.Origin )
building = building_layer.addObject("Part::Feature", f"Building_{way_id}")
building.Shape = extruded
building.Label = f"Building ({height}m)"
building.ViewObject.ShapeColor = self.feature_colors['building']
# Metadatos
building.addProperty("App::PropertyFloat", "Height", "Metadata", "Altura del edificio").Height = height
if 'building:levels' in tags:
building.addProperty("App::PropertyInteger", "Levels", "Metadata",
"Niveles del edificio").Levels = int(tags['building:levels'])
2025-03-11 21:20:18 +01:00
except Exception as e:
2025-03-28 19:39:06 +06:00
print(f"Error en edificio {way_id}: {str(e)}")
def get_building_height(self, tags):
# Lógica de cálculo de altura
if 'height' in tags:
try:
return float(tags['height'].split()[0])
except:
pass
if 'building:levels' in tags:
try:
return float(tags['building:levels']) * 3.0
except:
pass
return 5.0 # Altura por defecto
def create_power_infrastructure(self):
power_layer = self.create_layer("Power")
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 'power' in tags:
if tags['power'] == 'line':
self.create_power_line(nodes, power_layer)
elif tags['power'] == 'substation':
self.create_substation(nodes, power_layer)
def create_power_line(self, nodes, layer):
# Torres de alta tensión
for node in nodes:
cylinder = Part.makeCylinder(1.0, 20.0, FreeCAD.Vector(node[0], node[1], 0) * scale - self.Origin )
pole = FreeCAD.ActiveDocument.addObject("Part::Feature", "PowerPole")
layer.addObject(pole)
pole.Shape = cylinder
pole.ViewObject.ShapeColor = self.feature_colors['power']['tower']
# Líneas eléctricas
points = [FreeCAD.Vector(n[0], n[1], .0) * scale - self.Origin for n in nodes]
cable = Draft.make_wire(points, closed=False, face=False)
cable.ViewObject.LineWidth = 3.0
cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
layer.addObject(cable)
def create_substation(self, nodes, layer):
# Crear área de subestación
polygon_points = [FreeCAD.Vector(n[0], n[1], 0) * scale - self.Origin for n in nodes]
if len(polygon_points) > 2:
polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon)
substation = FreeCAD.ActiveDocument.addObject("Part::Feature", "Substation")
layer.addObject(substation)
substation.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.5) * scale - self.Origin )
substation.ViewObject.ShapeColor = self.feature_colors['power']['substation']
def create_vegetation(self):
vegetation_layer = self.create_layer("Vegetation")
# Árboles individuales
for node_id, coords in self.nodes.items():
# Verificar si es un árbol
# (Necesitarías procesar los tags de los nodos, implementación simplificada)
cylinder = Part.makeCylinder(0.5, 5.0, FreeCAD.Vector(coords[0], coords[1], 0) * scale - self.Origin )
tree = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
vegetation_layer.addObject(tree)
tree.Shape = cylinder
tree.ViewObject.ShapeColor = self.feature_colors['vegetation']
# Áreas verdes
for way_id, data in self.ways_data.items():
if 'natural' in data['tags'] or 'landuse' in data['tags']:
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
if len(nodes) > 2:
polygon_points = [FreeCAD.Vector(n[0], n[1], 0) * scale - self.Origin for n in nodes]
polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon)
area = vegetation_layer.addObject("Part::Feature", "GreenArea")
area.Shape = face
area.ViewObject.ShapeColor = self.feature_colors['vegetation']
def create_water_bodies(self):
water_layer = self.create_layer("Water")
2025-03-11 21:20:18 +01:00
2025-03-28 19:39:06 +06:00
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]
if len(nodes) > 2:
polygon_points = [FreeCAD.Vector(n[0], n[1], 0) * scale - self.Origin for n in nodes]
polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon)
water = water_layer.addObject("Part::Feature", "WaterBody")
water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1) * scale - self.Origin )
water.ViewObject.ShapeColor = self.feature_colors['water']