Compare commits

...

3 Commits

Author SHA1 Message Date
d009cb7695 update 2025-07-06 01:14:57 +02:00
5a642a4119 update 2025-07-06 01:12:08 +02:00
74bf60101c update 2025-06-15 23:10:17 +02:00
25 changed files with 2560 additions and 3007 deletions

View File

@@ -224,7 +224,6 @@ def spreadsheetBOQPoles(sheet: Worksheet, selection: List[FreeCAD.DocumentObject
# Write data to sheet # Write data to sheet
row = 3 row = 3
group_from = row group_from = row
print(poles_data[0])
f = poles_data[0]['frame'] f = poles_data[0]['frame']
for i, data in enumerate(poles_data): for i, data in enumerate(poles_data):
if f != data["frame"]: if f != data["frame"]:
@@ -255,6 +254,7 @@ def spreadsheetBOQPoles(sheet: Worksheet, selection: List[FreeCAD.DocumentObject
sheet['F{0}'.format(row)].number_format = "0.000" sheet['F{0}'.format(row)].number_format = "0.000"
except: except:
pass pass
sheet['G{0}'.format(row)] = round(data['head_z']) * scale sheet['G{0}'.format(row)] = round(data['head_z']) * scale
sheet['G{0}'.format(row)].number_format = "0.000" sheet['G{0}'.format(row)].number_format = "0.000"
sheet['H{0}'.format(row)] = data["length"] * scale sheet['H{0}'.format(row)] = data["length"] * scale

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>715</width> <width>462</width>
<height>520</height> <height>282</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@@ -78,6 +78,11 @@
<string>Lineweight</string> <string>Lineweight</string>
</property> </property>
</column> </column>
<item row="2" column="1">
<property name="text">
<string/>
</property>
</item>
</widget> </widget>
</item> </item>
</layout> </layout>
@@ -204,6 +209,21 @@
<item> <item>
<widget class="QWidget" name="widget" native="true"> <widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item> <item>
<widget class="QPushButton" name="buttonAcept"> <widget class="QPushButton" name="buttonAcept">
<property name="text"> <property name="text">

View File

@@ -44,32 +44,24 @@ except AttributeError:
def _fromUtf8(s): def _fromUtf8(s):
return s return s
from PVPlantResources import DirIcons as DirIcons
__title__ = "FreeCAD PVSyst importer"
__author__ = "Javier"
#__url__ = "http://www.freecadweb.org"
try:
# Python 2 forward compatibility
range = xrange
except NameError:
pass
def checkCollada():
"checks if collada if available"
global collada
COLLADA = None
try: try:
import collada import collada
COLLADA_AVAILABLE = True
except ImportError: except ImportError:
FreeCAD.Console.PrintError(translate("Arch", "pycollada not found, collada support is disabled.") + "\n") COLLADA_AVAILABLE = False
return False
else: __title__ = "FreeCAD PVSyst importer"
return True __author__ = "Javier Braña"
#__url__ = "http://www.freecadweb.org"
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
def check_collada():
"""Verifica la disponibilidad de pycollada"""
if not COLLADA_AVAILABLE:
FreeCAD.Console.PrintError(translate("PVPlant", "pycollada no encontrado, soporte Collada desactivado.") + "\n")
return COLLADA_AVAILABLE
# from ARCH: # from ARCH:
@@ -79,6 +71,7 @@ def triangulate(shape):
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
mesher = p.GetInt("ColladaMesher", 0) mesher = p.GetInt("ColladaMesher", 0)
tessellation = p.GetFloat("ColladaTessellation", 1.0) tessellation = p.GetFloat("ColladaTessellation", 1.0)
grading = p.GetFloat("ColladaGrading", 0.3) grading = p.GetFloat("ColladaGrading", 0.3)
segsperedge = p.GetInt("ColladaSegsPerEdge", 1) segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
segsperradius = p.GetInt("ColladaSegsPerRadius", 2) segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
@@ -91,9 +84,15 @@ def triangulate(shape):
elif mesher == 1: elif mesher == 1:
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
else: else:
return MeshPart.meshFromShape(Shape=shape, GrowthRate=grading, SegPerEdge=segsperedge, return MeshPart.meshFromShape(Shape=shape,
SegPerRadius=segsperradius, SecondOrder=secondorder, Optimize=optimize, GrowthRate=grading,
AllowQuad=allowquads).Topology SegPerEdge=segsperedge,
SegPerRadius=segsperradius,
SecondOrder=secondorder,
Optimize=optimize,
AllowQuad=allowquads
).Topology
def export(exportList, filename, tessellation=1, colors=None): def export(exportList, filename, tessellation=1, colors=None):
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file. """export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
@@ -249,12 +248,9 @@ def export(exportList, filename, tessellation=1, colors=None):
FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename) FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename)
def exportToDAE(path):
filename = path + ".dae"
def exportToPVC(path, exportTerrain = False): def exportToPVC(path, exportTerrain = False):
filename = path + ".pvc" filename = f"{path}.pvc"
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
from xml.etree.ElementTree import Element, SubElement from xml.etree.ElementTree import Element, SubElement
import datetime import datetime
@@ -528,6 +524,7 @@ def exportToPVC(path, exportTerrain = False):
# TODO: revisar # TODO: revisar
for typ in frameType: for typ in frameType:
isTracker = "tracker" in typ.Proxy.Type.lower() isTracker = "tracker" in typ.Proxy.Type.lower()
isTracker = False
objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker") objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
tmp = [] tmp = []
@@ -818,8 +815,6 @@ class PVSystTaskPanel:
name = date + "-" + name name = date + "-" + name
filename = os.path.join(path, name) filename = os.path.join(path, name)
#if self.form.cbDAE.isChecked():
# exportToDAE(filename)
if self.form.cbPVC.isChecked(): if self.form.cbPVC.isChecked():
exportToPVC(filename, self.form.cbTerrain.isChecked()) exportToPVC(filename, self.form.cbTerrain.isChecked())

View File

@@ -9,6 +9,7 @@ import urllib.request
import math import math
import utm import utm
from collections import defaultdict from collections import defaultdict
import PVPlantImportGrid as ImportElevation
scale = 1000.0 scale = 1000.0
@@ -42,8 +43,10 @@ class OSMImporter:
self.ssl_context = ssl.create_default_context(cafile=certifi.where()) self.ssl_context = ssl.create_default_context(cafile=certifi.where())
def transform_from_latlon(self, lat, lon): def transform_from_latlon(self, lat, lon):
x, y, _, _ = utm.from_latlon(lat, lon) point = ImportElevation.getElevationFromOE([[lat, lon], ])
return FreeCAD.Vector(x, y, .0) * scale - self.Origin return FreeCAD.Vector(point[0].x, point[0].y, point[0].z) * scale - self.Origin
'''x, y, _, _ = utm.from_latlon(lat, lon)
return FreeCAD.Vector(x, y, .0) * scale - self.Origin'''
def get_osm_data(self, bbox): def get_osm_data(self, bbox):
query = f""" query = f"""
@@ -92,10 +95,24 @@ class OSMImporter:
'nodes': [nd.attrib['ref'] for nd in way.findall('nd')] 'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
} }
print("1. Create Transportations")
FreeCADGui.updateGui()
self.create_transportation() self.create_transportation()
print("2. Create Buildings")
FreeCADGui.updateGui()
self.create_buildings() self.create_buildings()
print("3. Create Power Infrastructure")
FreeCADGui.updateGui()
self.create_power_infrastructure() self.create_power_infrastructure()
print("4. Create Vegetation")
FreeCADGui.updateGui()
self.create_vegetation() self.create_vegetation()
print("5. Create Water Bodies")
FreeCADGui.updateGui()
self.create_water_bodies() self.create_water_bodies()
def create_transportation(self): def create_transportation(self):
@@ -118,27 +135,26 @@ class OSMImporter:
'secondary': 5.0, 'secondary': 5.0,
'tertiary': 4.0 'tertiary': 4.0
}.get(highway_type, 3.0) }.get(highway_type, 3.0)
self.create_road(nodes, width, highway_type, transport_layer, tags['name'] if 'name' in tags else "")
self.create_road(nodes, width, highway_type, transport_layer)
# Vías férreas # Vías férreas
if 'railway' in tags: 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] points = [n for n in nodes]
polyline = Draft.make_wire(points, closed=False, face=False) 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.LineWidth = 2.0
polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5)) 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::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
layer.addObject(polyline) layer.addObject(polyline)
def create_railway(self, nodes, layer): def create_railway(self, nodes, layer, name=""):
points = [n for n in nodes] points = [n for n in nodes]
rail_line = Draft.make_wire(points, closed=False, face=False) 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.LineWidth = 1.5
rail_line.ViewObject.ShapeColor = self.feature_colors['railway'] rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
layer.addObject(rail_line) layer.addObject(rail_line)
@@ -149,6 +165,8 @@ class OSMImporter:
if 'building' not in data['tags']: if 'building' not in data['tags']:
continue continue
print(data)
tags = data['tags'] tags = data['tags']
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes] nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
@@ -205,9 +223,12 @@ class OSMImporter:
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes] nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
if 'power' in tags: if 'power' in tags:
print("\n\n")
print(tags)
feature_type = tags['power'] feature_type = tags['power']
if feature_type == 'line': if feature_type == 'line':
print("3.1. Create Power Lines")
FreeCADGui.updateGui()
self.create_power_line( self.create_power_line(
nodes=nodes, nodes=nodes,
tags=tags, tags=tags,
@@ -215,6 +236,8 @@ class OSMImporter:
) )
elif feature_type == 'substation': elif feature_type == 'substation':
print("3.1. Create substations")
FreeCADGui.updateGui()
self.create_substation( self.create_substation(
way_id=way_id, way_id=way_id,
tags=tags, tags=tags,
@@ -223,11 +246,17 @@ class OSMImporter:
) )
elif feature_type == 'tower': elif feature_type == 'tower':
print("3.1. Create power towers")
FreeCADGui.updateGui()
self.create_power_tower( self.create_power_tower(
position=nodes[0] if nodes else None, position=nodes[0] if nodes else None,
tags=tags, tags=tags,
layer=power_layer layer=power_layer
) )
'''self.create_power_tower_1(
way=way_id,
layer=power_layer
)'''
def create_power_line(self, nodes, tags, layer): def create_power_line(self, nodes, tags, layer):
"""Crea líneas de transmisión eléctrica con propiedades técnicas""" """Crea líneas de transmisión eléctrica con propiedades técnicas"""
@@ -244,10 +273,10 @@ class OSMImporter:
return return
wire = Draft.make_wire(points, closed=False, face=False) 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 # Propiedades visuales
wire.ViewObject.LineWidth = 1 + (voltage / 100000) wire.ViewObject.LineWidth = 6 #'''1 + (voltage / 100000)'''
color = self.feature_colors['power']['line'] color = self.feature_colors['power']['line']
wire.ViewObject.ShapeColor = color wire.ViewObject.ShapeColor = color
@@ -260,7 +289,7 @@ class OSMImporter:
layer.addObject(wire) layer.addObject(wire)
# Añadir torres si es overhead # Añadir torres si es overhead
if line_type == 'overhead': '''if line_type == 'overhead':
distance_between_towers = 150 # metros por defecto distance_between_towers = 150 # metros por defecto
if 'distance_between_towers' in tags: if 'distance_between_towers' in tags:
try: try:
@@ -273,7 +302,7 @@ class OSMImporter:
voltage=voltage, voltage=voltage,
distance=distance_between_towers, distance=distance_between_towers,
layer=layer layer=layer
) )'''
except Exception as e: except Exception as e:
FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n") FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")
@@ -318,7 +347,8 @@ class OSMImporter:
# Unir componentes # Unir componentes
tower = base.fuse(mast) 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.Shape = tower
tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower'] tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']
@@ -354,6 +384,164 @@ class OSMImporter:
cable.ViewObject.ShapeColor = self.feature_colors['power']['line'] cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
layer.addObject(cable) 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): def create_substation(self, way_id, tags, nodes, layer):
"""Crea subestaciones con todos los componentes detallados""" """Crea subestaciones con todos los componentes detallados"""
try: try:
@@ -551,27 +739,163 @@ class OSMImporter:
def create_vegetation(self): def create_vegetation(self):
vegetation_layer = self.create_layer("Vegetation") vegetation_layer = self.create_layer("Vegetation")
# Árboles individuales # Procesar nodos de vegetación individual
for node_id, coords in self.nodes.items(): for way_id, tags in self.ways_data.items():
# Verificar si es un árbol coords = self.nodes.get(way_id)
# (Necesitarías procesar los tags de los nodos, implementación simplificada) if not coords:
cylinder = Part.makeCylinder(0.5, 5.0, FreeCAD.Vector(coords[0], coords[1], 0))# * scale - self.Origin ) continue
tree = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
vegetation_layer.addObject(tree)
tree.Shape = cylinder
tree.ViewObject.ShapeColor = self.feature_colors['vegetation']
# Áreas verdes pos = FreeCAD.Vector(*coords)
if tags.get('natural') == 'tree':
self.create_tree(pos, tags, vegetation_layer)
elif tags.get('natural') == 'shrub':
self.create_shrub(pos, tags, vegetation_layer)
"""elif tags.get('natural') == 'tree_stump':
self.create_tree_stump(pos, vegetation_layer)"""
# Procesar áreas vegetales
for way_id, data in self.ways_data.items(): for way_id, data in self.ways_data.items():
if 'natural' in data['tags'] or 'landuse' in data['tags']: tags = data['tags']
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes] nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
if len(nodes) > 2:
polygon_points = [n for n in nodes] if not nodes or len(nodes) < 3:
continue
if tags.get('natural') == 'wood' or tags.get('landuse') == 'forest':
self.create_forest(nodes, tags, vegetation_layer)
elif tags.get('natural') == 'grassland':
self.create_grassland(nodes, vegetation_layer)
elif tags.get('natural') == 'heath':
self.create_heathland(nodes, vegetation_layer)
elif tags.get('natural') == 'scrub':
self.create_scrub_area(nodes, vegetation_layer)
def create_tree(self, position, tags, layer):
"""Crea un árbol individual con propiedades basadas en etiquetas OSM"""
height = float(tags.get('height', 10.0))
trunk_radius = float(tags.get('circumference', 1.0)) / (2 * math.pi)
canopy_radius = float(tags.get('diameter_crown', 4.0)) / 2
# Crear tronco
trunk = Part.makeCylinder(trunk_radius, height, position)
# Crear copa (forma cónica)
canopy_center = position + FreeCAD.Vector(0, 0, height)
canopy = Part.makeCone(canopy_radius, canopy_radius * 0.7, canopy_radius * 1.5, canopy_center)
tree = trunk.fuse(canopy)
tree_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
layer.addObject(tree_obj)
tree_obj.Shape = tree
tree_obj.ViewObject.ShapeColor = (0.3, 0.6, 0.2) # Verde bosque
# Añadir metadatos
for prop in ['genus', 'species', 'leaf_type', 'height']:
if prop in tags:
tree_obj.addProperty("App::PropertyString", prop.capitalize(), "Botany",
"Botanical property").__setattr__(prop.capitalize(), tags[prop])
def create_forest(self, nodes, tags, layer):
"""Crea un área boscosa con densidad variable"""
polygon_points = [FreeCAD.Vector(*n) for n in nodes]
if polygon_points[0] != polygon_points[-1]:
polygon_points.append(polygon_points[0])
# Crear base del bosque
polygon = Part.makePolygon(polygon_points) polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon) face = Part.Face(polygon)
area = vegetation_layer.addObject("Part::Feature", "GreenArea") forest_base = FreeCAD.ActiveDocument.addObject("Part::Feature", "Forest_Base")
area.Shape = face layer.addObject(forest_base)
area.ViewObject.ShapeColor = self.feature_colors['vegetation'] forest_base.Shape = face
forest_base.ViewObject.ShapeColor = (0.15, 0.4, 0.1) # Verde oscuro
# Generar árboles aleatorios dentro del polígono
density = float(tags.get('density', 0.5)) # Árboles por m²
area = face.Area
num_trees = int(area * density)
for _ in range(num_trees):
rand_point = self.random_point_in_polygon(polygon_points)
self.create_tree(rand_point, {}, layer)
def create_grassland(self, nodes, layer):
"""Crea un área de pastizales"""
polygon_points = [FreeCAD.Vector(*n) 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)
grassland = FreeCAD.ActiveDocument.addObject("Part::Feature", "Grassland")
layer.addObject(grassland)
grassland.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))
grassland.ViewObject.ShapeColor = (0.5, 0.7, 0.3) # Verde pasto
def create_heathland(self, nodes, layer):
"""Crea un área de brezales con vegetación baja"""
polygon_points = [FreeCAD.Vector(*n) 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)
heath = FreeCAD.ActiveDocument.addObject("Part::Feature", "Heathland")
layer.addObject(heath)
heath.Shape = face
heath.ViewObject.ShapeColor = (0.6, 0.5, 0.4) # Color terroso
# Añadir arbustos dispersos
for _ in range(int(face.Area * 0.1)): # 1 arbusto cada 10m²
rand_point = self.random_point_in_polygon(polygon_points)
self.create_shrub(rand_point, {}, layer)
def create_shrub(self, position, tags, layer):
"""Crea un arbusto individual"""
height = float(tags.get('height', 1.5))
radius = float(tags.get('diameter_crown', 1.0)) / 2
# Crear forma de arbusto (cono invertido)
base_center = position + FreeCAD.Vector(0, 0, height / 2)
shrub = Part.makeCone(radius, radius * 1.5, height, base_center)
shrub_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Shrub")
layer.addObject(shrub_obj)
shrub_obj.Shape = shrub
shrub_obj.ViewObject.ShapeColor = (0.4, 0.5, 0.3) # Verde arbusto
# Añadir metadatos si existen
if 'genus' in tags:
shrub_obj.addProperty("App::PropertyString", "Genus", "Botany", "Plant genus").Genus = tags['genus']
def create_tree_stump(self, position, layer):
"""Crea un tocón de árbol"""
height = 0.4
radius = 0.5
stump = Part.makeCylinder(radius, height, position)
stump_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree_Stump")
layer.addObject(stump_obj)
stump_obj.Shape = stump
stump_obj.ViewObject.ShapeColor = (0.3, 0.2, 0.1) # Marrón madera
def random_point_in_polygon(self, polygon_points):
"""Genera un punto aleatorio dentro de un polígono"""
min_x = min(p.x for p in polygon_points)
max_x = max(p.x for p in polygon_points)
min_y = min(p.y for p in polygon_points)
max_y = max(p.y for p in polygon_points)
while True:
rand_x = random.uniform(min_x, max_x)
rand_y = random.uniform(min_y, max_y)
rand_point = FreeCAD.Vector(rand_x, rand_y, 0)
# Verificar si el punto está dentro del polígono
polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon)
if face.isInside(rand_point, 0.1, True):
return rand_point
def create_water_bodies(self): def create_water_bodies(self):
water_layer = self.create_layer("Water") water_layer = self.create_layer("Water")
@@ -583,7 +907,8 @@ class OSMImporter:
polygon_points = [n for n in nodes] polygon_points = [n for n in nodes]
polygon = Part.makePolygon(polygon_points) polygon = Part.makePolygon(polygon_points)
face = Part.Face(polygon) face = Part.Face(polygon)
water = water_layer.addObject("Part::Feature", "WaterBody") water = FreeCAD.ActiveDocument.addObject("Part::Feature", "WaterBody")
water_layer.addObject(water)
water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin ) water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin )
water.ViewObject.ShapeColor = self.feature_colors['water'] water.ViewObject.ShapeColor = self.feature_colors['water']

View File

@@ -48,7 +48,7 @@ class PVPlantWorkbench(Workbench):
self.projectlist = PVPlantTools.projectlist self.projectlist = PVPlantTools.projectlist
self.projectlist.insert(0, 'Reload') self.projectlist.insert(0, 'Reload')
self.projectlist.insert(1, 'Separator') self.projectlist.insert(1, 'Separator')
self.framelist = PVPlantTools.pv_list self.framelist = PVPlantTools.pv_mechanical
from Export import ExporterCommands from Export import ExporterCommands
self.inportExportlist = ExporterCommands.Exportlist self.inportExportlist = ExporterCommands.Exportlist
@@ -77,14 +77,14 @@ class PVPlantWorkbench(Workbench):
# Toolbar # Toolbar
self.appendToolbar("Civil", self.projectlist) # creates a new toolbar with your commands self.appendToolbar("Civil", self.projectlist) # creates a new toolbar with your commands
self.appendToolbar("PVPlant", self.framelist) # creates a new toolbar with your commands self.appendToolbar("Mechanical", self.framelist) # creates a new toolbar with your commands
self.appendToolbar("Shadow", self.objectlist) # creates a new toolbar with your commands self.appendToolbar("Shadow", self.objectlist) # creates a new toolbar with your commands
self.appendToolbar("Outputs", self.inportExportlist) # creates a new toolbar with your commands self.appendToolbar("Outputs", self.inportExportlist) # creates a new toolbar with your commands
self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands
# Menu # Menu
self.appendMenu("&Civil", self.projectlist) # creates a new menu self.appendMenu("&Civil", self.projectlist) # creates a new menu
self.appendMenu("&PVPlant", self.framelist) # creates a new menu self.appendMenu("&Mechanical", self.framelist) # creates a new menu
self.appendMenu("&Shadow", self.objectlist) # creates a new menu self.appendMenu("&Shadow", self.objectlist) # creates a new menu
self.appendMenu("&Outputs", self.inportExportlist) # creates a new menu self.appendMenu("&Outputs", self.inportExportlist) # creates a new menu
self.appendMenu("&Electrical", self.electricalList) # creates a new menu self.appendMenu("&Electrical", self.electricalList) # creates a new menu
@@ -145,13 +145,14 @@ class PVPlantWorkbench(Workbench):
import SelectionObserver import SelectionObserver
import FreeCADGui import FreeCADGui
self.observer = SelectionObserver.SelObserver() #self.observer = SelectionObserver.SelObserver()
FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident #FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
return return
def Deactivated(self): def Deactivated(self):
"This function is executed when the workbench is deactivated" "This function is executed when the workbench is deactivated"
FreeCADGui.Selection.removeObserver(self.observer)
#FreeCADGui.Selection.removeObserver(self.observer)
return return
def ContextMenu(self, recipient): def ContextMenu(self, recipient):

View File

@@ -0,0 +1,877 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>formRack</class>
<widget class="QDialog" name="formRack">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>476</width>
<height>1032</height>
</rect>
</property>
<property name="windowTitle">
<string>Fixed Frame:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Módulos:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Altura (m)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editModuleHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>1.990000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Largura (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="editModuleLenght">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.960000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editModuleWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.030000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Potencia (wp)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editModulePower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>150</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>350</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Estructura</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Columnas (un)</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="editFrontHeight">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>5.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.800000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Orientación del módulo</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="editVerticalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="editRows">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboFrameType">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Fija</string>
</property>
</item>
<item>
<property name="text">
<string>Tracker 1 Eje</string>
</property>
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="editLeftOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Offset borde derecha (m)</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Ángulo de inclinación (º)</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QDoubleSpinBox" name="editRightOffset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.050000000000000</double>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="editTilt">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QSpinBox" name="editInclination">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>60</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Filas (un)</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Distancia al suelo en el frente (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="editCols">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>20</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelVerticalGap">
<property name="text">
<string>Separación vertical entre módulos (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboModuleOrientation">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<item>
<property name="text">
<string>Landscape</string>
</property>
</item>
<item>
<property name="text">
<string>Portrait</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Separación horizontal entre módulos (m)</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Ängulo máximo de inclinación longitudinal (ª)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="editHorizontalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Tipo de estructura</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Offset borde izquierda (m)</string>
</property>
</widget>
</item>
<item row="16" column="0" colspan="2">
<widget class="QWidget" name="widgetTracker" native="true">
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>5</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="labelVerticalGap_2">
<property name="text">
<string>Separación entre uniones (m)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Separación Motor (m)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="editInternalGapNumber">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Número de uniones</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="editInternalGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="editMotorGap">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="maximum">
<double>0.900000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.020000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Resultado</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Total de módulos</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editTotalModules">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Potencia total (wp)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editTotalPower">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Longitud (m)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="editTotalLength">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Anchura (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editTotalWidth">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -45,24 +45,11 @@ __title__ = "PVPlant Frames"
__author__ = "Javier Braña" __author__ = "Javier Braña"
__url__ = "http://www.sogos-solar.com" __url__ = "http://www.sogos-solar.com"
import os import os
import PVPlantResources import PVPlantResources
from PVPlantResources import DirIcons as DirIcons from PVPlantResources import DirIcons as DirIcons
def selectFrames():
objectlist = list()
#FreeCAD.ActiveDocument.findObjects(Name="Tracker")
for obj in FreeCAD.ActiveDocument.Objects:
if hasattr(obj, "Proxy") and obj.Proxy.isDerivedFrom("Frame"):
objectlist.append(obj)
return objectlist if len(objectlist) > 0 else None
def makeFrameSetup(name="FrameSetup"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
FrameSetup(obj)
ViewProviderFrameSetup(obj.ViewObject)
return obj
class FrameSetup: class FrameSetup:
"A Base Frame Setup Class" "A Base Frame Setup Class"
def __init__(self, obj): def __init__(self, obj):
@@ -72,6 +59,7 @@ class FrameSetup:
def setProperties(self, obj): def setProperties(self, obj):
''' Definición de Propiedades: ''' ''' Definición de Propiedades: '''
pl = obj.PropertiesList pl = obj.PropertiesList
# Modulo: ------------------------------------------------------------------------------------------------------ # Modulo: ------------------------------------------------------------------------------------------------------
if not "ModuleThick" in pl: if not "ModuleThick" in pl:
obj.addProperty("App::PropertyLength", obj.addProperty("App::PropertyLength",
@@ -175,14 +163,15 @@ class FrameSetup:
"The facemaker type to use to build the profile of this object") "The facemaker type to use to build the profile of this object")
) )
# Frame -------------------------------------------------------------------------------------------------------- # Poles --------------------------------------------------------------------------------------------------------
'''if not "Tilt" in pl: if not "PoleType" in pl:
obj.addProperty("App::PropertyAngle", obj.addProperty("App::PropertyLinkList",
"Tilt", "PoleType",
"Frame", "Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object") QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Tilt = 30''' )
# Frame --------------------------------------------------------------------------------------------------------
if not "MaxLengthwiseTilt" in pl: if not "MaxLengthwiseTilt" in pl:
obj.addProperty("App::PropertyAngle", obj.addProperty("App::PropertyAngle",
"MaxLengthwiseTilt", "MaxLengthwiseTilt",
@@ -217,268 +206,99 @@ class FrameSetup:
) )
obj.setEditorMode("TotalAreaShape", 1) obj.setEditorMode("TotalAreaShape", 1)
class Frame(ArchComponent.Component):
"A Base Frame Obcject - Class"
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.obj = obj
self.setProperties(obj)
# Does a IfcType exist?
obj.IfcType = "Structural Item"
obj.setEditorMode("IfcType", 1)
self.totalAreaShape = None
self.changed = True
def setProperties(self, obj):
# Definicion de Propiedades:
print("Frame - setProperties")
ArchComponent.Component.setProperties(self, obj)
pl = obj.PropertiesList
# Modulo: ------------------------------------------------------------------------------------------------------
if not "ModuleThick" in pl:
obj.addProperty("App::PropertyLength",
"ModuleThick",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleThick = 40
if not "ModuleWidth" in pl:
obj.addProperty("App::PropertyLength",
"ModuleWidth",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
).ModuleWidth = 992
if not "ModuleHeight" in pl:
obj.addProperty("App::PropertyLength",
"ModuleHeight",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).ModuleHeight = 1996
if not "PoleCableLength" in pl:
obj.addProperty("App::PropertyLength",
"PoleCableLength",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).PoleCableLength = 1200
if not "ModulePower" in pl:
obj.addProperty("App::PropertyQuantity",
"ModulePower",
"Module",
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
).ModulePower = 400
# Array de modulos: -------------------------------------------------------------------------------------------
if not "ModuleCols" in pl:
obj.addProperty("App::PropertyQuantity",
"ModuleCols",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleCols = 15
if not "ModuleRows" in pl:
obj.addProperty("App::PropertyQuantity",
"ModuleRows",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleRows = 2
if not "ModuleColGap" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleColGap",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleColGap = 20
if not "ModuleRowGap" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleRowGap",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleRowGap = 20
if not "ModuleOffsetX" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleOffsetX",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleOffsetX = 0
if not "ModuleOffsetY" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleOffsetY",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleOffsetY = 0
if not "ModuleOrientation" in pl:
obj.addProperty("App::PropertyEnumeration",
"ModuleOrientation",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).ModuleOrientation = ["Portrait", "Landscape"]
if not "ModuleViews" in pl:
obj.addProperty("App::PropertyBool",
"ModuleViews",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).ModuleViews = True
'''
if not "Modules" in pl:
obj.addProperty("App::PropertyLinkSubListChild",
"Modules",
"Posicion de modulos",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).ModuleViews = True
'''
# Frame --------------------------------------------------------------------------------------------------------
if not "Tilt" in pl:
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Tilt = 30
if not "MaxLengthwiseTilt" in pl:
obj.addProperty("App::PropertyAngle",
"MaxLengthwiseTilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "Máxima inclinación longitudinal")
).MaxLengthwiseTilt = 15
# Frame outputs -----------------------------
if not "Width" in pl:
obj.addProperty("App::PropertyDistance",
"Width",
"Frame",
QT_TRANSLATE_NOOP("App::Property",
"Ancho de la estructura")
)
obj.setEditorMode("Width", 1)
if not "Length" in pl:
obj.addProperty("App::PropertyDistance",
"Length",
"Frame",
QT_TRANSLATE_NOOP("App::Property",
"Largo de la estructura")
)
obj.setEditorMode("Length", 1)
if not "TotalArea" in pl:
obj.addProperty("App::PropertyArea",
"TotalArea",
"Frame",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Area total de los Paneles")
)
obj.setEditorMode("TotalArea", 1)
# Neighbours:
if not "North" in pl:
obj.addProperty("App::PropertyLink",
"North",
"Neighbours",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Area total de los Paneles")
)
if not "South" in pl:
obj.addProperty("App::PropertyLink",
"South",
"Neighbours",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Area total de los Paneles")
)
if not "East" in pl:
obj.addProperty("App::PropertyLink",
"East",
"Neighbours",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Area total de los Paneles")
)
if not "West" in pl:
obj.addProperty("App::PropertyLink",
"West",
"Neighbours",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Area total de los Paneles")
)
'''# Placement
if not "Route" in pl:
obj.addProperty("App::PropertyLink",
"Route",
"Placement",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Total Area de los Paneles")
)
obj.setEditorMode("Route", 1)
if not "RouteSection" in pl:
obj.addProperty("App::PropertyIntegerConstraint",
"RouteSection",
"Placement",
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
"Total Area de los Paneles")
)
obj.setEditorMode("RouteSection", 1)'''
self.Type = "Frame"
def onChanged(self, obj, prop):
if prop == "North":
if obj.getPropertyByName(prop):
obj.getPropertyByName("Route").South = obj
if (prop == "Route"):
if obj.getPropertyByName(prop):
obj.RouteSection = (1, 1, len(obj.getPropertyByName("Route").Shape.Edges) + 1, 1)
def getTotalAreaShape(self):
return self.totalAreaShape
''' ------------------------------------------- Fixed Structure --------------------------------------------------- ''' ''' ------------------------------------------- Fixed Structure --------------------------------------------------- '''
def makeRack(name="Rack"): def makeRack(name="Rack"):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
_FixedRack(obj) obj.Label = name
_ViewProviderFixedRack(obj.ViewObject) FixedRack(obj)
# FreeCAD.ActiveDocument.recompute() ViewProviderFixedRack(obj.ViewObject)
return obj return obj
class FixedRack(FrameSetup):
class _FixedRack(Frame):
"A Fixed Rack Obcject" "A Fixed Rack Obcject"
def __init__(self, obj): def __init__(self, obj):
# Definición de Variables: #FrameSetup.__init__(self, obj)
Frame.__init__(self, obj) super(FixedRack, self).__init__(obj)
Frame.setProperties(self, obj)
self.setProperties(obj) self.setProperties(obj)
obj.ModuleColumns = 6
obj.ModuleRows = 2
obj.ModuleColGap = 20
obj.ModuleRowGap = 20
#obj.Tilt = 30
# Does a IfcType exist? # Does a IfcType exist?
# obj.IfcType = "Fence" # obj.IfcType = "Fence"
# obj.MoveWithHost = False # obj.MoveWithHost = False
def setProperties(self, obj): def setProperties(self, obj):
FrameSetup.setProperties(self, obj)
pl = obj.PropertiesList pl = obj.PropertiesList
if not "ModuleElevation" in pl:
obj.addProperty("App::PropertyDistance",
"ModuleElevation",
"ModuleArray",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).ModuleElevation = 500
# Frame: ------------------------------------------------------------------------------------------------------
if not "Tilt" in pl:
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Tilt = 0
if not "FrameType" in pl:
obj.addProperty("App::PropertyEnumeration",
"FrameType",
"Frame",
QT_TRANSLATE_NOOP("App::Property",
"The facemaker type to use to build the profile of this object")
).FrameType = ["Simple", "West-Lest"]
# Pole: ------------------------------------------------------------------------------------------------------
if not "BackPostWidth" in pl:
obj.addProperty("App::PropertyLength",
"BackPostWidth",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BackPostWidth = 80
if not "BackPostHeight" in pl:
obj.addProperty("App::PropertyLength",
"BackPostHeight",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BackPostHeight = 160
if not "BackPostLength" in pl:
obj.addProperty("App::PropertyLength",
"BackPostLength",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).BackPostLength = 3200
if not "FrontPostWidth" in pl:
obj.addProperty("App::PropertyLength",
"FrontPostWidth",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPostWidth = 40
if not "FrontPostHeight" in pl:
obj.addProperty("App::PropertyLength",
"FrontPostHeight",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPostHeight = 80
if not "FrontPostLength" in pl:
obj.addProperty("App::PropertyLength",
"FrontPostLength",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).FrontPostLength = 2300
# Array of Posts: ------------------------------------------------------------------------------------------------------ # Array of Posts: ------------------------------------------------------------------------------------------------------
if not "NumberPostsX" in pl: if not "NumberPostsX" in pl:
@@ -486,7 +306,7 @@ class _FixedRack(Frame):
"NumberPostsX", "NumberPostsX",
"Poles", "Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object") QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).NumberPostsX = 5 ).NumberPostsX = 3
if not "DistancePostsX" in pl: if not "DistancePostsX" in pl:
obj.addProperty("App::PropertyLength", obj.addProperty("App::PropertyLength",
@@ -515,6 +335,7 @@ class _FixedRack(Frame):
"Poles", "Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object") QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).RammingDeep = 1500 ).RammingDeep = 1500
# Correas: ---------------------------------------------------------------------------------------------------- # Correas: ----------------------------------------------------------------------------------------------------
if not "BeamHeight" in pl: if not "BeamHeight" in pl:
obj.addProperty("App::PropertyLength", obj.addProperty("App::PropertyLength",
@@ -545,9 +366,11 @@ class _FixedRack(Frame):
).BeamSpacing = 1000 ).BeamSpacing = 1000
self.Type = "Fixed Rack" self.Type = "Fixed Rack"
obj.Proxy = self
def onDocumentRestored(self, obj): def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj) #ArchComponent.Component.onDocumentRestored(self, obj)
super(FixedRack, self).onDocumentRestored(obj)
self.setProperties(obj) self.setProperties(obj)
def __getstate__(self): def __getstate__(self):
@@ -557,153 +380,64 @@ class _FixedRack(Frame):
if state: if state:
self.Type = state self.Type = state
'''
def addObject(self, child):
print("addObject")
def removeObject(self, child):
print("removeObject")
def onChanged(self, obj, prop):
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
'''
def onChanged(self, fp, prop): def onChanged(self, fp, prop):
'''Do something when a property has changed''' '''Do something when a property has changed'''
# FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
def CalculateModuleArray(self, obj, totalh, totalw, moduleh, modulew): def generate_modules(self, obj, h, w):
# ModuleThick modulos=[]
# ModuleWidth
# ModuleThinness
# ModuleColor
# ModuleCols
# ModuleRows
# ModuleColGap
# ModuleRowGap
# BeamHeight
# BeamWidth
# BeamOffset
import Part
# pl = obj.Placement
module = Part.makeBox(modulew, moduleh, obj.ModuleThick.Value)
correa = Part.makeBox(totalw + obj.BeamOffset.Value * 2, obj.BeamWidth.Value, obj.BeamHeight.Value)
# Longitud poste - Produndidad de hincado + correas
correaoffsetz = obj.BackPostLength.Value
offsetz = correaoffsetz + obj.BeamHeight.Value
p1 = FreeCAD.Vector(0, 0, 0)
p2 = FreeCAD.Vector(totalw, 0, 0)
p3 = FreeCAD.Vector(totalw, totalh, 0)
p4 = FreeCAD.Vector(0, totalh, 0)
totalArea = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1]))
totalArea = Part.makePlane(totalw, totalh)
totalArea.Placement.Base.x = -totalw / 2
totalArea.Placement.Base.y = -totalh / 2
totalArea.Placement.Base.z = obj.BeamHeight.Value
self.ModuleArea = totalArea
# if ShowtotalArea:
self.ListModules.append(totalArea)
correaoffsety = (moduleh - obj.BeamSpacing.Value - obj.BeamWidth.Value) / 2
for y in range(int(obj.ModuleRows.Value)): for y in range(int(obj.ModuleRows.Value)):
for x in range(int(obj.ModuleCols.Value)): for x in range(int(obj.ModuleColumns.Value)):
xx = totalArea.Placement.Base.x + (modulew + obj.ModuleColGap.Value) * x mod = Part.makeBox(w, h, obj.ModuleThick.Value)
yy = totalArea.Placement.Base.y + (moduleh + obj.ModuleRowGap.Value) * y mod.Placement.Base = FreeCAD.Vector(
zz = obj.BeamHeight.Value x * (w + obj.ModuleColGap.Value),
moduleCopy = module.copy() y * (h + obj.ModuleRowGap.Value),
moduleCopy.Placement.Base.x = xx 0
moduleCopy.Placement.Base.y = yy )
moduleCopy.Placement.Base.z = zz modulos.append(mod)
self.ListModules.append(moduleCopy)
compound = Part.makeCompound(self.ListModules) compound = Part.makeCompound(modulos)
compound.Placement.Base.z = correaoffsetz - obj.RammingDeep.Value '''center = FreeCAD.Vector(compound.BoundBox.Center)
for mod in modulos:
mod.Placement.Base -= FreeCAD.Vector(center.x, center.y, 0)
compound = Part.makeCompound(modulos)'''
return compound
if obj.FrontPost:
base = FreeCAD.Vector(0, (-obj.BackPostHeight.Value + obj.DistancePostsY.Value) / 2,
correaoffsetz - obj.RammingDeep.Value)
else:
base = FreeCAD.Vector(0, -obj.BackPostHeight.Value / 2 + totalh / 3,
correaoffsetz - obj.RammingDeep.Value)
a = compound.rotate(base, FreeCAD.Vector(1, 0, 0), obj.Tilt)
del correa
del module
del compound
del self.ListModules[:]
self.ListModules.append(a)
def CalculatePosts(self, obj, totalh, totalw):
'''
# NumberPostsX
# NumberPostsY
# PostHeight
# PostWidth
# PostLength
# DistancePostsX
# DistancePostsY
'''
def generate_posts(self, obj):
postBack = Part.makeBox(obj.BackPostWidth.Value, obj.BackPostHeight.Value, obj.BackPostLength.Value) postBack = Part.makeBox(obj.BackPostWidth.Value, obj.BackPostHeight.Value, obj.BackPostLength.Value)
postFront = Part.makeBox(obj.FrontPostWidth.Value, obj.FrontPostHeight.Value, obj.FrontPostLength.Value) postFront = Part.makeBox(obj.FrontPostWidth.Value, obj.FrontPostHeight.Value, obj.FrontPostLength.Value)
angle = obj.Placement.Rotation.toEuler()[1] post_back = []
offsetX = (-obj.DistancePostsX.Value * (obj.NumberPostsX.Value - 1)) / 2 post_front = []
# offsetY = (-obj.PostHeight.Value - obj.DistancePostsY * (obj.NumberPostsY - 1)) / 2
if obj.FrontPost:
offsetY = (-obj.BackPostHeight.Value + obj.DistancePostsY.Value) / 2
else:
# TODO: cambiar el totalh / 3 por el valor adecuado
offsetY = -obj.BackPostHeight.Value / 2 + totalh / 3
offsetZ = -obj.RammingDeep.Value
for x in range(int(obj.NumberPostsX.Value)): for x in range(int(obj.NumberPostsX.Value)):
xx = offsetX + (
obj.NumberPostsX.Value + obj.DistancePostsX.Value) * x # * math.cos(obj.Placement.Rotation.toEuler()[1])
yy = offsetY
zz = offsetZ # * math.sin(obj.Placement.Rotation.toEuler()[1])
postCopy = postBack.copy() postCopy = postBack.copy()
postCopy.Placement.Base.x = xx postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, obj.DistancePostsY.Value, 0)
postCopy.Placement.Base.y = yy post_back.append(postCopy)
postCopy.Placement.Base.z = zz
base = FreeCAD.Vector(xx, yy, obj.BackPostHeight.Value - offsetZ)
postCopy = postCopy.rotate(base, FreeCAD.Vector(0, 1, 0), -angle)
self.ListPosts.append(postCopy)
if obj.FrontPost: if obj.FrontPost:
postCopy = postFront.copy() postCopy = postFront.copy()
postCopy.Placement.Base.x = xx postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, 0, 0)
postCopy.Placement.Base.y = -yy post_front.append(postCopy)
postCopy.Placement.Base.z = zz
base = FreeCAD.Vector(xx, yy, obj.FrontPostHeight.Value - offsetZ)
postCopy = postCopy.rotate(base, FreeCAD.Vector(0, 1, 0), -angle)
self.ListPosts.append(postCopy)
del postBack return Part.makeCompound([Part.makeCompound(post_back), Part.makeCompound(post_front)])
del postFront
def correct_placement(self, obj):
center = FreeCAD.Vector(obj.BoundBox.Center)
obj.Placement.Base -= FreeCAD.Vector(center.x, center.y, 0)
def execute(self, obj): def execute(self, obj):
# obj.Shape: compound
# |- Modules and Beams: compound
# |-- Modules array: compound
# |--- Modules: solid
# |-- Beams: compound
# |--- MainBeam: solid
# |--- Secundary Beams: solid (if exist)
# |- Poles array: compound
# |-- Poles: solid
pl = obj.Placement pl = obj.Placement
del self.ListModules[:]
del self.ListPosts[:]
self.ListModules = []
self.ListPosts = []
if obj.ModuleOrientation == "Portrait": if obj.ModuleOrientation == "Portrait":
w = obj.ModuleWidth.Value w = obj.ModuleWidth.Value
h = obj.ModuleHeight.Value h = obj.ModuleHeight.Value
@@ -712,63 +446,38 @@ class _FixedRack(Frame):
w = obj.ModuleHeight.Value w = obj.ModuleHeight.Value
totalh = h * obj.ModuleRows + obj.ModuleRowGap.Value * (obj.ModuleRows - 1) totalh = h * obj.ModuleRows + obj.ModuleRowGap.Value * (obj.ModuleRows - 1)
totalw = w * obj.ModuleCols + obj.ModuleColGap.Value * (obj.ModuleCols - 1) totalw = w * obj.ModuleColumns + obj.ModuleColGap.Value * (obj.ModuleColumns - 1)
obj.Width = totalw
obj.Length = totalh
# hacer el shape: modules = self.generate_modules(obj, h, w)
self.CalculateModuleArray(obj, totalh, totalw, h, w) modules.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), obj.Tilt.Value)
self.CalculatePosts(obj, totalh, totalw) self.correct_placement(modules)
modules.Placement.Base += FreeCAD.Vector(0, 0, obj.ModuleElevation.Value)
posts = self.generate_posts(obj)
self.correct_placement(posts)
posts.Placement.Base -= FreeCAD.Vector(0, 0, obj.RammingDeep.Value)
allShapes = [] compound = Part.makeCompound([modules, posts])
allShapes.extend(self.ListModules)
allShapes.extend(self.ListPosts)
compound = Part.makeCompound(allShapes)
obj.Shape = compound obj.Shape = compound
obj.Placement = pl obj.Placement = pl
angle = obj.Placement.Rotation.toEuler()[1] angle = obj.Placement.Rotation.toEuler()[1]
if angle > obj.MaxLengthwiseTilt: if angle > obj.MaxLengthwiseTilt:
obj.ViewObject.ShapeColor = (1.0, 0.0, 0.0) obj.ViewObject.ShapeColor = (1.0, 0.0, 0.0)
print("fin")
obj.Width = totalw
obj.Length = totalh
class _ViewProviderFixedRack(ArchComponent.ViewProviderComponent): class ViewProviderFixedRack:
"A View Provider for the Pipe object" "A View Provider for the Pipe object"
def __init__(self, vobj): def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
'''
ArchSite._ViewProviderSite.__init__(self, vobj)
vobj.Proxy = self vobj.Proxy = self
vobj.addExtension("Gui::ViewProviderGroupExtensionPython", self)
self.setProperties(vobj)
'''
def getIcon(self): def getIcon(self):
""" Return the path to the appropriate icon. """ """ Return the path to the appropriate icon. """
return str(os.path.join(DirIcons, "solar-fixed.svg")) return str(os.path.join(DirIcons, "solar-fixed.svg"))
def setEdit(self, vobj, mode): def setEdit(self, vobj, mode):
"""Method called when the document requests the object to enter edit mode.
Edit mode is entered when a user double clicks on an object in the tree
view, or when they use the menu option [Edit -> Toggle Edit Mode].
Just display the standard Arch component task panel.
Parameters
----------
mode: int or str
The edit mode the document has requested. Set to 0 when requested via
a double click or [Edit -> Toggle Edit Mode].
Returns
-------
bool
If edit mode was entered.
"""
if (mode == 0) and hasattr(self, "Object"): if (mode == 0) and hasattr(self, "Object"):
taskd = _FixedRackTaskPanel(self.Object) taskd = _FixedRackTaskPanel(self.Object)
taskd.obj = self.Object taskd.obj = self.Object
@@ -783,15 +492,7 @@ class _FixedRackTaskPanel:
# ------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------
# Module widget form # Module widget form
# ------------------------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------------------------
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.ui") self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "Mechanical/Frame/PVPlantFixedFrame.ui")
self.formRack.widgetTracker.setVisible(False)
self.formRack.comboFrameType.currentIndexChanged.connect(self.selectionchange)
self.formPiling = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantRackFixedPiling.ui")
self.formPiling.editBreadthwaysNumOfPost.valueChanged.connect(self.editBreadthwaysNumOfPostChange)
self.formPiling.editAlongNumOfPost.valueChanged.connect(self.editAlongNumOfPostChange)
self.form = [self.formRack, self.formPiling]
def selectionchange(self, i): def selectionchange(self, i):
vis = False vis = False
@@ -839,7 +540,6 @@ def makeTrackerSetup(name="TrackerSetup"):
pass pass
return obj return obj
def getarray(array, numberofpoles): def getarray(array, numberofpoles):
if len(array) == 0: if len(array) == 0:
newarray = [0] * numberofpoles newarray = [0] * numberofpoles
@@ -899,36 +599,15 @@ class TrackerSetup(FrameSetup):
QT_TRANSLATE_NOOP("App::Property", "The height of this object") QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).UseGroupsOfModules = False ).UseGroupsOfModules = False
# Poles: ------------------------------------------------------------------------------------------------------
#TODO: cambiar todo esto por una lista de objetos??
'''if not "PoleHeight" in pl:
obj.addProperty("App::PropertyLength",
"PoleHeight",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).PoleHeight = 160
if not "PoleWidth" in pl:
obj.addProperty("App::PropertyLength",
"PoleWidth",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
).PoleWidth = 80
if not "PoleLength" in pl:
obj.addProperty("App::PropertyLength",
"PoleLength",
"Pole",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).PoleLength = 3000'''
# Array of Posts: ------------------------------------------------------------------------------------------------------ # Array of Posts: ------------------------------------------------------------------------------------------------------
'''movido a la clase madre
if not "PoleType" in pl: if not "PoleType" in pl:
obj.addProperty("App::PropertyLinkList", obj.addProperty("App::PropertyLinkList",
"PoleType", "PoleType",
"Poles", "Poles",
QT_TRANSLATE_NOOP("App::Property", "The height of this object") QT_TRANSLATE_NOOP("App::Property", "The height of this object")
) )'''
if not "PoleSequence" in pl: if not "PoleSequence" in pl:
obj.addProperty("App::PropertyIntegerList", obj.addProperty("App::PropertyIntegerList",
@@ -1272,6 +951,7 @@ class TrackerSetup(FrameSetup):
modules = self.CalculateModuleArray(obj, totalh, totalw, h, w) modules = self.CalculateModuleArray(obj, totalh, totalw, h, w)
beams = self.calculateBeams(obj, totalh, totalw, h, w) beams = self.calculateBeams(obj, totalh, totalw, h, w)
poles, poleaxis = self.CalculatePosts(obj, totalh, totalw) poles, poleaxis = self.CalculatePosts(obj, totalh, totalw)
compound = Part.makeCompound([modules, beams]) compound = Part.makeCompound([modules, beams])
compound.Placement.Base.z = obj.MainBeamAxisPosition.Value - (obj.MainBeamHeight.Value / 2) compound.Placement.Base.z = obj.MainBeamAxisPosition.Value - (obj.MainBeamHeight.Value / 2)
obj.Shape = Part.makeCompound([compound, Part.makeCompound([poles, poleaxis])]) obj.Shape = Part.makeCompound([compound, Part.makeCompound([poles, poleaxis])])
@@ -1279,6 +959,7 @@ class TrackerSetup(FrameSetup):
obj.Length = max(obj.Shape.BoundBox.XLength, obj.Shape.BoundBox.YLength) obj.Length = max(obj.Shape.BoundBox.XLength, obj.Shape.BoundBox.YLength)
obj.TotalPower = obj.ModulePower.Value * obj.ModuleRows * obj.ModuleColumns obj.TotalPower = obj.ModulePower.Value * obj.ModuleRows * obj.ModuleColumns
class ViewProviderTrackerSetup: class ViewProviderTrackerSetup:
"A View Provider for the TrackerSetup object" "A View Provider for the TrackerSetup object"
@@ -1297,6 +978,7 @@ class ViewProviderTrackerSetup:
return True return True
return False return False
def makeTracker(name = "Tracker", setup = None): def makeTracker(name = "Tracker", setup = None):
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tracker") obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tracker")
obj.Label = name obj.Label = name
@@ -1306,6 +988,7 @@ def makeTracker(name = "Tracker", setup = None):
obj.Setup = setup obj.Setup = setup
return obj return obj
class Tracker(ArchComponent.Component): class Tracker(ArchComponent.Component):
"A 1 Axis single row Tracker Obcject" "A 1 Axis single row Tracker Obcject"
@@ -1461,94 +1144,6 @@ class ViewProviderTracker(ArchComponent.ViewProviderComponent):
return False return False
class dualRowTracker(ArchComponent.Component):
"A 1 Axis Dual Row Tracker Obcject"
def __init__(self, obj):
# Definición de Variables:
ArchComponent.Component.__init__(self, obj)
self.setProperties(obj)
self.Type = None
self.oldTilt = 0
self.oldRotation = None
def setProperties(self, obj):
pl = obj.PropertiesList
if not "MotorPosition" in pl:
obj.addProperty("App::PropertyLink",
"MotorPosition",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not "MotorRow" in pl:
obj.addProperty("App::PropertyLink",
"MotorRow",
"Frame",
"The height of this object"
)
if not "DrivenRow" in pl:
obj.addProperty("App::PropertyLink",
"DrivenRow",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not "Tilt" in pl:
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).Tilt = 0
if not "Tilt" in pl:
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not "Tilt" in pl:
obj.addProperty("App::PropertyAngle",
"Tilt",
"Frame",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
)
if not "AngleX" in pl:
obj.addProperty("App::PropertyAngle",
"AngleX",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AngleX = 0
if not "AngleY" in pl:
obj.addProperty("App::PropertyAngle",
"AngleY",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AngleX = 0
if not "AngleZ" in pl:
obj.addProperty("App::PropertyAngle",
"AngleZ",
"Outputs",
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
).AngleX = 0
self.Type = "Tracker"
# obj.Type = self.Type
obj.Proxy = self
def onDocumentRestored(self, obj):
ArchComponent.Component.onDocumentRestored(self, obj)
self.setProperties(obj)
def execute(self, obj):
''' '''
class _TrackerTaskPanel: class _TrackerTaskPanel:
def __init__(self, obj=None): def __init__(self, obj=None):
@@ -1657,7 +1252,7 @@ class _FrameTaskPanel:
return True return True
class _CommandFixedRack: class CommandFixedRack:
"the Arch Building command definition" "the Arch Building command definition"
def GetResources(self): def GetResources(self):
@@ -1672,11 +1267,11 @@ class _CommandFixedRack:
def Activated(self): def Activated(self):
obj = makeRack() obj = makeRack()
self.TaskPanel = _FixedRackTaskPanel(obj) #self.TaskPanel = _FixedRackTaskPanel(obj)
FreeCADGui.Control.showDialog(self.TaskPanel) #FreeCADGui.Control.showDialog(self.TaskPanel)
return return
class _CommandTrackerSetup: class CommandTrackerSetup:
"the Arch Building command definition" "the Arch Building command definition"
def GetResources(self): def GetResources(self):
@@ -1687,6 +1282,7 @@ class _CommandTrackerSetup:
"Creates a TrackerSetup object from setup dialog.")} "Creates a TrackerSetup object from setup dialog.")}
def IsActive(self): def IsActive(self):
return True
return (not FreeCAD.ActiveDocument is None and return (not FreeCAD.ActiveDocument is None and
not FreeCAD.ActiveDocument.getObject("Site") is None) not FreeCAD.ActiveDocument.getObject("Site") is None)
@@ -1696,7 +1292,7 @@ class _CommandTrackerSetup:
FreeCADGui.Control.showDialog(self.TaskPanel) FreeCADGui.Control.showDialog(self.TaskPanel)
return return
class _CommandTracker: class CommandTracker:
"the Arch Building command definition" "the Arch Building command definition"
def GetResources(self): def GetResources(self):
@@ -1720,51 +1316,3 @@ class _CommandTracker:
break break
makeTracker(setup=setupobj) makeTracker(setup=setupobj)
return return
class _CommandMultiRowTracker:
"the Arch Building command definition"
def GetResources(self):
return {'Pixmap': str(os.path.join(DirIcons, "solar-tracker.svg")),
'MenuText': "Multi-row Tracker",
'Accel': "R, M",
'ToolTip': "Creates a multi-row Tracker object from trackers."}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
if FreeCAD.ActiveDocument is not None:
if FreeCADGui.Selection.getCompleteSelection():
for ob in FreeCAD.ActiveDocument.Objects:
if ob.Name[:4] == "Site":
return True
def Activated(self):
self.TaskPanel = _FixedRackTaskPanel()
FreeCADGui.Control.showDialog(self.TaskPanel)
return
if FreeCAD.GuiUp:
class CommandRackGroup:
def GetCommands(self):
return tuple(['PVPlantFixedRack',
'PVPlantTrackerSetup',
'PVPlantTracker'
])
def GetResources(self):
return {'MenuText': QT_TRANSLATE_NOOP("", 'Rack Types'),
'ToolTip': QT_TRANSLATE_NOOP("", 'Rack Types')
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('PVPlantFixedRack', _CommandFixedRack())
FreeCADGui.addCommand('PVPlantTrackerSetup', _CommandTrackerSetup())
FreeCADGui.addCommand('PVPlantTracker', _CommandTracker())
FreeCADGui.addCommand('Multirow', _CommandMultiRowTracker())
FreeCADGui.addCommand('RackType', CommandRackGroup())

View File

@@ -845,7 +845,7 @@ class _FenceTaskPanel:
# Commands --------------------------------------------------------------------------------- # Commands ---------------------------------------------------------------------------------
class _CommandPVPlantFence: class CommandPVPlantFence:
"the PVPlant Fence command definition" "the PVPlant Fence command definition"
def GetResources(self): def GetResources(self):
@@ -882,14 +882,7 @@ if FreeCAD.GuiUp:
not (FreeCAD.ActiveDocument.getObject("Terrain") is None)) not (FreeCAD.ActiveDocument.getObject("Terrain") is None))
import PVPlantFenceGate import PVPlantFenceGate
FreeCADGui.addCommand('PVPlantFence', _CommandPVPlantFence()) FreeCADGui.addCommand('PVPlantFence', CommandPVPlantFence())
FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate()) FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate())
FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost()) FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost())
FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup()) #FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
def movep(obj):
pl = obj.Shape.BoundBox.Center
points = []
for ind in range(len(obj.Shape.Vertexes)):
points.append(obj.Shape.Vertexes[ind].Point - pl)
Draft.makeWire(points)

View File

@@ -52,7 +52,9 @@ class MapWindow(QtGui.QWidget):
self.maxLat = None self.maxLat = None
self.minLon = None self.minLon = None
self.maxLon = None self.maxLon = None
self.zoom = None
self.WinTitle = WinTitle self.WinTitle = WinTitle
self.georeference_coordinates = {'lat': None, 'lon': None}
self.setupUi() self.setupUi()
def setupUi(self): def setupUi(self):
@@ -152,6 +154,9 @@ class MapWindow(QtGui.QWidget):
self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS") self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
RightLayout.addWidget(self.checkboxImportGis) RightLayout.addWidget(self.checkboxImportGis)
self.checkboxImportSatelitalImagen = QtGui.QCheckBox("Importar Imagen Satelital")
RightLayout.addWidget(self.checkboxImportSatelitalImagen)
verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
RightLayout.addItem(verticalSpacer) RightLayout.addItem(verticalSpacer)
@@ -192,6 +197,7 @@ class MapWindow(QtGui.QWidget):
"var data = drawnItems.toGeoJSON();" "var data = drawnItems.toGeoJSON();"
"MyApp.shapes(JSON.stringify(data));" "MyApp.shapes(JSON.stringify(data));"
) )
self.close() self.close()
@QtCore.Slot(float, float) @QtCore.Slot(float, float)
@@ -203,17 +209,22 @@ class MapWindow(QtGui.QWidget):
' | UTM: ' + str(zone_number) + zone_letter + ' | UTM: ' + str(zone_number) + zone_letter +
', {:.5f}m E, {:.5f}m N'.format(x, y)) ', {:.5f}m E, {:.5f}m N'.format(x, y))
@QtCore.Slot(float, float, float, float) @QtCore.Slot(float, float, float, float, int)
def onMapZoom(self, minLat, minLon, maxLat, maxLon): def onMapZoom(self, minLat, minLon, maxLat, maxLon, zoom):
self.minLat = min([minLat, maxLat]) self.minLat = min([minLat, maxLat])
self.maxLat = max([minLat, maxLat]) self.maxLat = max([minLat, maxLat])
self.minLon = min([minLon, maxLon]) self.minLon = min([minLon, maxLon])
self.maxLon = max([minLon, maxLon]) self.maxLon = max([minLon, maxLon])
self.zoom = zoom
@QtCore.Slot(float, float) @QtCore.Slot(float, float)
def georeference(self, lat, lng): def georeference(self, lat, lng):
import PVPlantSite import PVPlantSite
from geopy.geocoders import Nominatim from geopy.geocoders import Nominatim
self.georeference_coordinates['lat'] = lat
self.georeference_coordinates['lon'] = lng
Site = PVPlantSite.get(create=True) Site = PVPlantSite.get(create=True)
Site.Proxy.setLatLon(lat, lng) Site.Proxy.setLatLon(lat, lng)
@@ -278,7 +289,7 @@ class MapWindow(QtGui.QWidget):
pts = [p.sub(offset) for p in tmp] pts = [p.sub(offset) for p in tmp]
obj = Draft.makeWire(pts, closed=cw, face=False) obj = Draft.makeWire(pts, closed=cw, face=False)
#obj.Placement.Base = offset #obj.Placement.Base = Site.Origin
obj.Label = name obj.Label = name
Draft.autogroup(obj) Draft.autogroup(obj)
@@ -288,6 +299,170 @@ class MapWindow(QtGui.QWidget):
if self.checkboxImportGis.isChecked(): if self.checkboxImportGis.isChecked():
self.getDataFromOSM(self.minLat, self.minLon, self.maxLat, self.maxLon) self.getDataFromOSM(self.minLat, self.minLon, self.maxLat, self.maxLon)
if self.checkboxImportSatelitalImagen.isChecked():
# Usar los límites reales del terreno (rectangular)
'''s_lat = self.minLat
s_lon = self.minLon
n_lat = self.maxLat
n_lon = self.maxLon
# Obtener puntos UTM para las esquinas
corners = ImportElevation.getElevationFromOE([
[s_lat, s_lon], # Esquina suroeste
[n_lat, s_lon], # Esquina sureste
[n_lat, n_lon], # Esquina noreste
[s_lat, n_lon] # Esquina noroeste
])
if not corners or len(corners) < 4:
FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas\n")
return
# Descargar imagen satelital
from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
downloader = GoogleMapDownloader(
zoom= 18, #self.zoom,
layer='raw_satellite'
)
img = downloader.generateImage(
sw_lat=s_lat,
sw_lng=s_lon,
ne_lat=n_lat,
ne_lng=n_lon
)
# Guardar imagen en el directorio del documento
doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
if not doc_path:
doc_path = FreeCAD.ConfigGet("UserAppData")
filename = os.path.join(doc_path, "background.jpeg")
img.save(filename)
ancho, alto = img.size
# Crear objeto de imagen en FreeCAD
doc = FreeCAD.ActiveDocument
img_obj = doc.addObject('Image::ImagePlane', 'Background')
img_obj.ImageFile = filename
img_obj.Label = 'Background'
# Calcular dimensiones en metros usando las coordenadas UTM
# Extraer las coordenadas de las esquinas
sw = corners[0] # Suroeste
se = corners[1] # Sureste
ne = corners[2] # Noreste
nw = corners[3] # Noroeste
# Calcular ancho (promedio de los lados superior e inferior)
width_bottom = se.x - sw.x
width_top = ne.x - nw.x
width_m = (width_bottom + width_top) / 2
# Calcular alto (promedio de los lados izquierdo y derecho)
height_left = nw.y - sw.y
height_right = ne.y - se.y
height_m = (height_left + height_right) / 2
img_obj.XSize = width_m
img_obj.YSize = height_m
# Posicionar el centro de la imagen en (0,0,0)
img_obj.Placement.Base = FreeCAD.Vector(-width_m / 2, -height_m / 2, 0)'''
# Definir área rectangular
s_lat = self.minLat
s_lon = self.minLon
n_lat = self.maxLat
n_lon = self.maxLon
# Obtener puntos UTM para las esquinas y el punto de referencia
points = [
[s_lat, s_lon], # Suroeste
[n_lat, n_lon], # Noreste
[self.georeference_coordinates['lat'], self.georeference_coordinates['lon']] # Punto de referencia
]
utm_points = ImportElevation.getElevationFromOE(points)
if not utm_points or len(utm_points) < 3:
FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas y referencia\n")
return
sw_utm, ne_utm, ref_utm = utm_points
# Descargar imagen satelital
from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
downloader = GoogleMapDownloader(
zoom=self.zoom,
layer='raw_satellite'
)
img = downloader.generateImage(
sw_lat=s_lat,
sw_lng=s_lon,
ne_lat=n_lat,
ne_lng=n_lon
)
# Guardar imagen
doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
if not doc_path:
doc_path = FreeCAD.ConfigGet("UserAppData")
filename = os.path.join(doc_path, "background.jpeg")
img.save(filename)
# Calcular dimensiones reales en metros
width_m = ne_utm.x - sw_utm.x # Ancho en metros (este-oeste)
height_m = ne_utm.y - sw_utm.y # Alto en metros (norte-sur)
# Calcular posición relativa del punto de referencia dentro de la imagen
rel_x = (ref_utm.x - sw_utm.x) / width_m if width_m != 0 else 0.5
rel_y = (ref_utm.y - sw_utm.y) / height_m if height_m != 0 else 0.5
# Crear objeto de imagen en FreeCAD
doc = FreeCAD.ActiveDocument
img_obj = doc.addObject('Image::ImagePlane', 'Background')
img_obj.ImageFile = filename
img_obj.Label = 'Background'
# Convertir dimensiones a milímetros (FreeCAD trabaja en mm)
img_obj.XSize = width_m * 1000
img_obj.YSize = height_m * 1000
# Posicionar para que el punto de referencia esté en (0,0,0)
# La esquina inferior izquierda debe estar en:
# x = -rel_x * ancho_total
# y = -rel_y * alto_total
img_obj.Placement.Base = FreeCAD.Vector(
-rel_x * width_m * 1000,
-rel_y * height_m * 1000,
0
)
# Refrescar el documento
doc.recompute()
def calculate_texture_transform(self, mesh_obj, width_m, height_m):
"""Calcula la transformación precisa para la textura"""
try:
# Obtener coordenadas reales de las esquinas
import utm
sw = utm.from_latlon(self.minLat, self.minLon)
ne = utm.from_latlon(self.maxLat, self.maxLon)
# Crear matriz de transformación
scale_x = (ne[0] - sw[0]) / width_m
scale_y = (ne[1] - sw[1]) / height_m
# Aplicar transformación (solo si se usa textura avanzada)
if hasattr(mesh_obj.ViewObject, "TextureMapping"):
mesh_obj.ViewObject.TextureMapping = "PLANE"
mesh_obj.ViewObject.TextureScale = (scale_x, scale_y)
mesh_obj.ViewObject.TextureOffset = (sw[0], sw[1])
except Exception as e:
FreeCAD.Console.PrintWarning(f"No se pudo calcular transformación: {str(e)}\n")
def getDataFromOSM(self, min_lat, min_lon, max_lat, max_lon): def getDataFromOSM(self, min_lat, min_lon, max_lat, max_lon):
import Importer.importOSM as importOSM import Importer.importOSM as importOSM
import PVPlantSite import PVPlantSite

View File

@@ -125,9 +125,9 @@ def getElevationFromOE(coordinates):
points = [] points = []
for i, point in enumerate(coordinates): for i, point in enumerate(coordinates):
c = utm.from_latlon(point[0], point[1]) c = utm.from_latlon(point[0], point[1])
points.append(FreeCAD.Vector(round(c[0] * 1000, 0), points.append(FreeCAD.Vector(round(c[0], 0),
round(c[1] * 1000, 0), round(c[1], 0),
0)) 0) * 1000)
return points return points
# Only get the json response in case of 200 or 201 # Only get the json response in case of 200 or 201
@@ -136,14 +136,16 @@ def getElevationFromOE(coordinates):
results = r.json() results = r.json()
for point in results["results"]: for point in results["results"]:
c = utm.from_latlon(point["latitude"], point["longitude"]) c = utm.from_latlon(point["latitude"], point["longitude"])
v = FreeCAD.Vector(round(c[0] * 1000, 0), v = FreeCAD.Vector(round(c[0], 0),
round(c[1] * 1000, 0), round(c[1], 0),
round(point["elevation"] * 1000, 0)) round(point["elevation"], 0)) * 1000
points.append(v) points.append(v)
return points return points
def getSinglePointElevationFromBing(lat, lng): def getSinglePointElevationFromBing(lat, lng):
#http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey} #http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey}
import utm
source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points=" source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points="
source += str(lat) + "," + str(lng) source += str(lat) + "," + str(lng)
source += "&heights=sealevel" source += "&heights=sealevel"
@@ -153,11 +155,9 @@ def getSinglePointElevationFromBing(lat, lng):
response = requests.get(source) response = requests.get(source)
ans = response.text ans = response.text
# +# to do: error handling - wait and try again
s = json.loads(ans) s = json.loads(ans)
print(s)
res = s['resourceSets'][0]['resources'][0]['elevations'] res = s['resourceSets'][0]['resources'][0]['elevations']
import utm
for elevation in res: for elevation in res:
c = utm.from_latlon(lat, lng) c = utm.from_latlon(lat, lng)
v = FreeCAD.Vector( v = FreeCAD.Vector(
@@ -324,7 +324,6 @@ def getSinglePointElevationUtm(lat, lon):
print (v) print (v)
return v return v
def getElevationUTM(polygon, lat, lng, resolution = 10000): def getElevationUTM(polygon, lat, lng, resolution = 10000):
import utm import utm
@@ -448,47 +447,6 @@ def getElevation(lat, lon, b=50.35, le=11.17, size=40):
FreeCADGui.updateGui() FreeCADGui.updateGui()
return FreeCAD.activeDocument().ActiveObject return FreeCAD.activeDocument().ActiveObject
'''
# original::
def getElevation(lat, lon, b=50.35, le=11.17, size=40):
tm.lat = lat
tm.lon = lon
baseheight = 0 #getheight(tm.lat, tm.lon)
center = tm.fromGeographic(tm.lat, tm.lon)
#https://maps.googleapis.com/maps/api/elevation/json?path=36.578581,-118.291994|36.23998,-116.83171&samples=3&key=YOUR_API_KEY
#https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key=YOUR_API_KEY
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
source += str(b-size*0.001) + "," + str(le) + "|" + str(b+size*0.001) + "," + str(le)
source += "&samples=" + str(100)
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
response = urllib.request.urlopen(source)
ans = response.read()
# +# to do: error handling - wait and try again
s = json.loads(ans)
res = s['results']
points = []
for r in res:
c = tm.fromGeographic(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector(
round(c[0], 2),
round(c[1], 2),
round(r['elevation'] * 1000, 2) - baseheight
)
points.append(v)
line = Draft.makeWire(points, closed=False, face=False, support=None)
line.ViewObject.Visibility = False
#FreeCAD.activeDocument().recompute()
FreeCADGui.updateGui()
return FreeCAD.activeDocument().ActiveObject
'''
class _ImportPointsTaskPanel: class _ImportPointsTaskPanel:
def __init__(self, obj = None): def __init__(self, obj = None):

View File

@@ -138,8 +138,6 @@ class _Manhole(ArchComponent.Component):
obj.Shape = ext_sol.cut([ins_sol, ], 0.0) obj.Shape = ext_sol.cut([ins_sol, ], 0.0)
class _ViewProviderManhole(ArchComponent.ViewProviderComponent): class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
"A View Provider for the Pipe object" "A View Provider for the Pipe object"

View File

@@ -578,22 +578,22 @@ class _PVPlantSite(ArchSite._Site):
obj.addProperty("App::PropertyLink", obj.addProperty("App::PropertyLink",
"Boundary", "Boundary",
"Site", "PVPlant",
"Boundary of land") "Boundary of land")
obj.addProperty("App::PropertyLinkList", obj.addProperty("App::PropertyLinkList",
"Frames", "Frames",
"Site", "PVPlant",
"Frames templates") "Frames templates")
obj.addProperty("App::PropertyEnumeration", obj.addProperty("App::PropertyEnumeration",
"UtmZone", "UtmZone",
"Base", "PVPlant",
"UTM zone").UtmZone = zone_list "UTM zone").UtmZone = zone_list
obj.addProperty("App::PropertyVector", obj.addProperty("App::PropertyVector",
"Origin", "Origin",
"Base", "PVPlant",
"Origin point.").Origin = (0, 0, 0) "Origin point.").Origin = (0, 0, 0)
def onDocumentRestored(self, obj): def onDocumentRestored(self, obj):
@@ -771,10 +771,12 @@ class _PVPlantSite(ArchSite._Site):
import PVPlantImportGrid import PVPlantImportGrid
x, y, zone_number, zone_letter = utm.from_latlon(lat, lon) x, y, zone_number, zone_letter = utm.from_latlon(lat, lon)
self.obj.UtmZone = zone_list[zone_number - 1] self.obj.UtmZone = zone_list[zone_number - 1]
# self.obj.UtmZone = "Z"+str(zone_number) zz = PVPlantImportGrid.getElevationFromOE([[lat, lon]])
#z = PVPlantImportGrid.get_elevation(lat, lon) self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz[0].z)
zz = PVPlantImportGrid.getSinglePointElevationFromBing(lat, lon) #self.obj.OriginOffset = FreeCAD.Vector(x * 1000, y * 1000, 0) #??
self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz.z) self.obj.Latitude = lat
self.obj.Longitude = lon
self.obj.Elevation = zz[0].z
class _ViewProviderSite(ArchSite._ViewProviderSite): class _ViewProviderSite(ArchSite._ViewProviderSite):

View File

@@ -651,6 +651,32 @@ if FreeCAD.GuiUp:
import Project.GenerateExternalDocument as GED import Project.GenerateExternalDocument as GED
FreeCADGui.addCommand('newExternalDocument', GED.CommandGenerateExternalDocument()) FreeCADGui.addCommand('newExternalDocument', GED.CommandGenerateExternalDocument())
from Mechanical.Frame import PVPlantFrame
class CommandRackGroup:
def GetCommands(self):
return tuple(['PVPlantFixedRack',
'PVPlantTrackerSetup',
'PVPlantTracker'
])
def GetResources(self):
return {'MenuText': QT_TRANSLATE_NOOP("", 'Rack Types'),
'ToolTip': QT_TRANSLATE_NOOP("", 'Rack Types')
}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
FreeCADGui.addCommand('PVPlantFixedRack', PVPlantFrame.CommandFixedRack())
FreeCADGui.addCommand('PVPlantTrackerSetup', PVPlantFrame.CommandTrackerSetup())
FreeCADGui.addCommand('PVPlantTracker', PVPlantFrame.CommandTracker())
FreeCADGui.addCommand('RackType', CommandRackGroup())
import PVPlantFence
FreeCADGui.addCommand('PVPlantFenceGroup', PVPlantFence.CommandFenceGroup())
projectlist = [ # "Reload", projectlist = [ # "Reload",
"PVPlantSite", "PVPlantSite",
"ProjectSetup", "ProjectSetup",
@@ -678,14 +704,12 @@ projectlist = [ # "Reload",
'newExternalDocument', 'newExternalDocument',
] ]
pv_list = [ pv_mechanical = [
# "RackType", "RackType",
# "PVPlantRackCheck",
# "Separator",
"PVPlantPlacement", "PVPlantPlacement",
"PVPlantAdjustToTerrain", "PVPlantAdjustToTerrain",
"PVPlantConvertTo", "PVPlantConvertTo",
# "PVArea"
] ]
objectlist = ['PVPlantTree',] objectlist = ['PVPlantTree',
'PVPlantFence',]

View File

@@ -1,462 +0,0 @@
/*!
Copyright (c) 2011-2015, Pavel Shramov, Bruno Bergot - MIT licence
*/
L.KML = L.FeatureGroup.extend({
initialize: function (kml) {
this._kml = kml;
this._layers = {};
if (kml) {
this.addKML(kml);
}
},
addKML: function (xml) {
var layers = L.KML.parseKML(xml);
if (!layers || !layers.length) return;
for (var i = 0; i < layers.length; i++) {
this.fire('addlayer', {
layer: layers[i]
});
this.addLayer(layers[i]);
}
this.latLngs = L.KML.getLatLngs(xml);
this.fire('loaded');
},
latLngs: []
});
L.Util.extend(L.KML, {
parseKML: function (xml) {
var style = this.parseStyles(xml);
this.parseStyleMap(xml, style);
var el = xml.getElementsByTagName('Folder');
var layers = [], l;
for (var i = 0; i < el.length; i++) {
if (!this._check_folder(el[i])) { continue; }
l = this.parseFolder(el[i], style);
if (l) { layers.push(l); }
}
el = xml.getElementsByTagName('Placemark');
for (var j = 0; j < el.length; j++) {
if (!this._check_folder(el[j])) { continue; }
l = this.parsePlacemark(el[j], xml, style);
if (l) { layers.push(l); }
}
el = xml.getElementsByTagName('GroundOverlay');
for (var k = 0; k < el.length; k++) {
l = this.parseGroundOverlay(el[k]);
if (l) { layers.push(l); }
}
return layers;
},
// Return false if e's first parent Folder is not [folder]
// - returns true if no parent Folders
_check_folder: function (e, folder) {
e = e.parentNode;
while (e && e.tagName !== 'Folder')
{
e = e.parentNode;
}
return !e || e === folder;
},
parseStyles: function (xml) {
var styles = {};
var sl = xml.getElementsByTagName('Style');
for (var i=0, len=sl.length; i<len; i++) {
var style = this.parseStyle(sl[i]);
if (style) {
var styleName = '#' + style.id;
styles[styleName] = style;
}
}
return styles;
},
parseStyle: function (xml) {
var style = {}, poptions = {}, ioptions = {}, el, id;
var attributes = {color: true, width: true, Icon: true, href: true, hotSpot: true};
function _parse (xml) {
var options = {};
for (var i = 0; i < xml.childNodes.length; i++) {
var e = xml.childNodes[i];
var key = e.tagName;
if (!attributes[key]) { continue; }
if (key === 'hotSpot')
{
for (var j = 0; j < e.attributes.length; j++) {
options[e.attributes[j].name] = e.attributes[j].nodeValue;
}
} else {
var value = e.childNodes[0].nodeValue;
if (key === 'color') {
options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
} else if (key === 'width') {
options.weight = parseInt(value);
} else if (key === 'Icon') {
ioptions = _parse(e);
if (ioptions.href) { options.href = ioptions.href; }
} else if (key === 'href') {
options.href = value;
}
}
}
return options;
}
el = xml.getElementsByTagName('LineStyle');
if (el && el[0]) { style = _parse(el[0]); }
el = xml.getElementsByTagName('PolyStyle');
if (el && el[0]) { poptions = _parse(el[0]); }
if (poptions.color) { style.fillColor = poptions.color; }
if (poptions.opacity) { style.fillOpacity = poptions.opacity; }
el = xml.getElementsByTagName('IconStyle');
if (el && el[0]) { ioptions = _parse(el[0]); }
if (ioptions.href) {
style.icon = new L.KMLIcon({
iconUrl: ioptions.href,
shadowUrl: null,
anchorRef: {x: ioptions.x, y: ioptions.y},
anchorType: {x: ioptions.xunits, y: ioptions.yunits}
});
}
id = xml.getAttribute('id');
if (id && style) {
style.id = id;
}
return style;
},
parseStyleMap: function (xml, existingStyles) {
var sl = xml.getElementsByTagName('StyleMap');
for (var i = 0; i < sl.length; i++) {
var e = sl[i], el;
var smKey, smStyleUrl;
el = e.getElementsByTagName('key');
if (el && el[0]) { smKey = el[0].textContent; }
el = e.getElementsByTagName('styleUrl');
if (el && el[0]) { smStyleUrl = el[0].textContent; }
if (smKey === 'normal')
{
existingStyles['#' + e.getAttribute('id')] = existingStyles[smStyleUrl];
}
}
return;
},
parseFolder: function (xml, style) {
var el, layers = [], l;
el = xml.getElementsByTagName('Folder');
for (var i = 0; i < el.length; i++) {
if (!this._check_folder(el[i], xml)) { continue; }
l = this.parseFolder(el[i], style);
if (l) { layers.push(l); }
}
el = xml.getElementsByTagName('Placemark');
for (var j = 0; j < el.length; j++) {
if (!this._check_folder(el[j], xml)) { continue; }
l = this.parsePlacemark(el[j], xml, style);
if (l) { layers.push(l); }
}
el = xml.getElementsByTagName('GroundOverlay');
for (var k = 0; k < el.length; k++) {
if (!this._check_folder(el[k], xml)) { continue; }
l = this.parseGroundOverlay(el[k]);
if (l) { layers.push(l); }
}
if (!layers.length) { return; }
if (layers.length === 1) { return layers[0]; }
return new L.FeatureGroup(layers);
},
parsePlacemark: function (place, xml, style, options) {
var h, i, j, k, el, il, opts = options || {};
el = place.getElementsByTagName('styleUrl');
for (i = 0; i < el.length; i++) {
var url = el[i].childNodes[0].nodeValue;
for (var a in style[url]) {
opts[a] = style[url][a];
}
}
il = place.getElementsByTagName('Style')[0];
if (il) {
var inlineStyle = this.parseStyle(place);
if (inlineStyle) {
for (k in inlineStyle) {
opts[k] = inlineStyle[k];
}
}
}
var multi = ['MultiGeometry', 'MultiTrack', 'gx:MultiTrack'];
for (h in multi) {
el = place.getElementsByTagName(multi[h]);
for (i = 0; i < el.length; i++) {
var layer = this.parsePlacemark(el[i], xml, style, opts);
this.addPlacePopup(place, layer);
return layer;
}
}
var layers = [];
var parse = ['LineString', 'Polygon', 'Point', 'Track', 'gx:Track'];
for (j in parse) {
var tag = parse[j];
el = place.getElementsByTagName(tag);
for (i = 0; i < el.length; i++) {
var l = this['parse' + tag.replace(/gx:/, '')](el[i], xml, opts);
if (l) { layers.push(l); }
}
}
if (!layers.length) {
return;
}
var layer = layers[0];
if (layers.length > 1) {
layer = new L.FeatureGroup(layers);
}
this.addPlacePopup(place, layer);
return layer;
},
addPlacePopup: function(place, layer) {
var i, j, name, descr = '';
el = place.getElementsByTagName('name');
if (el.length && el[0].childNodes.length) {
name = el[0].childNodes[0].nodeValue;
}
el = place.getElementsByTagName('description');
for (i = 0; i < el.length; i++) {
for (j = 0; j < el[i].childNodes.length; j++) {
descr = descr + el[i].childNodes[j].nodeValue;
}
}
if (name) {
layer.bindPopup('<h2>' + name + '</h2>' + descr, { className: 'kml-popup'});
}
},
parseCoords: function (xml) {
var el = xml.getElementsByTagName('coordinates');
return this._read_coords(el[0]);
},
parseLineString: function (line, xml, options) {
var coords = this.parseCoords(line);
if (!coords.length) { return; }
return new L.Polyline(coords, options);
},
parseTrack: function (line, xml, options) {
var el = xml.getElementsByTagName('gx:coord');
if (el.length === 0) { el = xml.getElementsByTagName('coord'); }
var coords = [];
for (var j = 0; j < el.length; j++) {
coords = coords.concat(this._read_gxcoords(el[j]));
}
if (!coords.length) { return; }
return new L.Polyline(coords, options);
},
parsePoint: function (line, xml, options) {
var el = line.getElementsByTagName('coordinates');
if (!el.length) {
return;
}
var ll = el[0].childNodes[0].nodeValue.split(',');
return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options);
},
parsePolygon: function (line, xml, options) {
var el, polys = [], inner = [], i, coords;
el = line.getElementsByTagName('outerBoundaryIs');
for (i = 0; i < el.length; i++) {
coords = this.parseCoords(el[i]);
if (coords) {
polys.push(coords);
}
}
el = line.getElementsByTagName('innerBoundaryIs');
for (i = 0; i < el.length; i++) {
coords = this.parseCoords(el[i]);
if (coords) {
inner.push(coords);
}
}
if (!polys.length) {
return;
}
if (options.fillColor) {
options.fill = true;
}
if (polys.length === 1) {
return new L.Polygon(polys.concat(inner), options);
}
return new L.MultiPolygon(polys, options);
},
getLatLngs: function (xml) {
var el = xml.getElementsByTagName('coordinates');
var coords = [];
for (var j = 0; j < el.length; j++) {
// text might span many childNodes
coords = coords.concat(this._read_coords(el[j]));
}
return coords;
},
_read_coords: function (el) {
var text = '', coords = [], i;
for (i = 0; i < el.childNodes.length; i++) {
text = text + el.childNodes[i].nodeValue;
}
text = text.split(/[\s\n]+/);
for (i = 0; i < text.length; i++) {
var ll = text[i].split(',');
if (ll.length < 2) {
continue;
}
coords.push(new L.LatLng(ll[1], ll[0]));
}
return coords;
},
_read_gxcoords: function (el) {
var text = '', coords = [];
text = el.firstChild.nodeValue.split(' ');
coords.push(new L.LatLng(text[1], text[0]));
return coords;
},
parseGroundOverlay: function (xml) {
var latlonbox = xml.getElementsByTagName('LatLonBox')[0];
var bounds = new L.LatLngBounds(
[
latlonbox.getElementsByTagName('south')[0].childNodes[0].nodeValue,
latlonbox.getElementsByTagName('west')[0].childNodes[0].nodeValue
],
[
latlonbox.getElementsByTagName('north')[0].childNodes[0].nodeValue,
latlonbox.getElementsByTagName('east')[0].childNodes[0].nodeValue
]
);
var attributes = {Icon: true, href: true, color: true};
function _parse (xml) {
var options = {}, ioptions = {};
for (var i = 0; i < xml.childNodes.length; i++) {
var e = xml.childNodes[i];
var key = e.tagName;
if (!attributes[key]) { continue; }
var value = e.childNodes[0].nodeValue;
if (key === 'Icon') {
ioptions = _parse(e);
if (ioptions.href) { options.href = ioptions.href; }
} else if (key === 'href') {
options.href = value;
} else if (key === 'color') {
options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
}
}
return options;
}
var options = {};
options = _parse(xml);
if (latlonbox.getElementsByTagName('rotation')[0] !== undefined) {
var rotation = latlonbox.getElementsByTagName('rotation')[0].childNodes[0].nodeValue;
options.rotation = parseFloat(rotation);
}
return new L.RotatedImageOverlay(options.href, bounds, {opacity: options.opacity, angle: options.rotation});
}
});
L.KMLIcon = L.Icon.extend({
options: {
iconSize: [32, 32],
iconAnchor: [16, 16],
},
_setIconStyles: function (img, name) {
L.Icon.prototype._setIconStyles.apply(this, [img, name]);
if( img.complete ) {
this.applyCustomStyles( img )
} else {
img.onload = this.applyCustomStyles.bind(this,img)
}
},
applyCustomStyles: function(img) {
var options = this.options;
var width = options.iconSize[0];
var height = options.iconSize[1];
this.options.popupAnchor = [0,(-0.83*height)];
if (options.anchorType.x === 'fraction')
img.style.marginLeft = (-options.anchorRef.x * width) + 'px';
if (options.anchorType.y === 'fraction')
img.style.marginTop = ((-(1 - options.anchorRef.y) * height) + 1) + 'px';
if (options.anchorType.x === 'pixels')
img.style.marginLeft = (-options.anchorRef.x) + 'px';
if (options.anchorType.y === 'pixels')
img.style.marginTop = (options.anchorRef.y - height + 1) + 'px';
}
});
L.KMLMarker = L.Marker.extend({
options: {
icon: new L.KMLIcon.Default()
}
});
// Inspired by https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src
L.RotatedImageOverlay = L.ImageOverlay.extend({
options: {
angle: 0
},
_reset: function () {
L.ImageOverlay.prototype._reset.call(this);
this._rotate();
},
_animateZoom: function (e) {
L.ImageOverlay.prototype._animateZoom.call(this, e);
this._rotate();
},
_rotate: function () {
if (L.DomUtil.TRANSFORM) {
// use the CSS transform rule if available
this._image.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
} else if (L.Browser.ie) {
// fallback for IE6, IE7, IE8
var rad = this.options.angle * (Math.PI / 180),
costheta = Math.cos(rad),
sintheta = Math.sin(rad);
this._image.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
}
},
getBounds: function () {
return this._bounds;
}
});

View File

@@ -1,58 +0,0 @@
# Leaflet KML layer plugin
![Example](assets/screenshot.jpg)
Demo: https://www.windy.com/uploader
This plugin was extracted from Pavel Shramov's Leaflet Plugins [repository](https://github.com/shramov/leaflet-plugins) in order to maintain this code more frequently and separate KML layer from other plugins.
So far we have fixed few issues.
Probably will work on Leaflet 1+, tested on Leaflet 1.4.
## How to use
```html
<html>
<head>
<link rel="stylesheet" href="http://unpkg.com/leaflet@1.4.0/dist/leaflet.css" />
<script src="http://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>
<script src="./L.KML.js"></script>
</head>
<body>
<div style="width: 100vw; height: 100vh" id="map"></div>
<script type="text/javascript">
// Make basemap
const map = new L.Map('map', { center: new L.LatLng(58.4, 43.0), zoom: 11 });
const osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
map.addLayer(osm);
// Load kml file
fetch('assets/example1.kml')
.then(res => res.text())
.then(kmltext => {
// Create new kml overlay
const parser = new DOMParser();
const kml = parser.parseFromString(kmltext, 'text/xml');
const track = new L.KML(kml);
map.addLayer(track);
// Adjust map to show the kml
const bounds = track.getBounds();
map.fitBounds(bounds);
});
</script>
</body>
</html>
```
## Changelog
- 1.0.1 - Updated README
- 1.0.0 - Initial commit, original version with few fixes
## Licence
MIT

View File

@@ -1,915 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>KML Samples</name>
<open>1</open>
<description>Unleash your creativity with the help of these examples!</description>
<Style id="downArrowIcon">
<IconStyle>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pal4/icon28.png</href>
</Icon>
</IconStyle>
</Style>
<Style id="globeIcon">
<IconStyle>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pal3/icon19.png</href>
</Icon>
</IconStyle>
<LineStyle>
<width>2</width>
</LineStyle>
</Style>
<Style id="transPurpleLineGreenPoly">
<LineStyle>
<color>7fff00ff</color>
<width>4</width>
</LineStyle>
<PolyStyle>
<color>7f00ff00</color>
</PolyStyle>
</Style>
<Style id="yellowLineGreenPoly">
<LineStyle>
<color>7f00ffff</color>
<width>4</width>
</LineStyle>
<PolyStyle>
<color>7f00ff00</color>
</PolyStyle>
</Style>
<Style id="thickBlackLine">
<LineStyle>
<color>87000000</color>
<width>10</width>
</LineStyle>
</Style>
<Style id="redLineBluePoly">
<LineStyle>
<color>ff0000ff</color>
</LineStyle>
<PolyStyle>
<color>ffff0000</color>
</PolyStyle>
</Style>
<Style id="blueLineRedPoly">
<LineStyle>
<color>ffff0000</color>
</LineStyle>
<PolyStyle>
<color>ff0000ff</color>
</PolyStyle>
</Style>
<Style id="transRedPoly">
<LineStyle>
<width>1.5</width>
</LineStyle>
<PolyStyle>
<color>7d0000ff</color>
</PolyStyle>
</Style>
<Style id="transBluePoly">
<LineStyle>
<width>1.5</width>
</LineStyle>
<PolyStyle>
<color>7dff0000</color>
</PolyStyle>
</Style>
<Style id="transGreenPoly">
<LineStyle>
<width>1.5</width>
</LineStyle>
<PolyStyle>
<color>7d00ff00</color>
</PolyStyle>
</Style>
<Style id="transYellowPoly">
<LineStyle>
<width>1.5</width>
</LineStyle>
<PolyStyle>
<color>7d00ffff</color>
</PolyStyle>
</Style>
<Style id="noDrivingDirections">
<BalloonStyle>
<text><![CDATA[
<b>$[name]</b>
<br /><br />
$[description]
]]></text>
</BalloonStyle>
</Style>
<Folder>
<name>Placemarks</name>
<description>These are just some of the different kinds of placemarks with
which you can mark your favorite places</description>
<LookAt>
<longitude>-122.0839597145766</longitude>
<latitude>37.42222904525232</latitude>
<altitude>0</altitude>
<heading>-148.4122922628044</heading>
<tilt>40.5575073395506</tilt>
<range>500.6566641072245</range>
</LookAt>
<Placemark>
<name>Simple placemark</name>
<description>Attached to the ground. Intelligently places itself at the
height of the underlying terrain.</description>
<Point>
<coordinates>-122.0822035425683,37.42228990140251,0</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Floating placemark</name>
<visibility>0</visibility>
<description>Floats a defined distance above the ground.</description>
<LookAt>
<longitude>-122.0839597145766</longitude>
<latitude>37.42222904525232</latitude>
<altitude>0</altitude>
<heading>-148.4122922628044</heading>
<tilt>40.5575073395506</tilt>
<range>500.6566641072245</range>
</LookAt>
<styleUrl>#downArrowIcon</styleUrl>
<Point>
<altitudeMode>relativeToGround</altitudeMode>
<coordinates>-122.084075,37.4220033612141,50</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Extruded placemark</name>
<visibility>0</visibility>
<description>Tethered to the ground by a customizable
&quot;tail&quot;</description>
<LookAt>
<longitude>-122.0845787421525</longitude>
<latitude>37.42215078737763</latitude>
<altitude>0</altitude>
<heading>-148.4126684946234</heading>
<tilt>40.55750733918048</tilt>
<range>365.2646606980322</range>
</LookAt>
<styleUrl>#globeIcon</styleUrl>
<Point>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
<coordinates>-122.0857667006183,37.42156927867553,50</coordinates>
</Point>
</Placemark>
</Folder>
<Folder>
<name>Styles and Markup</name>
<visibility>0</visibility>
<description>With KML it is easy to create rich, descriptive markup to
annotate and enrich your placemarks</description>
<LookAt>
<longitude>-122.0845787422371</longitude>
<latitude>37.42215078726837</latitude>
<altitude>0</altitude>
<heading>-148.4126777488172</heading>
<tilt>40.55750733930874</tilt>
<range>365.2646826292919</range>
</LookAt>
<styleUrl>#noDrivingDirections</styleUrl>
<Document>
<name>Highlighted Icon</name>
<visibility>0</visibility>
<description>Place your mouse over the icon to see it display the new
icon</description>
<LookAt>
<longitude>-122.0856552124024</longitude>
<latitude>37.4224281311035</latitude>
<altitude>0</altitude>
<heading>0</heading>
<tilt>0</tilt>
<range>265.8520424250024</range>
</LookAt>
<Style id="highlightPlacemark">
<IconStyle>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/red-stars.png</href>
</Icon>
</IconStyle>
</Style>
<Style id="normalPlacemark">
<IconStyle>
<Icon>
<href>http://maps.google.com/mapfiles/kml/paddle/wht-blank.png</href>
</Icon>
</IconStyle>
</Style>
<StyleMap id="exampleStyleMap">
<Pair>
<key>normal</key>
<styleUrl>#normalPlacemark</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#highlightPlacemark</styleUrl>
</Pair>
</StyleMap>
<Placemark>
<name>Roll over this icon</name>
<visibility>0</visibility>
<styleUrl>#exampleStyleMap</styleUrl>
<Point>
<coordinates>-122.0856545755255,37.42243077405461,0</coordinates>
</Point>
</Placemark>
</Document>
<Placemark>
<name>Descriptive HTML</name>
<visibility>0</visibility>
<description><![CDATA[Click on the blue link!<br><br>
Placemark descriptions can be enriched by using many standard HTML tags.<br>
For example:
<hr>
Styles:<br>
<i>Italics</i>,
<b>Bold</b>,
<u>Underlined</u>,
<s>Strike Out</s>,
subscript<sub>subscript</sub>,
superscript<sup>superscript</sup>,
<big>Big</big>,
<small>Small</small>,
<tt>Typewriter</tt>,
<em>Emphasized</em>,
<strong>Strong</strong>,
<code>Code</code>
<hr>
Fonts:<br>
<font color="red">red by name</font>,
<font color="#408010">leaf green by hexadecimal RGB</font>
<br>
<font size=1>size 1</font>,
<font size=2>size 2</font>,
<font size=3>size 3</font>,
<font size=4>size 4</font>,
<font size=5>size 5</font>,
<font size=6>size 6</font>,
<font size=7>size 7</font>
<br>
<font face=times>Times</font>,
<font face=verdana>Verdana</font>,
<font face=arial>Arial</font><br>
<hr>
Links:
<br>
<a href="http://earth.google.com/">Google Earth!</a>
<br>
or: Check out our website at www.google.com
<hr>
Alignment:<br>
<p align=left>left</p>
<p align=center>center</p>
<p align=right>right</p>
<hr>
Ordered Lists:<br>
<ol><li>First</li><li>Second</li><li>Third</li></ol>
<ol type="a"><li>First</li><li>Second</li><li>Third</li></ol>
<ol type="A"><li>First</li><li>Second</li><li>Third</li></ol>
<hr>
Unordered Lists:<br>
<ul><li>A</li><li>B</li><li>C</li></ul>
<ul type="circle"><li>A</li><li>B</li><li>C</li></ul>
<ul type="square"><li>A</li><li>B</li><li>C</li></ul>
<hr>
Definitions:<br>
<dl>
<dt>Google:</dt><dd>The best thing since sliced bread</dd>
</dl>
<hr>
Centered:<br><center>
Time present and time past<br>
Are both perhaps present in time future,<br>
And time future contained in time past.<br>
If all time is eternally present<br>
All time is unredeemable.<br>
</center>
<hr>
Block Quote:
<br>
<blockquote>
We shall not cease from exploration<br>
And the end of all our exploring<br>
Will be to arrive where we started<br>
And know the place for the first time.<br>
<i>-- T.S. Eliot</i>
</blockquote>
<br>
<hr>
Headings:<br>
<h1>Header 1</h1>
<h2>Header 2</h2>
<h3>Header 3</h3>
<h3>Header 4</h4>
<h3>Header 5</h5>
<hr>
Images:<br>
<i>Remote image</i><br>
<img src="//developers.google.com/kml/documentation/images/googleSample.png"><br>
<i>Scaled image</i><br>
<img src="//developers.google.com/kml/documentation/images/googleSample.png" width=100><br>
<hr>
Simple Tables:<br>
<table border="1" padding="1">
<tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr>
<tr><td>a</td><td>b</td><td>c</td><td>d</td><td>e</td></tr>
</table>
<br>
[Did you notice that double-clicking on the placemark doesn't cause the viewer to take you anywhere? This is because it is possible to directly author a "placeless placemark". If you look at the code for this example, you will see that it has neither a point coordinate nor a LookAt element.]]]></description>
</Placemark>
</Folder>
<Folder>
<name>Ground Overlays</name>
<visibility>0</visibility>
<description>Examples of ground overlays</description>
<GroundOverlay>
<name>Large-scale overlay on terrain</name>
<visibility>0</visibility>
<description>Overlay shows Mount Etna erupting on July 13th, 2001.</description>
<LookAt>
<longitude>15.02468937557116</longitude>
<latitude>37.67395167941667</latitude>
<altitude>0</altitude>
<heading>-16.5581842842829</heading>
<tilt>58.31228652890705</tilt>
<range>30350.36838438907</range>
</LookAt>
<Icon>
<href>http://developers.google.com/kml/documentation/images/etna.jpg</href>
</Icon>
<LatLonBox>
<north>37.91904192681665</north>
<south>37.46543388598137</south>
<east>15.35832653742206</east>
<west>14.60128369746704</west>
<rotation>-0.1556640799496235</rotation>
</LatLonBox>
</GroundOverlay>
</Folder>
<Folder>
<name>Screen Overlays</name>
<visibility>0</visibility>
<description>Screen overlays have to be authored directly in KML. These
examples illustrate absolute and dynamic positioning in screen space.</description>
<ScreenOverlay>
<name>Simple crosshairs</name>
<visibility>0</visibility>
<description>This screen overlay uses fractional positioning to put the
image in the exact center of the screen</description>
<Icon>
<href>http://developers.google.com/kml/documentation/images/crosshairs.png</href>
</Icon>
<overlayXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>
<screenXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>
<rotationXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>
<size x="0" y="0" xunits="pixels" yunits="pixels"/>
</ScreenOverlay>
<ScreenOverlay>
<name>Absolute Positioning: Top left</name>
<visibility>0</visibility>
<Icon>
<href>http://developers.google.com/kml/documentation/images/top_left.jpg</href>
</Icon>
<overlayXY x="0" y="1" xunits="fraction" yunits="fraction"/>
<screenXY x="0" y="1" xunits="fraction" yunits="fraction"/>
<rotationXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<size x="0" y="0" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
<ScreenOverlay>
<name>Absolute Positioning: Top right</name>
<visibility>0</visibility>
<Icon>
<href>http://developers.google.com/kml/documentation/images/top_right.jpg</href>
</Icon>
<overlayXY x="1" y="1" xunits="fraction" yunits="fraction"/>
<screenXY x="1" y="1" xunits="fraction" yunits="fraction"/>
<rotationXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<size x="0" y="0" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
<ScreenOverlay>
<name>Absolute Positioning: Bottom left</name>
<visibility>0</visibility>
<Icon>
<href>http://developers.google.com/kml/documentation/images/bottom_left.jpg</href>
</Icon>
<overlayXY x="0" y="-1" xunits="fraction" yunits="fraction"/>
<screenXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<rotationXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<size x="0" y="0" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
<ScreenOverlay>
<name>Absolute Positioning: Bottom right</name>
<visibility>0</visibility>
<Icon>
<href>http://developers.google.com/kml/documentation/images/bottom_right.jpg</href>
</Icon>
<overlayXY x="1" y="-1" xunits="fraction" yunits="fraction"/>
<screenXY x="1" y="0" xunits="fraction" yunits="fraction"/>
<rotationXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<size x="0" y="0" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
<ScreenOverlay>
<name>Dynamic Positioning: Top of screen</name>
<visibility>0</visibility>
<Icon>
<href>http://developers.google.com/kml/documentation/images/dynamic_screenoverlay.jpg</href>
</Icon>
<overlayXY x="0" y="1" xunits="fraction" yunits="fraction"/>
<screenXY x="0" y="1" xunits="fraction" yunits="fraction"/>
<rotationXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<size x="1" y="0.2" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
<ScreenOverlay>
<name>Dynamic Positioning: Right of screen</name>
<visibility>0</visibility>
<Icon>
<href>http://developers.google.com/kml/documentation/images/dynamic_right.jpg</href>
</Icon>
<overlayXY x="1" y="1" xunits="fraction" yunits="fraction"/>
<screenXY x="1" y="1" xunits="fraction" yunits="fraction"/>
<rotationXY x="0" y="0" xunits="fraction" yunits="fraction"/>
<size x="0" y="1" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
</Folder>
<Folder>
<name>Paths</name>
<visibility>0</visibility>
<description>Examples of paths. Note that the tessellate tag is by default
set to 0. If you want to create tessellated lines, they must be authored
(or edited) directly in KML.</description>
<Placemark>
<name>Tessellated</name>
<visibility>0</visibility>
<description><![CDATA[If the <tessellate> tag has a value of 1, the line will contour to the underlying terrain]]></description>
<LookAt>
<longitude>-112.0822680013139</longitude>
<latitude>36.09825589333556</latitude>
<altitude>0</altitude>
<heading>103.8120432044965</heading>
<tilt>62.04855796276328</tilt>
<range>2889.145007690472</range>
</LookAt>
<LineString>
<tessellate>1</tessellate>
<coordinates> -112.0814237830345,36.10677870477137,0
-112.0870267752693,36.0905099328766,0 </coordinates>
</LineString>
</Placemark>
<Placemark>
<name>Untessellated</name>
<visibility>0</visibility>
<description><![CDATA[If the <tessellate> tag has a value of 0, the line follow a simple straight-line path from point to point]]></description>
<LookAt>
<longitude>-112.0822680013139</longitude>
<latitude>36.09825589333556</latitude>
<altitude>0</altitude>
<heading>103.8120432044965</heading>
<tilt>62.04855796276328</tilt>
<range>2889.145007690472</range>
</LookAt>
<LineString>
<tessellate>0</tessellate>
<coordinates> -112.080622229595,36.10673460007995,0
-112.085242575315,36.09049598612422,0 </coordinates>
</LineString>
</Placemark>
<Placemark>
<name>Absolute</name>
<visibility>0</visibility>
<description>Transparent purple line</description>
<LookAt>
<longitude>-112.2719329043177</longitude>
<latitude>36.08890633450894</latitude>
<altitude>0</altitude>
<heading>-106.8161545998597</heading>
<tilt>44.60763714063257</tilt>
<range>2569.386744398339</range>
</LookAt>
<styleUrl>#transPurpleLineGreenPoly</styleUrl>
<LineString>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<coordinates> -112.265654928602,36.09447672602546,2357
-112.2660384528238,36.09342608838671,2357
-112.2668139013453,36.09251058776881,2357
-112.2677826834445,36.09189827357996,2357
-112.2688557510952,36.0913137941187,2357
-112.2694810717219,36.0903677207521,2357
-112.2695268555611,36.08932171487285,2357
-112.2690144567276,36.08850916060472,2357
-112.2681528815339,36.08753813597956,2357
-112.2670588176031,36.08682685262568,2357
-112.2657374587321,36.08646312301303,2357 </coordinates>
</LineString>
</Placemark>
<Placemark>
<name>Absolute Extruded</name>
<visibility>0</visibility>
<description>Transparent green wall with yellow outlines</description>
<LookAt>
<longitude>-112.2643334742529</longitude>
<latitude>36.08563154742419</latitude>
<altitude>0</altitude>
<heading>-125.7518698668815</heading>
<tilt>44.61038665812578</tilt>
<range>4451.842204068102</range>
</LookAt>
<styleUrl>#yellowLineGreenPoly</styleUrl>
<LineString>
<extrude>1</extrude>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<coordinates> -112.2550785337791,36.07954952145647,2357
-112.2549277039738,36.08117083492122,2357
-112.2552505069063,36.08260761307279,2357
-112.2564540158376,36.08395660588506,2357
-112.2580238976449,36.08511401044813,2357
-112.2595218489022,36.08584355239394,2357
-112.2608216347552,36.08612634548589,2357
-112.262073428656,36.08626019085147,2357
-112.2633204928495,36.08621519860091,2357
-112.2644963846444,36.08627897945274,2357
-112.2656969554589,36.08649599090644,2357 </coordinates>
</LineString>
</Placemark>
<Placemark>
<name>Relative</name>
<visibility>0</visibility>
<description>Black line (10 pixels wide), height tracks terrain</description>
<LookAt>
<longitude>-112.2580438551384</longitude>
<latitude>36.1072674824385</latitude>
<altitude>0</altitude>
<heading>4.947421249553717</heading>
<tilt>44.61324882043339</tilt>
<range>2927.61105910266</range>
</LookAt>
<styleUrl>#thickBlackLine</styleUrl>
<LineString>
<tessellate>1</tessellate>
<altitudeMode>relativeToGround</altitudeMode>
<coordinates> -112.2532845153347,36.09886943729116,645
-112.2540466121145,36.09919570465255,645
-112.254734666947,36.09984998366178,645
-112.255493345654,36.10051310621746,645
-112.2563157098468,36.10108441943419,645
-112.2568033076439,36.10159722088088,645
-112.257494011321,36.10204323542867,645
-112.2584106072308,36.10229131995655,645
-112.2596588987972,36.10240001286358,645
-112.2610581199487,36.10213176873407,645
-112.2626285262793,36.10157011437219,645 </coordinates>
</LineString>
</Placemark>
<Placemark>
<name>Relative Extruded</name>
<visibility>0</visibility>
<description>Opaque blue walls with red outline, height tracks terrain</description>
<LookAt>
<longitude>-112.2683594333433</longitude>
<latitude>36.09884362144909</latitude>
<altitude>0</altitude>
<heading>-72.24271551768405</heading>
<tilt>44.60855445139561</tilt>
<range>2184.193522571467</range>
</LookAt>
<styleUrl>#redLineBluePoly</styleUrl>
<LineString>
<extrude>1</extrude>
<tessellate>1</tessellate>
<altitudeMode>relativeToGround</altitudeMode>
<coordinates> -112.2656634181359,36.09445214722695,630
-112.2652238941097,36.09520916122063,630
-112.2645079986395,36.09580763864907,630
-112.2638827428817,36.09628572284063,630
-112.2635746835406,36.09679275951239,630
-112.2635711822407,36.09740038871899,630
-112.2640296531825,36.09804913435539,630
-112.264327720538,36.09880337400301,630
-112.2642436562271,36.09963644790288,630
-112.2639148687042,36.10055381117246,630
-112.2626894973474,36.10149062823369,630 </coordinates>
</LineString>
</Placemark>
</Folder>
<Folder>
<name>Polygons</name>
<visibility>0</visibility>
<description>Examples of polygon shapes</description>
<Folder>
<name>Google Campus</name>
<visibility>0</visibility>
<description>A collection showing how easy it is to create 3-dimensional
buildings</description>
<LookAt>
<longitude>-122.084120030116</longitude>
<latitude>37.42174011925477</latitude>
<altitude>0</altitude>
<heading>-34.82469740081282</heading>
<tilt>53.454348562403</tilt>
<range>276.7870053764046</range>
</LookAt>
<Placemark>
<name>Building 40</name>
<visibility>0</visibility>
<styleUrl>#transRedPoly</styleUrl>
<Polygon>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -122.0848938459612,37.42257124044786,17
-122.0849580979198,37.42211922626856,17
-122.0847469573047,37.42207183952619,17
-122.0845725380962,37.42209006729676,17
-122.0845954886723,37.42215932700895,17
-122.0838521118269,37.42227278564371,17
-122.083792243335,37.42203539112084,17
-122.0835076656616,37.42209006957106,17
-122.0834709464152,37.42200987395161,17
-122.0831221085748,37.4221046494946,17
-122.0829247374572,37.42226503990386,17
-122.0829339169385,37.42231242843094,17
-122.0833837359737,37.42225046087618,17
-122.0833607854248,37.42234159228745,17
-122.0834204551642,37.42237075460644,17
-122.083659133885,37.42251292011001,17
-122.0839758438952,37.42265873093781,17
-122.0842374743331,37.42265143972521,17
-122.0845036949503,37.4226514386435,17
-122.0848020460801,37.42261133916315,17
-122.0847882750515,37.42256395055121,17
-122.0848938459612,37.42257124044786,17 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
<name>Building 41</name>
<visibility>0</visibility>
<styleUrl>#transBluePoly</styleUrl>
<Polygon>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -122.0857412771483,37.42227033155257,17
-122.0858169768481,37.42231408832346,17
-122.085852582875,37.42230337469744,17
-122.0858799945639,37.42225686138789,17
-122.0858860101409,37.4222311076138,17
-122.0858069157288,37.42220250173855,17
-122.0858379542653,37.42214027058678,17
-122.0856732640519,37.42208690214408,17
-122.0856022926407,37.42214885429042,17
-122.0855902778436,37.422128290487,17
-122.0855841672237,37.42208171967246,17
-122.0854852065741,37.42210455874995,17
-122.0855067264352,37.42214267949824,17
-122.0854430712915,37.42212783846172,17
-122.0850990714904,37.42251282407603,17
-122.0856769818632,37.42281815323651,17
-122.0860162273783,37.42244918858722,17
-122.0857260327004,37.42229239604253,17
-122.0857412771483,37.42227033155257,17 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
<name>Building 42</name>
<visibility>0</visibility>
<styleUrl>#transGreenPoly</styleUrl>
<Polygon>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -122.0857862287242,37.42136208886969,25
-122.0857312990603,37.42136935989481,25
-122.0857312992918,37.42140934910903,25
-122.0856077073679,37.42138390166565,25
-122.0855802426516,37.42137299550869,25
-122.0852186221971,37.42137299504316,25
-122.0852277765639,37.42161656508265,25
-122.0852598189347,37.42160565894403,25
-122.0852598185499,37.42168200156,25
-122.0852369311478,37.42170017860346,25
-122.0852643957828,37.42176197982575,25
-122.0853239032746,37.42176198013907,25
-122.0853559454324,37.421852864452,25
-122.0854108752463,37.42188921823734,25
-122.0854795379357,37.42189285337048,25
-122.0855436229819,37.42188921797546,25
-122.0856260178042,37.42186013499926,25
-122.085937287963,37.42186013453605,25
-122.0859428718666,37.42160898590042,25
-122.0859655469861,37.42157992759144,25
-122.0858640462341,37.42147115002957,25
-122.0858548911215,37.42140571326184,25
-122.0858091162768,37.4214057134039,25
-122.0857862287242,37.42136208886969,25 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
<name>Building 43</name>
<visibility>0</visibility>
<styleUrl>#transYellowPoly</styleUrl>
<Polygon>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -122.0844371128284,37.42177253003091,19
-122.0845118855746,37.42191111542896,19
-122.0850470999805,37.42178755121535,19
-122.0850719913391,37.42143663023161,19
-122.084916406232,37.42137237822116,19
-122.0842193868167,37.42137237801626,19
-122.08421938659,37.42147617161496,19
-122.0838086419991,37.4214613409357,19
-122.0837899728564,37.42131306410796,19
-122.0832796534698,37.42129328840593,19
-122.0832609819207,37.42139213944298,19
-122.0829373621737,37.42137236399876,19
-122.0829062425667,37.42151569778871,19
-122.0828502269665,37.42176282576465,19
-122.0829435788635,37.42176776969635,19
-122.083217411188,37.42179248552686,19
-122.0835970430103,37.4217480074456,19
-122.0839455556771,37.42169364237603,19
-122.0840077894637,37.42176283815853,19
-122.084113587521,37.42174801104392,19
-122.0840762473784,37.42171341292375,19
-122.0841447047739,37.42167881534569,19
-122.084144704223,37.42181720660197,19
-122.0842503333074,37.4218170700446,19
-122.0844371128284,37.42177253003091,19 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
<Folder>
<name>Extruded Polygon</name>
<description>A simple way to model a building</description>
<Placemark>
<name>The Pentagon</name>
<LookAt>
<longitude>-77.05580139178142</longitude>
<latitude>38.870832443487</latitude>
<heading>59.88865561738225</heading>
<tilt>48.09646074797388</tilt>
<range>742.0552506670548</range>
</LookAt>
<Polygon>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -77.05788457660967,38.87253259892824,100
-77.05465973756702,38.87291016281703,100
-77.05315536854791,38.87053267794386,100
-77.05552622493516,38.868757801256,100
-77.05844056290393,38.86996206506943,100
-77.05788457660967,38.87253259892824,100 </coordinates>
</LinearRing>
</outerBoundaryIs>
<innerBoundaryIs>
<LinearRing>
<coordinates> -77.05668055019126,38.87154239798456,100
-77.05542625960818,38.87167890344077,100
-77.05485125901024,38.87076535397792,100
-77.05577677433152,38.87008686581446,100
-77.05691162017543,38.87054446963351,100
-77.05668055019126,38.87154239798456,100 </coordinates>
</LinearRing>
</innerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
<Folder>
<name>Absolute and Relative</name>
<visibility>0</visibility>
<description>Four structures whose roofs meet exactly. Turn on/off
terrain to see the difference between relative and absolute
positioning.</description>
<LookAt>
<longitude>-112.3348969157552</longitude>
<latitude>36.14845533214919</latitude>
<altitude>0</altitude>
<heading>-86.91235037566909</heading>
<tilt>49.30695423894192</tilt>
<range>990.6761201087104</range>
</LookAt>
<Placemark>
<name>Absolute</name>
<visibility>0</visibility>
<styleUrl>#transBluePoly</styleUrl>
<Polygon>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -112.3372510731295,36.14888505105317,1784
-112.3356128688403,36.14781540589019,1784
-112.3368169371048,36.14658677734382,1784
-112.3384408457543,36.14762778914076,1784
-112.3372510731295,36.14888505105317,1784 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
<name>Absolute Extruded</name>
<visibility>0</visibility>
<styleUrl>#transRedPoly</styleUrl>
<Polygon>
<extrude>1</extrude>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -112.3396586818843,36.14637618647505,1784
-112.3380597654315,36.14531751871353,1784
-112.3368254237788,36.14659596244607,1784
-112.3384555043203,36.14762621763982,1784
-112.3396586818843,36.14637618647505,1784 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
<name>Relative</name>
<visibility>0</visibility>
<LookAt>
<longitude>-112.3350152490417</longitude>
<latitude>36.14943123077423</latitude>
<altitude>0</altitude>
<heading>-118.9214100848499</heading>
<tilt>37.92486261093203</tilt>
<range>345.5169113679813</range>
</LookAt>
<styleUrl>#transGreenPoly</styleUrl>
<Polygon>
<tessellate>1</tessellate>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -112.3349463145932,36.14988705767721,100
-112.3354019540677,36.14941108398372,100
-112.3344428289146,36.14878490381308,100
-112.3331289492913,36.14780840132443,100
-112.3317019516947,36.14680755678357,100
-112.331131440106,36.1474173426228,100
-112.332616324338,36.14845453364654,100
-112.3339876620524,36.14926570522069,100
-112.3349463145932,36.14988705767721,100 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark>
<name>Relative Extruded</name>
<visibility>0</visibility>
<LookAt>
<longitude>-112.3351587892382</longitude>
<latitude>36.14979247129029</latitude>
<altitude>0</altitude>
<heading>-55.42811560891606</heading>
<tilt>56.10280503739589</tilt>
<range>401.0997279712519</range>
</LookAt>
<styleUrl>#transYellowPoly</styleUrl>
<Polygon>
<extrude>1</extrude>
<tessellate>1</tessellate>
<altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs>
<LinearRing>
<coordinates> -112.3348783983763,36.1514008468736,100
-112.3372535345629,36.14888517553886,100
-112.3356068927954,36.14781612679284,100
-112.3350034807972,36.14846469024177,100
-112.3358353861232,36.1489624162954,100
-112.3345888301373,36.15026229372507,100
-112.3337937856278,36.14978096026463,100
-112.3331798208424,36.1504472788618,100
-112.3348783983763,36.1514008468736,100 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
</Folder>
</Document>
</kml>

View File

@@ -1,446 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Red Bull X-Alps 2019 Route</name>
<snippet>https://www.redbullxalps.com/ Created by twpayne@gmail.com</snippet>
<open>1</open>
<Folder>
<name>Route</name>
<Placemark>
<LineString>
<coordinates>13.0484,47.79885 13.110917,47.804133 13.305787,47.332295 12.33277,47.784362 11.9549,46.737598 10.98526,47.4211 10.879767,47.401283 9.851879,46.815225 8.424457,46.770918 8.005393,46.577621 5.887857,45.306816 7.090381,44.667312 6.422229,44.120985 7.410751,43.755956 7.454787,43.75875</coordinates>
<tessellate>1</tessellate>
</LineString>
<Style>
<LineStyle>
<color>c0009090</color>
<width>4</width>
</LineStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Turnpoints</name>
<Folder>
<name>Salzburg</name>
<Placemark>
<Point>
<coordinates>13.0484,47.79885</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/go.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Gaisberg</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>13.110917,47.804133</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/1.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Wagrain-Kleinarl</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>13.305787,47.332295</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/2.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Aschau-Chiemsee</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>12.33277,47.784362</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/3.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Kronplatz</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>11.9549,46.737598</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/4.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Zugspitz</name>
<snippet>pass N</snippet>
<Placemark>
<Point>
<coordinates>10.98526,47.4211</coordinates>
</Point>
<Style>
<IconStyle>
<Icon>
<href>https://maps.google.com/mapfiles/kml/pal2/icon15.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Folder>
<Placemark>
<LineString>
<coordinates>10.98526,47.4211 10.98526,47.196269598520324</coordinates>
</LineString>
<Style>
<LineStyle>
<color>c00000c0</color>
<tessellate>1</tessellate>
<width>3</width>
</LineStyle>
</Style>
</Placemark>
</Folder>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Lermoos-Tiroler Zugspitz Arena</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>10.879767,47.401283</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/5.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Davos</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>9.851879,46.815225</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/6.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Titlis</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>8.424457,46.770918</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/7.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Eiger</name>
<snippet>1500m radius</snippet>
<Placemark>
<Point>
<coordinates>8.005393,46.577621</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/8.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Placemark>
<LineString>
<coordinates>8.005393,46.59111082408879 8.007411386004984,46.59103930859754 8.009408360780075,46.590825520768504 8.011362740730094,46.59047172847008 8.013253795087904,46.58998168468399 8.01506146625202,46.58936058760861 8.016766582908044,46.58861502540384 8.018351063653368,46.587752906169705 8.019798108949551,46.586783373908844 8.021092379355597,46.58571671137243 8.022220158146354,46.584564230829145 8.02316949659154,46.58333815392459 8.023930340360643,46.582051481914704 8.024494635724556,46.58071785765729 8.024856414444256,46.57935142083319 8.025011856467065,46.57796665793827 8.024959329790072,46.576578248641624 8.024699407094783,46.57520091014225 8.02423485900483,46.5738492411753 8.023570624066586,46.572537567321 8.022713755798401,46.57127978925356 8.02167334739507,46.57008923553433 8.020460434907957,46.56897852150325 8.019087879945149,46.56795941575718 8.01757023314834,46.5670427156218 8.015923579901438,46.56623813292746 8.014165369908543,46.565554191290495 8.012314232444112,46.56499813597875 8.01038977922428,46.564575857307794 8.00841239697435,46.56429182837157 8.006403031871999,46.5641490577601 8.004382968128002,46.5641490577601 8.002373603025651,46.56429182837157 8.00039622077572,46.564575857307794 7.998471767555888,46.56499813597875 7.996620630091457,46.565554191290495 7.9948624200985625,46.56623813292746 7.99321576685166,46.5670427156218 7.991698120054851,46.56795941575718 7.990325565092044,46.56897852150325 7.989112652604931,46.57008923553433 7.9880722442016,46.57127978925356 7.987215375933413,46.572537567321 7.986551140995171,46.5738492411753 7.986086592905218,46.57520091014225 7.98582667020993,46.576578248641624 7.985774143532935,46.57796665793827 7.985929585555744,46.57935142083319 7.986291364275444,46.58071785765729 7.9868556596393585,46.582051481914704 7.987616503408459,46.58333815392459 7.988565841853647,46.584564230829145 7.989693620644403,46.58571671137243 7.990987891050449,46.586783373908844 7.992434936346632,46.587752906169705 7.994019417091955,46.58861502540384 7.995724533747981,46.58936058760861 7.997532204912097,46.58998168468399 7.999423259269906,46.59047172847008 8.001377639219925,46.590825520768504 8.003374613995016,46.59103930859754 8.005393,46.59111082408879</coordinates>
</LineString>
<Style>
<LineStyle>
<color>c000c000</color>
<tessellate>1</tessellate>
<width>3</width>
</LineStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Mont Blanc</name>
<snippet>pass N</snippet>
<Placemark>
<Point>
<coordinates>6.867674,45.830359</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/9.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Folder>
<Placemark>
<LineString>
<coordinates>6.867674,45.830359 6.867674,45.605528598520316</coordinates>
</LineString>
<Style>
<LineStyle>
<color>c00000c0</color>
<tessellate>1</tessellate>
<width>3</width>
</LineStyle>
</Style>
</Placemark>
</Folder>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>St. Hilare</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>5.887857,45.306816</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/10.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Monte Viso</name>
<snippet>2250m radius</snippet>
<Placemark>
<Point>
<coordinates>7.090381,44.667312</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/A.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Placemark>
<LineString>
<coordinates>7.090381,44.68754673613318 7.092762589980537,44.6874757453242 7.095127457411984,44.687263271366724 7.09745899753014,44.686910806157954 7.099740840300694,44.68642082451503 7.101956965690705,44.68579676674505 7.104091816445464,44.685043014417296 7.106130407568226,44.68416485951042 7.108058431724881,44.68316846715423 7.109862359825947,44.6820608322314 7.111529536074018,44.680849730147806 7.113048266805791,44.67954366212132 7.114407902503551,44.678151795378 7.115598912401194,44.67668389867965 7.1166129511641305,44.675150273640085 7.117442917180201,44.673561682316134 7.118083002059652,44.67192927158581 7.118528731005785,44.67026449484756 7.118776993783513,44.668579031593154 7.11882606608039,44.666884705420784 7.118675621123042,44.66519340106522 7.118326731480924,44.663516981028 7.1177818610584405,44.661867202392706 7.117044847345161,44.66025563440796 7.116120874061689,44.65869357741505 7.115016434405281,44.65719198368645 7.113739285164013,44.65576138072787 7.1122983920308585,44.65441179757801 7.110703866508997,44.653152694619315 7.108966894856653,44.65199289738741 7.1070996595734846,44.650940534838895 7.105115253980594,44.65000298250571 7.103027590492422,44.64918681092998 7.1008513032207565,44.64849773973612 7.098601645588721,44.64794059765821 7.096294383665553,44.64751928879869 7.093945685961249,44.64723676535147 7.091572010443376,44.64709500697793 7.0891899895566235,44.64709500697793 7.08681631403875,44.64723676535147 7.084467616334447,44.64751928879869 7.08216035441128,44.64794059765821 7.079910696779244,44.64849773973612 7.0777344095075785,44.64918681092998 7.075646746019405,44.65000298250571 7.073662340426515,44.650940534838895 7.071795105143346,44.65199289738741 7.070058133491003,44.653152694619315 7.068463607969141,44.65441179757801 7.0670227148359865,44.65576138072787 7.065745565594718,44.65719198368645 7.06464112593831,44.65869357741505 7.063717152654839,44.66025563440796 7.062980138941559,44.661867202392706 7.062435268519076,44.663516981028 7.062086378876957,44.66519340106522 7.061935933919609,44.666884705420784 7.061985006216487,44.668579031593154 7.062233268994214,44.67026449484756 7.062678997940347,44.67192927158581 7.063319082819799,44.673561682316134 7.064149048835869,44.675150273640085 7.065163087598806,44.67668389867965 7.06635409749645,44.678151795378 7.067713733194209,44.67954366212132 7.0692324639259825,44.680849730147806 7.070899640174052,44.6820608322314 7.072703568275118,44.68316846715423 7.074631592431774,44.68416485951042 7.076670183554536,44.685043014417296 7.078805034309295,44.68579676674505 7.081021159699306,44.68642082451503 7.08330300246986,44.686910806157954 7.085634542588016,44.687263271366724 7.087999410019463,44.6874757453242 7.090381,44.68754673613318</coordinates>
</LineString>
<Style>
<LineStyle>
<color>c000c000</color>
<tessellate>1</tessellate>
<width>3</width>
</LineStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Cheval Blanc</name>
<snippet>pass W</snippet>
<Placemark>
<Point>
<coordinates>6.422229,44.120985</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/B.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Folder>
<Placemark>
<LineString>
<coordinates>6.422229,44.120985 6.7354178618529215,44.12055721299625</coordinates>
</LineString>
<Style>
<LineStyle>
<color>c00000c0</color>
<tessellate>1</tessellate>
<width>3</width>
</LineStyle>
</Style>
</Placemark>
</Folder>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Peille</name>
<snippet>signboard</snippet>
<Placemark>
<Point>
<coordinates>7.410751,43.755956</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/stop.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
<Folder>
<name>Monaco</name>
<Placemark>
<Point>
<coordinates>7.454787,43.75875</coordinates>
</Point>
<Style>
<IconStyle>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"></hotSpot>
<Icon>
<href>https://maps.google.com/mapfiles/kml/paddle/ylw-stars.png</href>
</Icon>
</IconStyle>
</Style>
</Placemark>
<Style>
<ListStyle>
<listItemType>checkHideChildren</listItemType>
</ListStyle>
</Style>
</Folder>
</Folder>
</Document>
</kml>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

View File

@@ -1,19 +0,0 @@
{
"name": "leaflet-kml",
"version": "1.0.1",
"description": "Leaflet KML layer plugin",
"main": "L.KML.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/windycom/leaflet-kml.git"
},
"author": "Pavel Shramov, Bruno Bergot",
"license": "MIT",
"bugs": {
"url": "https://github.com/windycom/leaflet-kml/issues"
},
"homepage": "https://github.com/windycom/leaflet-kml#readme"
}

View File

@@ -41,7 +41,7 @@ map.on('mousemove', function(e)
MyApp.onMapMove(e.latlng.lat, e.latlng.lng); MyApp.onMapMove(e.latlng.lat, e.latlng.lng);
const bounds = map.getBounds(); const bounds = map.getBounds();
MyApp.onMapZoom(bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast()); MyApp.onMapZoom(bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast(), map.getZoom());
}); });
var DrawShapes; var DrawShapes;

View File

@@ -7,7 +7,7 @@
# Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/ # Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/
import math import math
# from PIL import Image from PIL import Image
import os import os
import urllib import urllib
@@ -15,7 +15,7 @@ import urllib
# alternativa a PIL: Image # alternativa a PIL: Image
# CV2 # CV2
class GoogleMapDownloader: class GoogleMapDownloader1:
""" """
A class which generates high resolution google maps images given A class which generates high resolution google maps images given
a longitude, latitude and zoom level a longitude, latitude and zoom level

View File

@@ -0,0 +1,207 @@
import math
from PIL import Image
import urllib.request
from io import BytesIO
import time
class GoogleMapDownloader:
def __init__(self, zoom=12, layer='raw_satellite'):
self._zoom = zoom
self.layer_map = {
'roadmap': 'm',
'terrain': 'p',
'satellite': 's',
'hybrid': 'y',
'raw_satellite': 's'
}
self._layer = self.layer_map.get(layer, 's')
self._style = 'style=feature:all|element:labels|visibility:off' if layer == 'raw_satellite' else ''
def latlng_to_tile(self, lat, lng):
"""Convierte coordenadas a tiles X/Y con precisión decimal"""
tile_size = 256
numTiles = 1 << self._zoom
point_x = (tile_size / 2 + lng * tile_size / 360.0) * numTiles / tile_size
sin_y = math.sin(lat * (math.pi / 180.0))
point_y = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) *
-(tile_size / (2 * math.pi))) * numTiles / tile_size
return point_x, point_y
def generateImage(self, sw_lat, sw_lng, ne_lat, ne_lng):
"""Genera la imagen para un área rectangular definida por coordenadas"""
# Convertir coordenadas a tiles con precisión decimal
sw_x, sw_y = self.latlng_to_tile(sw_lat, sw_lng)
ne_x, ne_y = self.latlng_to_tile(ne_lat, ne_lng)
# Asegurar que las coordenadas estén en el orden correcto
min_x = min(sw_x, ne_x)
max_x = max(sw_x, ne_x)
min_y = min(sw_y, ne_y)
max_y = max(sw_y, ne_y)
# Calcular los tiles mínimos y máximos necesarios
min_tile_x = math.floor(min_x)
max_tile_x = math.ceil(max_x)
min_tile_y = math.floor(min_y)
max_tile_y = math.ceil(max_y)
# Calcular dimensiones en tiles
tile_width = int(max_tile_x - min_tile_x) + 1
tile_height = int(max_tile_y - min_tile_y) + 1
# Crear imagen temporal para todos los tiles necesarios
full_img = Image.new('RGB', (tile_width * 256, tile_height * 256))
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
servers = ['mt0', 'mt1', 'mt2', 'mt3']
for x in range(min_tile_x, max_tile_x + 1):
for y in range(min_tile_y, max_tile_y + 1):
server = servers[(x + y) % len(servers)]
base_url = f"https://{server}.google.com/vt?lyrs={self._layer}&x={x}&y={y}&z={self._zoom}"
url = f"{base_url}&{self._style}" if self._style else base_url
try:
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as response:
tile_data = response.read()
img = Image.open(BytesIO(tile_data))
pos_x = (x - min_tile_x) * 256
pos_y = (y - min_tile_y) * 256
full_img.paste(img, (pos_x, pos_y))
#print(f"✅ Tile ({x}, {y}) descargado")
except Exception as e:
#print(f"❌ Error en tile ({x},{y}): {str(e)}")
error_tile = Image.new('RGB', (256, 256), (255, 0, 0))
full_img.paste(error_tile, (pos_x, pos_y))
time.sleep(0.05)
# Calcular desplazamientos para recorte final
left_offset = int((min_x - min_tile_x) * 256)
right_offset = int((max_tile_x - max_x) * 256)
top_offset = int((min_y - min_tile_y) * 256)
bottom_offset = int((max_tile_y - max_y) * 256)
# Calcular coordenadas de recorte
left = left_offset
top = top_offset
right = full_img.width - right_offset
bottom = full_img.height - bottom_offset
# Asegurar que las coordenadas sean válidas
if right < left:
right = left + 1
if bottom < top:
bottom = top + 1
# Recortar la imagen al área exacta solicitada
result = full_img.crop((
left,
top,
right,
bottom
))
return full_img
class GoogleMapDownloader_1:
def __init__(self, zoom=12, layer='hybrid'):
"""
Args:
zoom: Zoom level (0-23)
layer: Map type (roadmap, terrain, satellite, hybrid)
"""
self._zoom = zoom
self.layer_map = {
'roadmap': 'm',
'terrain': 'p',
'satellite': 's',
'hybrid': 'y',
'raw_satellite': 's' # Capa especial sin etiquetas
}
self._layer = self.layer_map.get(layer, 's')
self._style = 'style=feature:all|element:labels|visibility:off' if layer == 'raw_satellite' else ''
def latlng_to_tile(self, lat, lng):
"""Convierte coordenadas a tiles X/Y"""
tile_size = 256
numTiles = 1 << self._zoom
# Cálculo para coordenada X
point_x = (tile_size / 2 + lng * tile_size / 360.0) * numTiles / tile_size
# Cálculo para coordenada Y
sin_y = math.sin(lat * (math.pi / 180.0))
point_y = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) *
-(tile_size / (2 * math.pi))) * numTiles / tile_size
return int(point_x), int(point_y)
def generateImage(self, sw_lat, sw_lng, ne_lat, ne_lng):
"""
Genera la imagen para un área rectangular definida por:
- sw_lat, sw_lng: Esquina suroeste (latitud, longitud)
- ne_lat, ne_lng: Esquina noreste (latitud, longitud)
"""
# Convertir coordenadas a tiles
sw_x, sw_y = self.latlng_to_tile(sw_lat, sw_lng)
ne_x, ne_y = self.latlng_to_tile(ne_lat, ne_lng)
# Determinar rango de tiles
min_x = min(sw_x, ne_x)
max_x = max(sw_x, ne_x)
min_y = min(sw_y, ne_y)
max_y = max(sw_y, ne_y)
# Calcular dimensiones en tiles
tile_width = max_x - min_x + 1
tile_height = max_y - min_y + 1
# Crear imagen final
result = Image.new('RGB', (256 * tile_width, 256 * tile_height))
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
servers = ['mt0', 'mt1', 'mt2', 'mt3']
print(f"Descargando {tile_width}x{tile_height} tiles ({tile_width * tile_height} total)")
for x in range(min_x, max_x + 1):
for y in range(min_y, max_y + 1):
# Seleccionar servidor rotatorio
server = servers[(x + y) % len(servers)]
# Construir URL con parámetro para quitar etiquetas si es necesario
url = f"https://{server}.google.com/vt?lyrs={self._layer}&x={x}&y={y}&z={self._zoom}"
if self._style:
url = f"{url}&{self._style}"
print("Descargando tile:", url)
try:
# Descargar tile
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as response:
tile_data = response.read()
# Procesar en memoria
img = Image.open(BytesIO(tile_data))
pos_x = (x - min_x) * 256
pos_y = (y - min_y) * 256
result.paste(img, (pos_x, pos_y))
print(f"✅ Tile ({x}, {y}) descargado")
except Exception as e:
print(f"❌ Error en tile ({x},{y}): {str(e)}")
# Crear tile de error (rojo)
error_tile = Image.new('RGB', (256, 256), (255, 0, 0))
pos_x = (x - min_x) * 256
pos_y = (y - min_y) * 256
result.paste(error_tile, (pos_x, pos_y))
# Pausa para evitar bloqueos
time.sleep(0.05)
return result

View File

@@ -2,8 +2,8 @@
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata"> <package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
<name>PVPlant</name> <name>PVPlant</name>
<description>FreeCAD Fotovoltaic Power Plant Toolkit</description> <description>FreeCAD Fotovoltaic Power Plant Toolkit</description>
<version>2025.02.22</version> <version>2025.07.06</version>
<date>2025.02.22</date> <date>2025.07.06</date>
<maintainer email="javier.branagutierrez@gmail.com">Javier Braña</maintainer> <maintainer email="javier.branagutierrez@gmail.com">Javier Braña</maintainer>
<license file="LICENSE">LGPL-2.1-or-later</license> <license file="LICENSE">LGPL-2.1-or-later</license>
<url type="repository" branch="main">https://homehud.duckdns.org/javier/PVPlant</url> <url type="repository" branch="main">https://homehud.duckdns.org/javier/PVPlant</url>