Compare commits
2 Commits
5dd8869caf
...
5a642a4119
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a642a4119 | |||
| 74bf60101c |
@@ -224,7 +224,6 @@ def spreadsheetBOQPoles(sheet: Worksheet, selection: List[FreeCAD.DocumentObject
|
||||
# Write data to sheet
|
||||
row = 3
|
||||
group_from = row
|
||||
print(poles_data[0])
|
||||
f = poles_data[0]['frame']
|
||||
for i, data in enumerate(poles_data):
|
||||
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"
|
||||
except:
|
||||
pass
|
||||
|
||||
sheet['G{0}'.format(row)] = round(data['head_z']) * scale
|
||||
sheet['G{0}'.format(row)].number_format = "0.000"
|
||||
sheet['H{0}'.format(row)] = data["length"] * scale
|
||||
|
||||
1016
Export/exportDXF.py
1016
Export/exportDXF.py
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>715</width>
|
||||
<height>520</height>
|
||||
<width>462</width>
|
||||
<height>282</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -78,6 +78,11 @@
|
||||
<string>Lineweight</string>
|
||||
</property>
|
||||
</column>
|
||||
<item row="2" column="1">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -204,6 +209,21 @@
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<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>
|
||||
<widget class="QPushButton" name="buttonAcept">
|
||||
<property name="text">
|
||||
|
||||
@@ -44,32 +44,24 @@ except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
try:
|
||||
import collada
|
||||
COLLADA_AVAILABLE = True
|
||||
except ImportError:
|
||||
COLLADA_AVAILABLE = False
|
||||
|
||||
__title__ = "FreeCAD PVSyst importer"
|
||||
__author__ = "Javier"
|
||||
__author__ = "Javier Braña"
|
||||
#__url__ = "http://www.freecadweb.org"
|
||||
|
||||
try:
|
||||
# Python 2 forward compatibility
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||
|
||||
|
||||
def checkCollada():
|
||||
"checks if collada if available"
|
||||
|
||||
global collada
|
||||
COLLADA = None
|
||||
try:
|
||||
import collada
|
||||
except ImportError:
|
||||
FreeCAD.Console.PrintError(translate("Arch", "pycollada not found, collada support is disabled.") + "\n")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
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:
|
||||
@@ -79,6 +71,7 @@ def triangulate(shape):
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||
mesher = p.GetInt("ColladaMesher", 0)
|
||||
tessellation = p.GetFloat("ColladaTessellation", 1.0)
|
||||
|
||||
grading = p.GetFloat("ColladaGrading", 0.3)
|
||||
segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
|
||||
segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
|
||||
@@ -91,9 +84,15 @@ def triangulate(shape):
|
||||
elif mesher == 1:
|
||||
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
|
||||
else:
|
||||
return MeshPart.meshFromShape(Shape=shape, GrowthRate=grading, SegPerEdge=segsperedge,
|
||||
SegPerRadius=segsperradius, SecondOrder=secondorder, Optimize=optimize,
|
||||
AllowQuad=allowquads).Topology
|
||||
return MeshPart.meshFromShape(Shape=shape,
|
||||
GrowthRate=grading,
|
||||
SegPerEdge=segsperedge,
|
||||
SegPerRadius=segsperradius,
|
||||
SecondOrder=secondorder,
|
||||
Optimize=optimize,
|
||||
AllowQuad=allowquads
|
||||
).Topology
|
||||
|
||||
|
||||
def export(exportList, filename, tessellation=1, colors=None):
|
||||
"""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)
|
||||
|
||||
def exportToDAE(path):
|
||||
filename = path + ".dae"
|
||||
|
||||
def exportToPVC(path, exportTerrain = False):
|
||||
filename = path + ".pvc"
|
||||
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||
filename = f"{path}.pvc"
|
||||
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
import datetime
|
||||
@@ -528,6 +524,7 @@ def exportToPVC(path, exportTerrain = False):
|
||||
# TODO: revisar
|
||||
for typ in frameType:
|
||||
isTracker = "tracker" in typ.Proxy.Type.lower()
|
||||
isTracker = False
|
||||
|
||||
objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
tmp = []
|
||||
@@ -818,8 +815,6 @@ class PVSystTaskPanel:
|
||||
name = date + "-" + name
|
||||
filename = os.path.join(path, name)
|
||||
|
||||
#if self.form.cbDAE.isChecked():
|
||||
# exportToDAE(filename)
|
||||
|
||||
if self.form.cbPVC.isChecked():
|
||||
exportToPVC(filename, self.form.cbTerrain.isChecked())
|
||||
|
||||
@@ -9,6 +9,7 @@ import urllib.request
|
||||
import math
|
||||
import utm
|
||||
from collections import defaultdict
|
||||
import PVPlantImportGrid as ImportElevation
|
||||
|
||||
scale = 1000.0
|
||||
|
||||
@@ -42,8 +43,10 @@ class OSMImporter:
|
||||
self.ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
|
||||
def transform_from_latlon(self, lat, lon):
|
||||
x, y, _, _ = utm.from_latlon(lat, lon)
|
||||
return FreeCAD.Vector(x, y, .0) * scale - self.Origin
|
||||
point = ImportElevation.getElevationFromOE([[lat, lon], ])
|
||||
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):
|
||||
query = f"""
|
||||
@@ -92,10 +95,24 @@ class OSMImporter:
|
||||
'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
|
||||
}
|
||||
|
||||
print("1. Create Transportations")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_transportation()
|
||||
|
||||
print("2. Create Buildings")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_buildings()
|
||||
|
||||
print("3. Create Power Infrastructure")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_power_infrastructure()
|
||||
|
||||
print("4. Create Vegetation")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_vegetation()
|
||||
|
||||
print("5. Create Water Bodies")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_water_bodies()
|
||||
|
||||
def create_transportation(self):
|
||||
@@ -118,27 +135,26 @@ class OSMImporter:
|
||||
'secondary': 5.0,
|
||||
'tertiary': 4.0
|
||||
}.get(highway_type, 3.0)
|
||||
|
||||
self.create_road(nodes, width, highway_type, transport_layer)
|
||||
self.create_road(nodes, width, highway_type, transport_layer, tags['name'] if 'name' in tags else "")
|
||||
|
||||
# Vías férreas
|
||||
if 'railway' in tags:
|
||||
self.create_railway(nodes, transport_layer)
|
||||
self.create_railway(nodes, transport_layer, tags['name'] if 'name' in tags else "")
|
||||
|
||||
def create_road(self, nodes, width, road_type, layer):
|
||||
def create_road(self, nodes, width, road_type, layer, name=""):
|
||||
points = [n for n in nodes]
|
||||
polyline = Draft.make_wire(points, closed=False, face=False)
|
||||
polyline.Label = f"Road_{road_type}"
|
||||
polyline.Label = f"Road_{name if (name != '') else road_type}"
|
||||
polyline.ViewObject.LineWidth = 2.0
|
||||
polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5))
|
||||
polyline.addProperty("App::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
|
||||
polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
|
||||
layer.addObject(polyline)
|
||||
|
||||
def create_railway(self, nodes, layer):
|
||||
def create_railway(self, nodes, layer, name=""):
|
||||
points = [n for n in nodes]
|
||||
rail_line = Draft.make_wire(points, closed=False, face=False)
|
||||
rail_line.Label = "Railway"
|
||||
rail_line.Label = f"{name if (name != '') else 'Railway'}"
|
||||
rail_line.ViewObject.LineWidth = 1.5
|
||||
rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
|
||||
layer.addObject(rail_line)
|
||||
@@ -149,6 +165,8 @@ class OSMImporter:
|
||||
if 'building' not in data['tags']:
|
||||
continue
|
||||
|
||||
print(data)
|
||||
|
||||
tags = data['tags']
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
|
||||
@@ -205,9 +223,12 @@ class OSMImporter:
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
|
||||
if 'power' in tags:
|
||||
print("\n\n")
|
||||
print(tags)
|
||||
feature_type = tags['power']
|
||||
|
||||
if feature_type == 'line':
|
||||
print("3.1. Create Power Lines")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_power_line(
|
||||
nodes=nodes,
|
||||
tags=tags,
|
||||
@@ -215,6 +236,8 @@ class OSMImporter:
|
||||
)
|
||||
|
||||
elif feature_type == 'substation':
|
||||
print("3.1. Create substations")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_substation(
|
||||
way_id=way_id,
|
||||
tags=tags,
|
||||
@@ -223,11 +246,17 @@ class OSMImporter:
|
||||
)
|
||||
|
||||
elif feature_type == 'tower':
|
||||
print("3.1. Create power towers")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_power_tower(
|
||||
position=nodes[0] if nodes else None,
|
||||
tags=tags,
|
||||
layer=power_layer
|
||||
)
|
||||
'''self.create_power_tower_1(
|
||||
way=way_id,
|
||||
layer=power_layer
|
||||
)'''
|
||||
|
||||
def create_power_line(self, nodes, tags, layer):
|
||||
"""Crea líneas de transmisión eléctrica con propiedades técnicas"""
|
||||
@@ -244,10 +273,10 @@ class OSMImporter:
|
||||
return
|
||||
|
||||
wire = Draft.make_wire(points, closed=False, face=False)
|
||||
wire.Label = f"Power_Line_{voltage}V"
|
||||
wire.Label = f"Power_Line_{int(voltage/1000000)}kV"
|
||||
|
||||
# Propiedades visuales
|
||||
wire.ViewObject.LineWidth = 1 + (voltage / 100000)
|
||||
wire.ViewObject.LineWidth = 6 #'''1 + (voltage / 100000)'''
|
||||
color = self.feature_colors['power']['line']
|
||||
wire.ViewObject.ShapeColor = color
|
||||
|
||||
@@ -260,7 +289,7 @@ class OSMImporter:
|
||||
layer.addObject(wire)
|
||||
|
||||
# Añadir torres si es overhead
|
||||
if line_type == 'overhead':
|
||||
'''if line_type == 'overhead':
|
||||
distance_between_towers = 150 # metros por defecto
|
||||
if 'distance_between_towers' in tags:
|
||||
try:
|
||||
@@ -273,7 +302,7 @@ class OSMImporter:
|
||||
voltage=voltage,
|
||||
distance=distance_between_towers,
|
||||
layer=layer
|
||||
)
|
||||
)'''
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")
|
||||
@@ -318,7 +347,8 @@ class OSMImporter:
|
||||
|
||||
# Unir componentes
|
||||
tower = base.fuse(mast)
|
||||
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
|
||||
tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Power_Tower")
|
||||
layer.addObject(tower_obj)
|
||||
tower_obj.Shape = tower
|
||||
tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']
|
||||
|
||||
@@ -354,6 +384,164 @@ class OSMImporter:
|
||||
cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
|
||||
layer.addObject(cable)
|
||||
|
||||
def create_power_tower_1(self, way, layer):
|
||||
"""Crea una torre eléctrica según especificaciones OSM"""
|
||||
tags = way['tags']
|
||||
nodes = [self.nodes[ref] for ref in way['nodes'] if ref in self.nodes]
|
||||
|
||||
if not nodes:
|
||||
return
|
||||
|
||||
try:
|
||||
# Obtener parámetros principales
|
||||
structure_type = tags.get('structure', 'lattice')
|
||||
material = tags.get('material', 'steel')
|
||||
height = float(tags.get('height', 30.0)) # Altura en metros
|
||||
voltage = self.parse_voltage(tags.get('voltage', '132000')) # 132kV por defecto
|
||||
lines = int(tags.get('lines', '3'))
|
||||
operator = tags.get('operator', '')
|
||||
tower_name = tags.get('name', f"Tower_{way['id']}")
|
||||
|
||||
# Calcular posición (usar primer nodo)
|
||||
position = FreeCAD.Vector(*nodes[0])
|
||||
|
||||
# Configurar dimensiones basadas en parámetros
|
||||
base_size = self.calculate_base_size(structure_type, height)
|
||||
crossarm_length = self.calculate_crossarm_length(voltage, lines)
|
||||
color = self.get_material_color(material)
|
||||
|
||||
# Crear geometría según tipo de estructura
|
||||
if structure_type == 'lattice':
|
||||
tower = self.create_lattice_tower(height, base_size, crossarm_length)
|
||||
elif structure_type == 'tubular':
|
||||
tower = self.create_tubular_tower(height, base_size, crossarm_length)
|
||||
elif structure_type == 'portal':
|
||||
tower = self.create_portal_tower(height, base_size, crossarm_length)
|
||||
else:
|
||||
tower = self.create_default_tower(height, base_size, crossarm_length)
|
||||
|
||||
# Crear objeto en FreeCAD
|
||||
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
|
||||
tower_obj.Shape = tower
|
||||
tower_obj.ViewObject.ShapeColor = color
|
||||
tower_obj.Placement.Base = position
|
||||
|
||||
# Añadir propiedades técnicas
|
||||
tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Voltaje nominal (V)").Voltage = voltage
|
||||
tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Altura total (m)").Height = height
|
||||
tower_obj.addProperty("App::PropertyString", "StructureType", "Technical",
|
||||
"Tipo de estructura").StructureType = structure_type
|
||||
tower_obj.addProperty("App::PropertyString", "Material", "Technical",
|
||||
"Material de construcción").Material = material
|
||||
tower_obj.addProperty("App::PropertyInteger", "Lines", "Technical", "Número de circuitos").Lines = lines
|
||||
if operator:
|
||||
tower_obj.addProperty("App::PropertyString", "Operator", "General", "Operador").Operator = operator
|
||||
|
||||
# Añadir cables si existen nodos de conexión
|
||||
if len(nodes) >= 2:
|
||||
connection_points = [FreeCAD.Vector(*n) for n in nodes]
|
||||
self.create_power_lines_between_towers(connection_points, voltage, layer)
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creando torre {way['id']}: {str(e)}\n")
|
||||
|
||||
def create_lattice_tower(self, height, base_size, crossarm_length):
|
||||
"""Crea torre de celosía tipo armazón"""
|
||||
# Base
|
||||
base = Part.makeBox(base_size, base_size, 3.0)
|
||||
|
||||
# Patas principales
|
||||
leg_profile = Part.makeBox(0.5, 0.5, height)
|
||||
legs = []
|
||||
for x in [-base_size / 2, base_size / 2]:
|
||||
for y in [-base_size / 2, base_size / 2]:
|
||||
leg = leg_profile.copy()
|
||||
leg.translate(FreeCAD.Vector(x, y, 3.0))
|
||||
legs.append(leg)
|
||||
|
||||
# Travesaños horizontales
|
||||
horizontal_bars = []
|
||||
for z in [10.0, height / 2, height - 5.0]:
|
||||
bar = Part.makeBox(base_size + 1.0, 0.3, 0.3, FreeCAD.Vector(-(base_size + 1) / 2, -0.15, z))
|
||||
horizontal_bars.append(bar)
|
||||
|
||||
# Crucetas
|
||||
crossarms = self.create_crossarms(height, crossarm_length)
|
||||
|
||||
# Unir todas las partes
|
||||
tower = base.multiFuse(legs + horizontal_bars + crossarms)
|
||||
return tower.removeSplitter()
|
||||
|
||||
def create_crossarms(self, height, length):
|
||||
"""Crea crucetas para líneas eléctricas"""
|
||||
crossarms = []
|
||||
positions = [
|
||||
(height - 5.0, 0), # Superior
|
||||
(height - 15.0, 22.5), # Media con ángulo
|
||||
(height - 25.0, -22.5) # Inferior con ángulo
|
||||
]
|
||||
|
||||
for z, angle in positions:
|
||||
crossarm = Part.makeBox(length, 0.3, 0.3)
|
||||
crossarm.rotate(FreeCAD.Vector(0, 0, z), FreeCAD.Vector(0, 0, 1), angle)
|
||||
crossarm.translate(FreeCAD.Vector(-length / 2, -0.15, z))
|
||||
crossarms.append(crossarm)
|
||||
|
||||
return crossarms
|
||||
|
||||
def calculate_base_size(self, structure_type, height):
|
||||
"""Calcula tamaño de base según tipo de estructura y altura"""
|
||||
base_sizes = {
|
||||
'lattice': 4.0 + (height * 0.1),
|
||||
'tubular': 3.0 + (height * 0.05),
|
||||
'portal': 6.0,
|
||||
'default': 3.0
|
||||
}
|
||||
return base_sizes.get(structure_type, base_sizes['default'])
|
||||
|
||||
def calculate_crossarm_length(self, voltage, lines):
|
||||
"""Calcula longitud de crucetas según voltaje y número de circuitos"""
|
||||
return (voltage / 100000) * 8.0 + (lines * 2.0)
|
||||
|
||||
def get_material_color(self, material):
|
||||
"""Devuelve color según material de construcción"""
|
||||
colors = {
|
||||
'steel': (0.65, 0.65, 0.7),
|
||||
'concrete': (0.8, 0.8, 0.8),
|
||||
'wood': (0.5, 0.3, 0.2),
|
||||
'aluminum': (0.9, 0.9, 0.9),
|
||||
'default': (0.5, 0.5, 0.5)
|
||||
}
|
||||
return colors.get(material.lower(), colors['default'])
|
||||
|
||||
def create_power_lines_between_towers(self, points, voltage, layer):
|
||||
"""Crea cables entre torres"""
|
||||
cable_thickness = 0.1 + (voltage / 500000)
|
||||
|
||||
for i in range(len(points) - 1):
|
||||
start = points[i]
|
||||
end = points[i + 1]
|
||||
|
||||
# Crear cable curvo (catenaria)
|
||||
cable = self.create_catenary(start, end, sag=5.0)
|
||||
cable_obj = layer.addObject("Part::Feature", f"Power_Line_{i}")
|
||||
cable_obj.Shape = cable
|
||||
cable_obj.ViewObject.LineWidth = cable_thickness * 1000 # Convertir a mm
|
||||
cable_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)
|
||||
|
||||
def create_catenary(self, start, end, sag=5.0):
|
||||
"""Crea curva de catenaria para cables eléctricos"""
|
||||
mid_point = (start + end) / 2
|
||||
mid_point.z -= sag
|
||||
|
||||
arc = Part.Arc(
|
||||
start,
|
||||
mid_point,
|
||||
end
|
||||
)
|
||||
|
||||
return Part.Edge(arc.toShape())
|
||||
|
||||
def create_substation(self, way_id, tags, nodes, layer):
|
||||
"""Crea subestaciones con todos los componentes detallados"""
|
||||
try:
|
||||
@@ -551,27 +739,163 @@ class OSMImporter:
|
||||
def create_vegetation(self):
|
||||
vegetation_layer = self.create_layer("Vegetation")
|
||||
|
||||
# Árboles individuales
|
||||
for node_id, coords in self.nodes.items():
|
||||
# Verificar si es un árbol
|
||||
# (Necesitarías procesar los tags de los nodos, implementación simplificada)
|
||||
cylinder = Part.makeCylinder(0.5, 5.0, FreeCAD.Vector(coords[0], coords[1], 0))# * scale - self.Origin )
|
||||
tree = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
|
||||
vegetation_layer.addObject(tree)
|
||||
tree.Shape = cylinder
|
||||
tree.ViewObject.ShapeColor = self.feature_colors['vegetation']
|
||||
# Procesar nodos de vegetación individual
|
||||
for way_id, tags in self.ways_data.items():
|
||||
coords = self.nodes.get(way_id)
|
||||
if not coords:
|
||||
continue
|
||||
|
||||
# Á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():
|
||||
if 'natural' in data['tags'] or 'landuse' in data['tags']:
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
if len(nodes) > 2:
|
||||
polygon_points = [n for n in nodes]
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
area = vegetation_layer.addObject("Part::Feature", "GreenArea")
|
||||
area.Shape = face
|
||||
area.ViewObject.ShapeColor = self.feature_colors['vegetation']
|
||||
tags = data['tags']
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.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)
|
||||
face = Part.Face(polygon)
|
||||
forest_base = FreeCAD.ActiveDocument.addObject("Part::Feature", "Forest_Base")
|
||||
layer.addObject(forest_base)
|
||||
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):
|
||||
water_layer = self.create_layer("Water")
|
||||
@@ -583,7 +907,8 @@ class OSMImporter:
|
||||
polygon_points = [n for n in nodes]
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
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.ViewObject.ShapeColor = self.feature_colors['water']
|
||||
|
||||
|
||||
13
InitGui.py
13
InitGui.py
@@ -48,7 +48,7 @@ class PVPlantWorkbench(Workbench):
|
||||
self.projectlist = PVPlantTools.projectlist
|
||||
self.projectlist.insert(0, 'Reload')
|
||||
self.projectlist.insert(1, 'Separator')
|
||||
self.framelist = PVPlantTools.pv_list
|
||||
self.framelist = PVPlantTools.pv_mechanical
|
||||
|
||||
from Export import ExporterCommands
|
||||
self.inportExportlist = ExporterCommands.Exportlist
|
||||
@@ -77,14 +77,14 @@ class PVPlantWorkbench(Workbench):
|
||||
|
||||
# Toolbar
|
||||
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("Outputs", self.inportExportlist) # creates a new toolbar with your commands
|
||||
self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands
|
||||
|
||||
# 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("&Outputs", self.inportExportlist) # creates a new menu
|
||||
self.appendMenu("&Electrical", self.electricalList) # creates a new menu
|
||||
@@ -145,13 +145,14 @@ class PVPlantWorkbench(Workbench):
|
||||
import SelectionObserver
|
||||
import FreeCADGui
|
||||
|
||||
self.observer = SelectionObserver.SelObserver()
|
||||
FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
|
||||
#self.observer = SelectionObserver.SelObserver()
|
||||
#FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
|
||||
return
|
||||
|
||||
def Deactivated(self):
|
||||
"This function is executed when the workbench is deactivated"
|
||||
FreeCADGui.Selection.removeObserver(self.observer)
|
||||
|
||||
#FreeCADGui.Selection.removeObserver(self.observer)
|
||||
return
|
||||
|
||||
def ContextMenu(self, recipient):
|
||||
|
||||
@@ -45,24 +45,11 @@ __title__ = "PVPlant Frames"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
|
||||
import os
|
||||
import PVPlantResources
|
||||
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:
|
||||
"A Base Frame Setup Class"
|
||||
def __init__(self, obj):
|
||||
@@ -72,6 +59,7 @@ class FrameSetup:
|
||||
def setProperties(self, obj):
|
||||
''' Definición de Propiedades: '''
|
||||
pl = obj.PropertiesList
|
||||
|
||||
# Modulo: ------------------------------------------------------------------------------------------------------
|
||||
if not "ModuleThick" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
@@ -175,14 +163,15 @@ class FrameSetup:
|
||||
"The facemaker type to use to build the profile of this object")
|
||||
)
|
||||
|
||||
# Frame --------------------------------------------------------------------------------------------------------
|
||||
'''if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
# Poles --------------------------------------------------------------------------------------------------------
|
||||
if not "PoleType" in pl:
|
||||
obj.addProperty("App::PropertyLinkList",
|
||||
"PoleType",
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).Tilt = 30'''
|
||||
)
|
||||
|
||||
# Frame --------------------------------------------------------------------------------------------------------
|
||||
if not "MaxLengthwiseTilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"MaxLengthwiseTilt",
|
||||
@@ -217,268 +206,99 @@ class FrameSetup:
|
||||
)
|
||||
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 --------------------------------------------------- '''
|
||||
|
||||
|
||||
def makeRack(name="Rack"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
_FixedRack(obj)
|
||||
_ViewProviderFixedRack(obj.ViewObject)
|
||||
# FreeCAD.ActiveDocument.recompute()
|
||||
obj.Label = name
|
||||
FixedRack(obj)
|
||||
ViewProviderFixedRack(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
|
||||
class _FixedRack(Frame):
|
||||
class FixedRack(FrameSetup):
|
||||
"A Fixed Rack Obcject"
|
||||
|
||||
def __init__(self, obj):
|
||||
# Definición de Variables:
|
||||
Frame.__init__(self, obj)
|
||||
Frame.setProperties(self, obj)
|
||||
#FrameSetup.__init__(self, obj)
|
||||
super(FixedRack, self).__init__(obj)
|
||||
self.setProperties(obj)
|
||||
obj.ModuleColumns = 6
|
||||
obj.ModuleRows = 2
|
||||
obj.ModuleColGap = 20
|
||||
obj.ModuleRowGap = 20
|
||||
#obj.Tilt = 30
|
||||
# Does a IfcType exist?
|
||||
# obj.IfcType = "Fence"
|
||||
# obj.MoveWithHost = False
|
||||
|
||||
def setProperties(self, obj):
|
||||
FrameSetup.setProperties(self, obj)
|
||||
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: ------------------------------------------------------------------------------------------------------
|
||||
if not "NumberPostsX" in pl:
|
||||
@@ -486,7 +306,7 @@ class _FixedRack(Frame):
|
||||
"NumberPostsX",
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).NumberPostsX = 5
|
||||
).NumberPostsX = 3
|
||||
|
||||
if not "DistancePostsX" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
@@ -515,6 +335,7 @@ class _FixedRack(Frame):
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).RammingDeep = 1500
|
||||
|
||||
# Correas: ----------------------------------------------------------------------------------------------------
|
||||
if not "BeamHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
@@ -545,9 +366,11 @@ class _FixedRack(Frame):
|
||||
).BeamSpacing = 1000
|
||||
|
||||
self.Type = "Fixed Rack"
|
||||
obj.Proxy = self
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
#ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
super(FixedRack, self).onDocumentRestored(obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
@@ -557,153 +380,64 @@ class _FixedRack(Frame):
|
||||
if 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):
|
||||
'''Do something when a property has changed'''
|
||||
# FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
|
||||
|
||||
def CalculateModuleArray(self, obj, totalh, totalw, moduleh, modulew):
|
||||
# ModuleThick
|
||||
# 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
|
||||
def generate_modules(self, obj, h, w):
|
||||
modulos=[]
|
||||
for y in range(int(obj.ModuleRows.Value)):
|
||||
for x in range(int(obj.ModuleCols.Value)):
|
||||
xx = totalArea.Placement.Base.x + (modulew + obj.ModuleColGap.Value) * x
|
||||
yy = totalArea.Placement.Base.y + (moduleh + obj.ModuleRowGap.Value) * y
|
||||
zz = obj.BeamHeight.Value
|
||||
moduleCopy = module.copy()
|
||||
moduleCopy.Placement.Base.x = xx
|
||||
moduleCopy.Placement.Base.y = yy
|
||||
moduleCopy.Placement.Base.z = zz
|
||||
self.ListModules.append(moduleCopy)
|
||||
for x in range(int(obj.ModuleColumns.Value)):
|
||||
mod = Part.makeBox(w, h, obj.ModuleThick.Value)
|
||||
mod.Placement.Base = FreeCAD.Vector(
|
||||
x * (w + obj.ModuleColGap.Value),
|
||||
y * (h + obj.ModuleRowGap.Value),
|
||||
0
|
||||
)
|
||||
modulos.append(mod)
|
||||
|
||||
compound = Part.makeCompound(self.ListModules)
|
||||
compound.Placement.Base.z = correaoffsetz - obj.RammingDeep.Value
|
||||
compound = Part.makeCompound(modulos)
|
||||
'''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)
|
||||
postFront = Part.makeBox(obj.FrontPostWidth.Value, obj.FrontPostHeight.Value, obj.FrontPostLength.Value)
|
||||
|
||||
angle = obj.Placement.Rotation.toEuler()[1]
|
||||
offsetX = (-obj.DistancePostsX.Value * (obj.NumberPostsX.Value - 1)) / 2
|
||||
|
||||
# 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
|
||||
|
||||
post_back = []
|
||||
post_front = []
|
||||
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.Placement.Base.x = xx
|
||||
postCopy.Placement.Base.y = yy
|
||||
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)
|
||||
postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, obj.DistancePostsY.Value, 0)
|
||||
post_back.append(postCopy)
|
||||
|
||||
if obj.FrontPost:
|
||||
postCopy = postFront.copy()
|
||||
postCopy.Placement.Base.x = xx
|
||||
postCopy.Placement.Base.y = -yy
|
||||
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)
|
||||
postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, 0, 0)
|
||||
post_front.append(postCopy)
|
||||
|
||||
del postBack
|
||||
del postFront
|
||||
return Part.makeCompound([Part.makeCompound(post_back), Part.makeCompound(post_front)])
|
||||
|
||||
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):
|
||||
# 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
|
||||
|
||||
del self.ListModules[:]
|
||||
del self.ListPosts[:]
|
||||
|
||||
self.ListModules = []
|
||||
self.ListPosts = []
|
||||
|
||||
if obj.ModuleOrientation == "Portrait":
|
||||
w = obj.ModuleWidth.Value
|
||||
h = obj.ModuleHeight.Value
|
||||
@@ -712,63 +446,38 @@ class _FixedRack(Frame):
|
||||
w = obj.ModuleHeight.Value
|
||||
|
||||
totalh = h * obj.ModuleRows + obj.ModuleRowGap.Value * (obj.ModuleRows - 1)
|
||||
totalw = w * obj.ModuleCols + obj.ModuleColGap.Value * (obj.ModuleCols - 1)
|
||||
obj.Width = totalw
|
||||
obj.Length = totalh
|
||||
totalw = w * obj.ModuleColumns + obj.ModuleColGap.Value * (obj.ModuleColumns - 1)
|
||||
|
||||
# hacer el shape:
|
||||
self.CalculateModuleArray(obj, totalh, totalw, h, w)
|
||||
self.CalculatePosts(obj, totalh, totalw)
|
||||
modules = self.generate_modules(obj, h, w)
|
||||
modules.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), obj.Tilt.Value)
|
||||
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 = []
|
||||
allShapes.extend(self.ListModules)
|
||||
allShapes.extend(self.ListPosts)
|
||||
compound = Part.makeCompound(allShapes)
|
||||
compound = Part.makeCompound([modules, posts])
|
||||
obj.Shape = compound
|
||||
obj.Placement = pl
|
||||
|
||||
angle = obj.Placement.Rotation.toEuler()[1]
|
||||
if angle > obj.MaxLengthwiseTilt:
|
||||
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"
|
||||
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
'''
|
||||
ArchSite._ViewProviderSite.__init__(self, vobj)
|
||||
vobj.Proxy = self
|
||||
vobj.addExtension("Gui::ViewProviderGroupExtensionPython", self)
|
||||
self.setProperties(vobj)
|
||||
'''
|
||||
|
||||
def getIcon(self):
|
||||
""" Return the path to the appropriate icon. """
|
||||
return str(os.path.join(DirIcons, "solar-fixed.svg"))
|
||||
|
||||
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"):
|
||||
taskd = _FixedRackTaskPanel(self.Object)
|
||||
taskd.obj = self.Object
|
||||
@@ -783,15 +492,7 @@ class _FixedRackTaskPanel:
|
||||
# -------------------------------------------------------------------------------------------------------------
|
||||
# Module widget form
|
||||
# -------------------------------------------------------------------------------------------------------------
|
||||
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.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]
|
||||
self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "Mechanical/Frame/PVPlantFixedFrame.ui")
|
||||
|
||||
def selectionchange(self, i):
|
||||
vis = False
|
||||
@@ -839,7 +540,6 @@ def makeTrackerSetup(name="TrackerSetup"):
|
||||
pass
|
||||
return obj
|
||||
|
||||
|
||||
def getarray(array, numberofpoles):
|
||||
if len(array) == 0:
|
||||
newarray = [0] * numberofpoles
|
||||
@@ -899,36 +599,15 @@ class TrackerSetup(FrameSetup):
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).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: ------------------------------------------------------------------------------------------------------
|
||||
'''movido a la clase madre
|
||||
if not "PoleType" in pl:
|
||||
obj.addProperty("App::PropertyLinkList",
|
||||
"PoleType",
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
)
|
||||
)'''
|
||||
|
||||
if not "PoleSequence" in pl:
|
||||
obj.addProperty("App::PropertyIntegerList",
|
||||
@@ -1272,6 +951,7 @@ class TrackerSetup(FrameSetup):
|
||||
modules = self.CalculateModuleArray(obj, totalh, totalw, h, w)
|
||||
beams = self.calculateBeams(obj, totalh, totalw, h, w)
|
||||
poles, poleaxis = self.CalculatePosts(obj, totalh, totalw)
|
||||
|
||||
compound = Part.makeCompound([modules, beams])
|
||||
compound.Placement.Base.z = obj.MainBeamAxisPosition.Value - (obj.MainBeamHeight.Value / 2)
|
||||
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.TotalPower = obj.ModulePower.Value * obj.ModuleRows * obj.ModuleColumns
|
||||
|
||||
|
||||
class ViewProviderTrackerSetup:
|
||||
"A View Provider for the TrackerSetup object"
|
||||
|
||||
@@ -1297,6 +978,7 @@ class ViewProviderTrackerSetup:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def makeTracker(name = "Tracker", setup = None):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tracker")
|
||||
obj.Label = name
|
||||
@@ -1306,6 +988,7 @@ def makeTracker(name = "Tracker", setup = None):
|
||||
obj.Setup = setup
|
||||
return obj
|
||||
|
||||
|
||||
class Tracker(ArchComponent.Component):
|
||||
"A 1 Axis single row Tracker Obcject"
|
||||
|
||||
@@ -1461,95 +1144,7 @@ class ViewProviderTracker(ArchComponent.ViewProviderComponent):
|
||||
|
||||
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:
|
||||
def __init__(self, obj=None):
|
||||
|
||||
@@ -1657,7 +1252,7 @@ class _FrameTaskPanel:
|
||||
return True
|
||||
|
||||
|
||||
class _CommandFixedRack:
|
||||
class CommandFixedRack:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1672,11 +1267,11 @@ class _CommandFixedRack:
|
||||
|
||||
def Activated(self):
|
||||
obj = makeRack()
|
||||
self.TaskPanel = _FixedRackTaskPanel(obj)
|
||||
FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
#self.TaskPanel = _FixedRackTaskPanel(obj)
|
||||
#FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
return
|
||||
|
||||
class _CommandTrackerSetup:
|
||||
class CommandTrackerSetup:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1687,6 +1282,7 @@ class _CommandTrackerSetup:
|
||||
"Creates a TrackerSetup object from setup dialog.")}
|
||||
|
||||
def IsActive(self):
|
||||
return True
|
||||
return (not FreeCAD.ActiveDocument is None and
|
||||
not FreeCAD.ActiveDocument.getObject("Site") is None)
|
||||
|
||||
@@ -1696,7 +1292,7 @@ class _CommandTrackerSetup:
|
||||
FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
return
|
||||
|
||||
class _CommandTracker:
|
||||
class CommandTracker:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1720,51 +1316,3 @@ class _CommandTracker:
|
||||
break
|
||||
makeTracker(setup=setupobj)
|
||||
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())
|
||||
|
||||
@@ -845,7 +845,7 @@ class _FenceTaskPanel:
|
||||
|
||||
|
||||
# Commands ---------------------------------------------------------------------------------
|
||||
class _CommandPVPlantFence:
|
||||
class CommandPVPlantFence:
|
||||
"the PVPlant Fence command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -882,14 +882,7 @@ if FreeCAD.GuiUp:
|
||||
not (FreeCAD.ActiveDocument.getObject("Terrain") is None))
|
||||
|
||||
import PVPlantFenceGate
|
||||
FreeCADGui.addCommand('PVPlantFence', _CommandPVPlantFence())
|
||||
FreeCADGui.addCommand('PVPlantFence', CommandPVPlantFence())
|
||||
FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate())
|
||||
FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost())
|
||||
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)
|
||||
#FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
|
||||
|
||||
@@ -52,7 +52,9 @@ class MapWindow(QtGui.QWidget):
|
||||
self.maxLat = None
|
||||
self.minLon = None
|
||||
self.maxLon = None
|
||||
self.zoom = None
|
||||
self.WinTitle = WinTitle
|
||||
self.georeference_coordinates = {'lat': None, 'lon': None}
|
||||
self.setupUi()
|
||||
|
||||
def setupUi(self):
|
||||
@@ -152,6 +154,9 @@ class MapWindow(QtGui.QWidget):
|
||||
self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
|
||||
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)
|
||||
RightLayout.addItem(verticalSpacer)
|
||||
|
||||
@@ -192,6 +197,7 @@ class MapWindow(QtGui.QWidget):
|
||||
"var data = drawnItems.toGeoJSON();"
|
||||
"MyApp.shapes(JSON.stringify(data));"
|
||||
)
|
||||
|
||||
self.close()
|
||||
|
||||
@QtCore.Slot(float, float)
|
||||
@@ -203,17 +209,22 @@ class MapWindow(QtGui.QWidget):
|
||||
' | UTM: ' + str(zone_number) + zone_letter +
|
||||
', {:.5f}m E, {:.5f}m N'.format(x, y))
|
||||
|
||||
@QtCore.Slot(float, float, float, float)
|
||||
def onMapZoom(self, minLat, minLon, maxLat, maxLon):
|
||||
@QtCore.Slot(float, float, float, float, int)
|
||||
def onMapZoom(self, minLat, minLon, maxLat, maxLon, zoom):
|
||||
self.minLat = min([minLat, maxLat])
|
||||
self.maxLat = max([minLat, maxLat])
|
||||
self.minLon = min([minLon, maxLon])
|
||||
self.maxLon = max([minLon, maxLon])
|
||||
self.zoom = zoom
|
||||
|
||||
@QtCore.Slot(float, float)
|
||||
def georeference(self, lat, lng):
|
||||
import PVPlantSite
|
||||
from geopy.geocoders import Nominatim
|
||||
|
||||
self.georeference_coordinates['lat'] = lat
|
||||
self.georeference_coordinates['lon'] = lng
|
||||
|
||||
Site = PVPlantSite.get(create=True)
|
||||
Site.Proxy.setLatLon(lat, lng)
|
||||
|
||||
@@ -278,7 +289,7 @@ class MapWindow(QtGui.QWidget):
|
||||
pts = [p.sub(offset) for p in tmp]
|
||||
|
||||
obj = Draft.makeWire(pts, closed=cw, face=False)
|
||||
#obj.Placement.Base = offset
|
||||
#obj.Placement.Base = Site.Origin
|
||||
obj.Label = name
|
||||
Draft.autogroup(obj)
|
||||
|
||||
@@ -288,6 +299,170 @@ class MapWindow(QtGui.QWidget):
|
||||
if self.checkboxImportGis.isChecked():
|
||||
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):
|
||||
import Importer.importOSM as importOSM
|
||||
import PVPlantSite
|
||||
|
||||
@@ -125,9 +125,9 @@ def getElevationFromOE(coordinates):
|
||||
points = []
|
||||
for i, point in enumerate(coordinates):
|
||||
c = utm.from_latlon(point[0], point[1])
|
||||
points.append(FreeCAD.Vector(round(c[0] * 1000, 0),
|
||||
round(c[1] * 1000, 0),
|
||||
0))
|
||||
points.append(FreeCAD.Vector(round(c[0], 0),
|
||||
round(c[1], 0),
|
||||
0) * 1000)
|
||||
return points
|
||||
|
||||
# Only get the json response in case of 200 or 201
|
||||
@@ -136,14 +136,16 @@ def getElevationFromOE(coordinates):
|
||||
results = r.json()
|
||||
for point in results["results"]:
|
||||
c = utm.from_latlon(point["latitude"], point["longitude"])
|
||||
v = FreeCAD.Vector(round(c[0] * 1000, 0),
|
||||
round(c[1] * 1000, 0),
|
||||
round(point["elevation"] * 1000, 0))
|
||||
v = FreeCAD.Vector(round(c[0], 0),
|
||||
round(c[1], 0),
|
||||
round(point["elevation"], 0)) * 1000
|
||||
points.append(v)
|
||||
return points
|
||||
|
||||
def getSinglePointElevationFromBing(lat, lng):
|
||||
#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 += str(lat) + "," + str(lng)
|
||||
source += "&heights=sealevel"
|
||||
@@ -153,11 +155,9 @@ def getSinglePointElevationFromBing(lat, lng):
|
||||
response = requests.get(source)
|
||||
ans = response.text
|
||||
|
||||
# +# to do: error handling - wait and try again
|
||||
s = json.loads(ans)
|
||||
print(s)
|
||||
res = s['resourceSets'][0]['resources'][0]['elevations']
|
||||
|
||||
import utm
|
||||
for elevation in res:
|
||||
c = utm.from_latlon(lat, lng)
|
||||
v = FreeCAD.Vector(
|
||||
@@ -324,7 +324,6 @@ def getSinglePointElevationUtm(lat, lon):
|
||||
print (v)
|
||||
return v
|
||||
|
||||
|
||||
def getElevationUTM(polygon, lat, lng, resolution = 10000):
|
||||
|
||||
import utm
|
||||
@@ -448,47 +447,6 @@ def getElevation(lat, lon, b=50.35, le=11.17, size=40):
|
||||
FreeCADGui.updateGui()
|
||||
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:
|
||||
|
||||
def __init__(self, obj = None):
|
||||
|
||||
@@ -138,8 +138,6 @@ class _Manhole(ArchComponent.Component):
|
||||
obj.Shape = ext_sol.cut([ins_sol, ], 0.0)
|
||||
|
||||
|
||||
|
||||
|
||||
class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
|
||||
"A View Provider for the Pipe object"
|
||||
|
||||
|
||||
@@ -578,22 +578,22 @@ class _PVPlantSite(ArchSite._Site):
|
||||
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"Boundary",
|
||||
"Site",
|
||||
"PVPlant",
|
||||
"Boundary of land")
|
||||
|
||||
obj.addProperty("App::PropertyLinkList",
|
||||
"Frames",
|
||||
"Site",
|
||||
"PVPlant",
|
||||
"Frames templates")
|
||||
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"UtmZone",
|
||||
"Base",
|
||||
"PVPlant",
|
||||
"UTM zone").UtmZone = zone_list
|
||||
|
||||
obj.addProperty("App::PropertyVector",
|
||||
"Origin",
|
||||
"Base",
|
||||
"PVPlant",
|
||||
"Origin point.").Origin = (0, 0, 0)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
@@ -771,10 +771,12 @@ class _PVPlantSite(ArchSite._Site):
|
||||
import PVPlantImportGrid
|
||||
x, y, zone_number, zone_letter = utm.from_latlon(lat, lon)
|
||||
self.obj.UtmZone = zone_list[zone_number - 1]
|
||||
# self.obj.UtmZone = "Z"+str(zone_number)
|
||||
#z = PVPlantImportGrid.get_elevation(lat, lon)
|
||||
zz = PVPlantImportGrid.getSinglePointElevationFromBing(lat, lon)
|
||||
self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz.z)
|
||||
zz = PVPlantImportGrid.getElevationFromOE([[lat, lon]])
|
||||
self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz[0].z)
|
||||
#self.obj.OriginOffset = FreeCAD.Vector(x * 1000, y * 1000, 0) #??
|
||||
self.obj.Latitude = lat
|
||||
self.obj.Longitude = lon
|
||||
self.obj.Elevation = zz[0].z
|
||||
|
||||
|
||||
class _ViewProviderSite(ArchSite._ViewProviderSite):
|
||||
|
||||
@@ -651,6 +651,32 @@ if FreeCAD.GuiUp:
|
||||
import Project.GenerateExternalDocument as GED
|
||||
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",
|
||||
"PVPlantSite",
|
||||
"ProjectSetup",
|
||||
@@ -678,14 +704,12 @@ projectlist = [ # "Reload",
|
||||
'newExternalDocument',
|
||||
]
|
||||
|
||||
pv_list = [
|
||||
# "RackType",
|
||||
# "PVPlantRackCheck",
|
||||
# "Separator",
|
||||
pv_mechanical = [
|
||||
"RackType",
|
||||
"PVPlantPlacement",
|
||||
"PVPlantAdjustToTerrain",
|
||||
"PVPlantConvertTo",
|
||||
# "PVArea"
|
||||
]
|
||||
|
||||
objectlist = ['PVPlantTree',]
|
||||
objectlist = ['PVPlantTree',
|
||||
'PVPlantFence',]
|
||||
Binary file not shown.
@@ -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;
|
||||
}
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
# Leaflet KML layer plugin
|
||||
|
||||

|
||||
|
||||
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
|
||||
@@ -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
|
||||
"tail"</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>
|
||||
@@ -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 |
@@ -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"
|
||||
}
|
||||
@@ -41,7 +41,7 @@ map.on('mousemove', function(e)
|
||||
MyApp.onMapMove(e.latlng.lat, e.latlng.lng);
|
||||
|
||||
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;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/
|
||||
|
||||
import math
|
||||
# from PIL import Image
|
||||
from PIL import Image
|
||||
import os
|
||||
import urllib
|
||||
|
||||
@@ -15,7 +15,7 @@ import urllib
|
||||
# alternativa a PIL: Image
|
||||
# CV2
|
||||
|
||||
class GoogleMapDownloader:
|
||||
class GoogleMapDownloader1:
|
||||
"""
|
||||
A class which generates high resolution google maps images given
|
||||
a longitude, latitude and zoom level
|
||||
|
||||
207
lib/GoogleSatelitalImageDownload.py
Normal file
207
lib/GoogleSatelitalImageDownload.py
Normal 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
|
||||
@@ -2,8 +2,8 @@
|
||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||
<name>PVPlant</name>
|
||||
<description>FreeCAD Fotovoltaic Power Plant Toolkit</description>
|
||||
<version>2025.02.22</version>
|
||||
<date>2025.02.22</date>
|
||||
<version>2025.07.06</version>
|
||||
<date>2025.07.06</date>
|
||||
<maintainer email="javier.branagutierrez@gmail.com">Javier Braña</maintainer>
|
||||
<license file="LICENSE">LGPL-2.1-or-later</license>
|
||||
<url type="repository" branch="main">https://homehud.duckdns.org/javier/PVPlant</url>
|
||||
|
||||
Reference in New Issue
Block a user