Compare commits
9 Commits
5dd8869caf
...
developed
| Author | SHA1 | Date | |
|---|---|---|---|
| 4476afc1a2 | |||
| d61260fdd3 | |||
| 049898c939 | |||
| 3a188cc47d | |||
| 5db8f5439d | |||
| e1e1441892 | |||
| d009cb7695 | |||
| 5a642a4119 | |||
| 74bf60101c |
4
.idea/PVPlant.iml
generated
4
.idea/PVPlant.iml
generated
@@ -5,4 +5,8 @@
|
|||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PackageRequirementsSettings">
|
||||||
|
<option name="removeUnused" value="true" />
|
||||||
|
<option name="modifyBaseFiles" value="true" />
|
||||||
|
</component>
|
||||||
</module>
|
</module>
|
||||||
@@ -20,16 +20,18 @@
|
|||||||
# * *
|
# * *
|
||||||
# ***********************************************************************
|
# ***********************************************************************
|
||||||
|
|
||||||
|
import FreeCAD
|
||||||
|
import Part
|
||||||
|
import Draft
|
||||||
|
import MeshPart as mp
|
||||||
|
import ArchComponent
|
||||||
|
|
||||||
|
import Civil.Fence.PVPlantFencePost as PVPlantFencePost
|
||||||
|
import PVPlantSite
|
||||||
|
import Utils.PVPlantUtils as utils
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
|
|
||||||
import ArchComponent
|
|
||||||
import Draft
|
|
||||||
import FreeCAD
|
|
||||||
import Part
|
|
||||||
|
|
||||||
import PVPlantFencePost
|
|
||||||
import PVPlantSite
|
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
import FreeCADGui
|
import FreeCADGui
|
||||||
@@ -56,26 +58,28 @@ from PVPlantResources import DirIcons as DirIcons
|
|||||||
|
|
||||||
EAST = FreeCAD.Vector(1, 0, 0)
|
EAST = FreeCAD.Vector(1, 0, 0)
|
||||||
|
|
||||||
def makeprojection(pathwire):
|
|
||||||
site = FreeCAD.ActiveDocument.Site
|
|
||||||
land = site.Terrain.Shape
|
|
||||||
proj = land.makeParallelProjection(pathwire, FreeCAD.Vector(0, 0, 1))
|
|
||||||
return proj
|
|
||||||
|
|
||||||
def makePVPlantFence(section, post, path):
|
def makePVPlantFence(section, post, path):
|
||||||
obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Fence')
|
obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Fence')
|
||||||
|
|
||||||
_Fence(obj)
|
Fence(obj)
|
||||||
obj.Post = post
|
obj.Post = post
|
||||||
obj.Base = path
|
obj.Base = path
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
_ViewProviderFence(obj.ViewObject)
|
ViewProviderFence(obj.ViewObject)
|
||||||
|
|
||||||
hide(section)
|
hide(section)
|
||||||
hide(post)
|
hide(post)
|
||||||
hide(path)
|
hide(path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
fende_group = FreeCAD.ActiveDocument.Fences
|
||||||
|
except:
|
||||||
|
fende_group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Fences')
|
||||||
|
fende_group.Label = "Fences"
|
||||||
|
FreeCAD.ActiveDocument.CivilGroup.addObject(fende_group)
|
||||||
|
fende_group.addObject(obj)
|
||||||
|
|
||||||
FreeCAD.ActiveDocument.recompute()
|
FreeCAD.ActiveDocument.recompute()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@@ -83,16 +87,8 @@ def hide(obj):
|
|||||||
if hasattr(obj, 'ViewObject') and obj.ViewObject:
|
if hasattr(obj, 'ViewObject') and obj.ViewObject:
|
||||||
obj.ViewObject.Visibility = False
|
obj.ViewObject.Visibility = False
|
||||||
|
|
||||||
def getAngle(Line1, Line2):
|
def get_parameter_from_v0_old(edge, offset):
|
||||||
v1 = Line1.Vertexes[1].Point - Line1.Vertexes[0].Point
|
""" Return parameter at distance offset from edge.Vertexes[0].sb method in Part.TopoShapeEdge??? """
|
||||||
v2 = Line2.Vertexes[1].Point - Line2.Vertexes[0].Point
|
|
||||||
return v1.getAngle(v2)
|
|
||||||
|
|
||||||
|
|
||||||
def get_parameter_from_v0(edge, offset):
|
|
||||||
"""Return parameter at distance offset from edge.Vertexes[0].
|
|
||||||
sb method in Part.TopoShapeEdge???
|
|
||||||
"""
|
|
||||||
import DraftVecUtils
|
import DraftVecUtils
|
||||||
|
|
||||||
lpt = edge.valueAt(edge.getParameterByLength(0))
|
lpt = edge.valueAt(edge.getParameterByLength(0))
|
||||||
@@ -106,14 +102,16 @@ def get_parameter_from_v0(edge, offset):
|
|||||||
length = offset
|
length = offset
|
||||||
return edge.getParameterByLength(length)
|
return edge.getParameterByLength(length)
|
||||||
|
|
||||||
|
def get_parameter_from_v0(edge, offset):
|
||||||
|
"""Parámetro a distancia offset desde el primer vértice"""
|
||||||
|
lpt = edge.valueAt(edge.getParameterByLength(0))
|
||||||
|
vpt = edge.Vertexes[0].Point
|
||||||
|
|
||||||
|
if not vpt.isEqual(lpt, 1e-6):
|
||||||
|
return edge.getParameterByLength(edge.Length - offset)
|
||||||
|
return edge.getParameterByLength(offset)
|
||||||
|
|
||||||
def calculatePlacement(globalRotation, edge, offset, RefPt, xlate, align, normal=None):
|
def calculatePlacement(globalRotation, edge, offset, RefPt, xlate, align, normal=None):
|
||||||
"""Orient shape to tangent at parm offset along edge."""
|
|
||||||
import functools
|
|
||||||
import DraftVecUtils
|
|
||||||
# http://en.wikipedia.org/wiki/Euler_angles
|
|
||||||
# start with null Placement point so translate goes to right place.
|
|
||||||
|
|
||||||
placement = FreeCAD.Placement()
|
placement = FreeCAD.Placement()
|
||||||
placement.Rotation = globalRotation
|
placement.Rotation = globalRotation
|
||||||
placement.move(RefPt + xlate)
|
placement.move(RefPt + xlate)
|
||||||
@@ -121,55 +119,23 @@ def calculatePlacement(globalRotation, edge, offset, RefPt, xlate, align, normal
|
|||||||
if not align:
|
if not align:
|
||||||
return placement
|
return placement
|
||||||
|
|
||||||
# unit +Z Probably defined elsewhere?
|
t = edge.tangentAt(get_parameter_from_v0(edge, offset)).normalize()
|
||||||
z = FreeCAD.Vector(0, 0, 1)
|
n = normal or FreeCAD.Vector(0, 0, 1)
|
||||||
# y = FreeCAD.Vector(0, 1, 0) # unit +Y
|
b = t.cross(n).normalize()
|
||||||
x = FreeCAD.Vector(1, 0, 0) # unit +X
|
|
||||||
nullv = FreeCAD.Vector(0, 0, 0)
|
|
||||||
|
|
||||||
# get local coord system - tangent, normal, binormal, if possible
|
# Asegurar sistema de coordenadas derecho
|
||||||
t = edge.tangentAt(get_parameter_from_v0(edge, offset))
|
if n.dot(t.cross(b)) < 0:
|
||||||
t.normalize()
|
b = -b
|
||||||
n = normal
|
|
||||||
b = t.cross(n)
|
|
||||||
b.normalize()
|
|
||||||
|
|
||||||
lnodes = z.cross(b)
|
# Construir matriz
|
||||||
try:
|
rotation_matrix = FreeCAD.Matrix(
|
||||||
# Can't normalize null vector.
|
t.x, b.x, n.x, 0,
|
||||||
lnodes.normalize()
|
t.y, b.y, n.y, 0,
|
||||||
except:
|
t.z, b.z, n.z, 0,
|
||||||
# pathological cases:
|
0, 0, 0, 1
|
||||||
pass
|
)
|
||||||
|
|
||||||
print(b, " - ", b.dot(z))
|
|
||||||
if abs(b.dot(z)) == 1.0: # 2) binormal is || z
|
|
||||||
# align shape to tangent only
|
|
||||||
psi = math.degrees(DraftVecUtils.angle(x, t, z))
|
|
||||||
theta = 0.0
|
|
||||||
phi = 0.0
|
|
||||||
FreeCAD.Console.PrintWarning("Draft PathArray.orientShape - Gimbal lock. Infinite lnodes. Change Path or Base.\n")
|
|
||||||
else: # regular case
|
|
||||||
psi = math.degrees(DraftVecUtils.angle(x, lnodes, z))
|
|
||||||
theta = math.degrees(DraftVecUtils.angle(z, b, lnodes))
|
|
||||||
phi = math.degrees(DraftVecUtils.angle(lnodes, t, b))
|
|
||||||
|
|
||||||
rotations = [placement.Rotation]
|
|
||||||
|
|
||||||
if psi != 0.0:
|
|
||||||
rotations.insert(0, FreeCAD.Rotation(z, psi))
|
|
||||||
if theta != 0.0:
|
|
||||||
rotations.insert(0, FreeCAD.Rotation(lnodes, theta))
|
|
||||||
if phi != 0.0:
|
|
||||||
rotations.insert(0, FreeCAD.Rotation(b, phi))
|
|
||||||
|
|
||||||
if len(rotations) == 1:
|
|
||||||
finalRotation = rotations[0]
|
|
||||||
else:
|
|
||||||
finalRotation = functools.reduce(lambda rot1, rot2: rot1.multiply(rot2), rotations)
|
|
||||||
|
|
||||||
placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), finalRotation.toEuler()[2])
|
|
||||||
|
|
||||||
|
placement.Rotation = FreeCAD.Rotation(rotation_matrix)
|
||||||
return placement
|
return placement
|
||||||
|
|
||||||
def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align):
|
def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align):
|
||||||
@@ -183,12 +149,8 @@ def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align):
|
|||||||
import DraftGeomUtils
|
import DraftGeomUtils
|
||||||
|
|
||||||
closedpath = DraftGeomUtils.isReallyClosed(pathwire)
|
closedpath = DraftGeomUtils.isReallyClosed(pathwire)
|
||||||
normal = DraftGeomUtils.getNormal(pathwire)
|
|
||||||
if normal:
|
normal = FreeCAD.Vector(0, 0, 1)
|
||||||
if normal.z < 0: # asegurarse de que siempre se dibuje por encima del suelo
|
|
||||||
normal.z *= -1
|
|
||||||
else:
|
|
||||||
normal = FreeCAD.Vector(0, 0, 1)
|
|
||||||
path = Part.__sortEdges__(pathwire.Edges)
|
path = Part.__sortEdges__(pathwire.Edges)
|
||||||
ends = []
|
ends = []
|
||||||
cdist = 0
|
cdist = 0
|
||||||
@@ -241,7 +203,7 @@ def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align):
|
|||||||
|
|
||||||
return placements
|
return placements
|
||||||
|
|
||||||
class _Fence(ArchComponent.Component):
|
class Fence(ArchComponent.Component):
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
ArchComponent.Component.__init__(self, obj)
|
ArchComponent.Component.__init__(self, obj)
|
||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
@@ -347,7 +309,6 @@ class _Fence(ArchComponent.Component):
|
|||||||
QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
|
QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence"))
|
||||||
obj.setEditorMode("Length", 1)
|
obj.setEditorMode("Length", 1)
|
||||||
|
|
||||||
|
|
||||||
self.Type = "PVPlatFence"
|
self.Type = "PVPlatFence"
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
@@ -361,75 +322,30 @@ class _Fence(ArchComponent.Component):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def execute(self, obj):
|
def execute(self, obj):
|
||||||
|
if not obj.Base or not obj.Post:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 1. Preparar trazado base
|
||||||
pathwire = self.calculatePathWire(obj)
|
pathwire = self.calculatePathWire(obj)
|
||||||
if pathwire is None:
|
pathwire = utils.getProjected(pathwire, FreeCAD.Vector(0, 0, 1))
|
||||||
# FreeCAD.Console.PrintLog("ArchFence.execute: path " + obj.Base.Name + " has no edges\n")
|
pathwire = utils.simplifyWire(pathwire)
|
||||||
return
|
if not pathwire or not pathwire.Edges:
|
||||||
|
|
||||||
if not obj.Post:
|
|
||||||
FreeCAD.Console.PrintLog("ArchFence.execute: Post not set\n")
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 2. Proyectar sobre terreno (con caché)
|
||||||
self.Posts = []
|
self.Posts = []
|
||||||
self.Foundations = []
|
self.Foundations = []
|
||||||
site = PVPlantSite.get()
|
|
||||||
if True: # prueba
|
|
||||||
import MeshPart as mp
|
|
||||||
land = FreeCAD.ActiveDocument.Terrain.Mesh
|
|
||||||
segments = mp.projectShapeOnMesh(pathwire, land, FreeCAD.Vector(0, 0, 1))
|
|
||||||
points=[]
|
|
||||||
for segment in segments:
|
|
||||||
points.extend(segment)
|
|
||||||
pathwire = Part.makePolygon(points)
|
|
||||||
else:
|
|
||||||
if PVPlantSite.get().Terrain.TypeId == 'Mesh::Feature':
|
|
||||||
import MeshPart as mp
|
|
||||||
land = PVPlantSite.get().Terrain.Mesh
|
|
||||||
pathwire = mp.projectShapeOnMesh(pathwire, land, FreeCAD.Vector(0, 0, 1))
|
|
||||||
|
|
||||||
else:
|
site = PVPlantSite.get()
|
||||||
land = site.Terrain.Shape
|
segments = mp.projectShapeOnMesh(pathwire, site.Terrain.Mesh, FreeCAD.Vector(0, 0, 1))
|
||||||
pathwire = land.makeParallelProjection(pathwire, FreeCAD.Vector(0, 0, 1))
|
points=[]
|
||||||
|
for segment in segments:
|
||||||
|
points.extend(segment)
|
||||||
|
pathwire = Part.makePolygon(points)
|
||||||
|
|
||||||
if pathwire is None:
|
if pathwire is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
''' no sirve:
|
|
||||||
if len(pathwire.Wires) > 1:
|
|
||||||
import draftgeoutils
|
|
||||||
pathwire = draftgeoutils.wires.superWire(pathwire.Edges, True)
|
|
||||||
Part.show(pathwire)
|
|
||||||
'''
|
|
||||||
|
|
||||||
''' unir todas en una '''
|
|
||||||
'''
|
|
||||||
if len(pathwire.Wires) > 1:
|
|
||||||
import Utils.PVPlantUtils as utils
|
|
||||||
wires = pathwire.Wires
|
|
||||||
new_wire = []
|
|
||||||
to_compare = utils.getPoints(wires.pop(0))
|
|
||||||
new_wire.extend(to_compare)
|
|
||||||
while len(wires)>0:
|
|
||||||
wire = wires[0]
|
|
||||||
points = utils.getPoints(wire)
|
|
||||||
to_remove = None
|
|
||||||
if points[0] in to_compare:
|
|
||||||
to_remove = points[0]
|
|
||||||
if points[-1] in to_compare:
|
|
||||||
to_remove = points[-1]
|
|
||||||
if to_remove:
|
|
||||||
to_compare = points.copy()
|
|
||||||
points.remove(to_remove)
|
|
||||||
new_wire.extend(points)
|
|
||||||
wires.pop()
|
|
||||||
continue
|
|
||||||
wires.append(wires.pop())
|
|
||||||
pathwire = Part.makePolygon(new_wire)
|
|
||||||
#Part.show(pathwire)
|
|
||||||
#return
|
|
||||||
'''
|
|
||||||
|
|
||||||
sectionLength = obj.Gap.Value
|
sectionLength = obj.Gap.Value
|
||||||
postLength = 0 #obj.Post.Diameter.Value #considerarlo 0 porque no influye
|
postLength = 0 #obj.Post.Diameter.Value #considerarlo 0 porque no influye
|
||||||
postPlacements = []
|
postPlacements = []
|
||||||
@@ -457,18 +373,19 @@ class _Fence(ArchComponent.Component):
|
|||||||
|
|
||||||
postPlacements.extend(placements)
|
postPlacements.extend(placements)
|
||||||
|
|
||||||
|
# 5. Generar geometría
|
||||||
postShapes, postFoundation = self.calculatePosts(obj, postPlacements)
|
postShapes, postFoundation = self.calculatePosts(obj, postPlacements)
|
||||||
sections, num = self.calculateSections(obj, postPlacements)
|
mesh = self.calculate_sections(obj, postPlacements)
|
||||||
|
|
||||||
postShapes = Part.makeCompound(postShapes)
|
postShapes = Part.makeCompound(postShapes)
|
||||||
postFoundation = Part.makeCompound(postFoundation)
|
postFoundation = Part.makeCompound(postFoundation)
|
||||||
sections = Part.makeCompound(sections)
|
|
||||||
compound = Part.makeCompound([postShapes, postFoundation, sections])
|
|
||||||
obj.Shape = compound
|
|
||||||
|
|
||||||
# Give information
|
# 6. Crear forma final
|
||||||
|
obj.Shape = Part.makeCompound([postShapes, postFoundation, mesh])
|
||||||
|
|
||||||
|
# 7. Actualizar propiedades
|
||||||
obj.NumberOfSections = count
|
obj.NumberOfSections = count
|
||||||
obj.NumberOfPosts = obj.NumberOfSections + 1
|
obj.NumberOfPosts = count + 1
|
||||||
obj.Length = pathLength
|
obj.Length = pathLength
|
||||||
obj.Concrete = count * postFoundation.SubShapes[0].Volume
|
obj.Concrete = count * postFoundation.SubShapes[0].Volume
|
||||||
|
|
||||||
@@ -497,7 +414,7 @@ class _Fence(ArchComponent.Component):
|
|||||||
|
|
||||||
def calculatePostPlacements(self, obj, pathwire, rotation):
|
def calculatePostPlacements(self, obj, pathwire, rotation):
|
||||||
postWidth = obj.Post.Diameter.Value
|
postWidth = obj.Post.Diameter.Value
|
||||||
transformationVector = FreeCAD.Vector(0, postWidth / 2, 0)
|
transformationVector = FreeCAD.Vector(0, - postWidth / 2, 0)
|
||||||
placements = calculatePlacementsOnPath(rotation, pathwire, int(obj.NumberOfSections) + 1, transformationVector, True)
|
placements = calculatePlacementsOnPath(rotation, pathwire, int(obj.NumberOfSections) + 1, transformationVector, True)
|
||||||
# The placement of the last object is always the second entry in the list.
|
# The placement of the last object is always the second entry in the list.
|
||||||
# So we move it to the end:
|
# So we move it to the end:
|
||||||
@@ -509,47 +426,36 @@ class _Fence(ArchComponent.Component):
|
|||||||
posts = []
|
posts = []
|
||||||
foundations = []
|
foundations = []
|
||||||
for placement in postPlacements:
|
for placement in postPlacements:
|
||||||
postCopy = obj.Post.Shape.copy()
|
new_post = obj.Post.Shape.copy()
|
||||||
postCopy = Part.Solid(postCopy)
|
new_post = Part.Solid(new_post)
|
||||||
postCopy.Placement = placement
|
new_post.Placement = placement
|
||||||
postCopy.Placement.Base.z += 100
|
new_post.Placement.Base.z += 100
|
||||||
posts.append(postCopy)
|
posts.append(new_post)
|
||||||
|
|
||||||
foundation = Part.makeCylinder(150, 700)
|
foundation = Part.makeCylinder(150, 700)
|
||||||
foundation.Placement = placement
|
foundation.Placement = placement
|
||||||
foundation.Placement.Base.z -= obj.Depth.Value
|
foundation.Placement.Base.z -= obj.Depth.Value
|
||||||
foundation = foundation.cut(postCopy)
|
#foundation = foundation.cut(new_post)
|
||||||
foundations.append(foundation)
|
foundations.append(foundation)
|
||||||
|
|
||||||
return posts, foundations
|
return posts, foundations
|
||||||
|
|
||||||
def calculateSections(self, obj, postPlacements):
|
def calculate_sections(self, obj, postPlacements):
|
||||||
shapes = []
|
offsetz = FreeCAD.Vector(0, 0, obj.MeshOffsetZ.Value)
|
||||||
faceNumbers = []
|
meshHeight = FreeCAD.Vector(0, 0, obj.MeshHeight.Value)
|
||||||
|
|
||||||
offsetz = obj.MeshOffsetZ.Value
|
points_down = []
|
||||||
meshHeight = obj.MeshHeight.Value
|
points_up = []
|
||||||
for i in range(len(postPlacements) - 1):
|
for i in range(len(postPlacements) - 1):
|
||||||
startPlacement = postPlacements[i]
|
p1 = postPlacements[i].Base + offsetz
|
||||||
endPlacement = postPlacements[i + 1]
|
p2 = postPlacements[i + 1].Base + offsetz
|
||||||
|
p3 = p1 + meshHeight
|
||||||
|
p4 = p2 + meshHeight
|
||||||
|
points_down.extend([p1, p2])
|
||||||
|
points_up.extend([p3, p4])
|
||||||
|
|
||||||
p1 = startPlacement.Base + FreeCAD.Vector(0, 0, offsetz)
|
shape = Part.makeRuledSurface(Part.makePolygon(points_down), Part.makePolygon(points_up))
|
||||||
p2 = endPlacement.Base + FreeCAD.Vector(0, 0, offsetz)
|
return shape
|
||||||
p3 = p2 + FreeCAD.Vector(0, 0, meshHeight)
|
|
||||||
p4 = p1 + FreeCAD.Vector(0, 0, meshHeight)
|
|
||||||
pointlist = [p1, p2, p3, p4, p1]
|
|
||||||
|
|
||||||
try:
|
|
||||||
pol = Part.makePolygon(pointlist)
|
|
||||||
face = Part.Face(pol)
|
|
||||||
shapes.append(face)
|
|
||||||
faceNumbers.append(1)
|
|
||||||
except:
|
|
||||||
print("No es posible crear la cara: ---------------------------------------------------")
|
|
||||||
print(" +++++ Start: ", startPlacement.Base, " - end: ", endPlacement.Base)
|
|
||||||
print(" +++++ algo: ", pointlist, "\n")
|
|
||||||
print("---------------------------------------------------\n")
|
|
||||||
return (shapes, faceNumbers)
|
|
||||||
|
|
||||||
def calculatePathWire(self, obj):
|
def calculatePathWire(self, obj):
|
||||||
if obj.Base:
|
if obj.Base:
|
||||||
@@ -562,7 +468,7 @@ class _Fence(ArchComponent.Component):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class _ViewProviderFence(ArchComponent.ViewProviderComponent):
|
class ViewProviderFence(ArchComponent.ViewProviderComponent):
|
||||||
"A View Provider for the Fence object"
|
"A View Provider for the Fence object"
|
||||||
|
|
||||||
def __init__(self, vobj):
|
def __init__(self, vobj):
|
||||||
@@ -642,7 +548,7 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent):
|
|||||||
children.append(self.Object.Gate)
|
children.append(self.Object.Gate)
|
||||||
return children
|
return children
|
||||||
|
|
||||||
class _FenceTaskPanel:
|
class FenceTaskPanel:
|
||||||
'''The TaskPanel to setup the fence'''
|
'''The TaskPanel to setup the fence'''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -775,15 +681,8 @@ class _FenceTaskPanel:
|
|||||||
self.form = [self.formFence, self.formPost, self.formFoundation]
|
self.form = [self.formFence, self.formPost, self.formFoundation]
|
||||||
|
|
||||||
# valores iniciales y creación del la valla:
|
# valores iniciales y creación del la valla:
|
||||||
import Draft
|
self.post = PVPlantFencePost.makeFencePost()
|
||||||
self.post = PVPlantFencePost.makeFencePost() # Arch.makePipe()
|
|
||||||
self.post.Label = "Post"
|
self.post.Label = "Post"
|
||||||
Draft.autogroup(self.post)
|
|
||||||
|
|
||||||
'''
|
|
||||||
self.section = self.makeGrid()
|
|
||||||
self.path = self.section.Base
|
|
||||||
'''
|
|
||||||
|
|
||||||
FreeCAD.ActiveDocument.recompute()
|
FreeCAD.ActiveDocument.recompute()
|
||||||
self.fence = makePVPlantFence(self.section, self.post, self.path)
|
self.fence = makePVPlantFence(self.section, self.post, self.path)
|
||||||
@@ -845,7 +744,7 @@ class _FenceTaskPanel:
|
|||||||
|
|
||||||
|
|
||||||
# Commands ---------------------------------------------------------------------------------
|
# Commands ---------------------------------------------------------------------------------
|
||||||
class _CommandPVPlantFence:
|
class CommandPVPlantFence:
|
||||||
"the PVPlant Fence command definition"
|
"the PVPlant Fence command definition"
|
||||||
|
|
||||||
def GetResources(self):
|
def GetResources(self):
|
||||||
@@ -859,7 +758,7 @@ class _CommandPVPlantFence:
|
|||||||
return not FreeCAD.ActiveDocument is None
|
return not FreeCAD.ActiveDocument is None
|
||||||
|
|
||||||
def Activated(self):
|
def Activated(self):
|
||||||
self.TaskPanel = _FenceTaskPanel()
|
self.TaskPanel = FenceTaskPanel()
|
||||||
FreeCADGui.Control.showDialog(self.TaskPanel)
|
FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||||
|
|
||||||
|
|
||||||
@@ -877,19 +776,13 @@ if FreeCAD.GuiUp:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def IsActive(self):
|
def IsActive(self):
|
||||||
|
site = FreeCAD.ActiveDocument.getObject("Site")
|
||||||
return (not (FreeCAD.ActiveDocument is None) and
|
return (not (FreeCAD.ActiveDocument is None) and
|
||||||
not (FreeCAD.ActiveDocument.getObject("Site") is None) and
|
not (site is None) and
|
||||||
not (FreeCAD.ActiveDocument.getObject("Terrain") is None))
|
not (site.Terrain is None))
|
||||||
|
|
||||||
import PVPlantFenceGate
|
import Civil.Fence.PVPlantFenceGate as PVPlantFenceGate
|
||||||
FreeCADGui.addCommand('PVPlantFence', _CommandPVPlantFence())
|
FreeCADGui.addCommand('PVPlantFence', CommandPVPlantFence())
|
||||||
FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate())
|
FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate.CommandPVPlantGate())
|
||||||
FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost())
|
FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost.CommandFencePost())
|
||||||
FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
|
#FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
|
||||||
|
|
||||||
def movep(obj):
|
|
||||||
pl = obj.Shape.BoundBox.Center
|
|
||||||
points = []
|
|
||||||
for ind in range(len(obj.Shape.Vertexes)):
|
|
||||||
points.append(obj.Shape.Vertexes[ind].Point - pl)
|
|
||||||
Draft.makeWire(points)
|
|
||||||
@@ -202,7 +202,7 @@ class ViewProviderGate:
|
|||||||
children.append(self.Object.Base)
|
children.append(self.Object.Base)
|
||||||
return children
|
return children
|
||||||
|
|
||||||
class _CommandPVPlantGate:
|
class CommandPVPlantGate:
|
||||||
"the PVPlant Fence command definition"
|
"the PVPlant Fence command definition"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -256,7 +256,9 @@ class _CommandPVPlantGate:
|
|||||||
gate = makePVPlantFence()
|
gate = makePVPlantFence()
|
||||||
try:
|
try:
|
||||||
import MeshPart as mp
|
import MeshPart as mp
|
||||||
point1 = mp.projectPointsOnMesh([point1,], FreeCAD.ActiveDocument.Terrain.Mesh, FreeCAD.Vector(0, 0, 1))[0]
|
import PVPlantSite
|
||||||
|
site = PVPlantSite.get()
|
||||||
|
point1 = mp.projectPointsOnMesh([point1,], site.Terrain.Mesh, FreeCAD.Vector(0, 0, 1))[0]
|
||||||
|
|
||||||
except:
|
except:
|
||||||
FreeCAD.Console.PrintError("No se puede encontrar punto 3D.." + "\n")
|
FreeCAD.Console.PrintError("No se puede encontrar punto 3D.." + "\n")
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import ArchComponent
|
|
||||||
import FreeCAD
|
import FreeCAD
|
||||||
|
import Part
|
||||||
|
import ArchComponent
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
import FreeCADGui
|
import FreeCADGui
|
||||||
@@ -9,8 +10,6 @@ else:
|
|||||||
# \cond
|
# \cond
|
||||||
def translate(ctxt, txt):
|
def translate(ctxt, txt):
|
||||||
return txt
|
return txt
|
||||||
|
|
||||||
|
|
||||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||||
return txt
|
return txt
|
||||||
# \endcond
|
# \endcond
|
||||||
@@ -21,20 +20,14 @@ except AttributeError:
|
|||||||
def _fromUtf8(s):
|
def _fromUtf8(s):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
def makeFencePost(diameter=48, length=3000, placement=None, name="FencePost"):
|
||||||
def makeFencePost(diameter=48, length=3000, placement=None, name="Post"):
|
|
||||||
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
|
|
||||||
|
|
||||||
if not FreeCAD.ActiveDocument:
|
|
||||||
FreeCAD.Console.PrintError("No active document. Aborting\n")
|
|
||||||
return
|
|
||||||
|
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||||
obj.Label = name
|
obj.Label = name
|
||||||
_FencePost(obj)
|
FencePost(obj)
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
_ViewProviderFencePost(obj.ViewObject)
|
ViewProviderFencePost(obj.ViewObject)
|
||||||
|
|
||||||
obj.Length = length
|
obj.Length = length
|
||||||
obj.Diameter = diameter
|
obj.Diameter = diameter
|
||||||
@@ -45,18 +38,13 @@ def makeFencePost(diameter=48, length=3000, placement=None, name="Post"):
|
|||||||
|
|
||||||
|
|
||||||
def makeFenceReinforcePost(diameter=48, length=3000, placement=None, name="Post"):
|
def makeFenceReinforcePost(diameter=48, length=3000, placement=None, name="Post"):
|
||||||
"makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object"
|
|
||||||
|
|
||||||
if not FreeCAD.ActiveDocument:
|
|
||||||
FreeCAD.Console.PrintError("No active document. Aborting\n")
|
|
||||||
return
|
|
||||||
|
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||||
obj.Label = name
|
obj.Label = name
|
||||||
_FenceReinforcePostPost(obj)
|
FenceReinforcePost(obj)
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
_ViewProviderFencePost(obj.ViewObject)
|
ViewProviderFencePost(obj.ViewObject)
|
||||||
|
|
||||||
obj.Length = length
|
obj.Length = length
|
||||||
obj.Diameter = diameter
|
obj.Diameter = diameter
|
||||||
@@ -66,7 +54,7 @@ def makeFenceReinforcePost(diameter=48, length=3000, placement=None, name="Post"
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class _FencePost(ArchComponent.Component):
|
class FencePost(ArchComponent.Component):
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
ArchComponent.Component.__init__(self, obj)
|
ArchComponent.Component.__init__(self, obj)
|
||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
@@ -80,10 +68,10 @@ class _FencePost(ArchComponent.Component):
|
|||||||
obj.addProperty("App::PropertyLength", "Diameter", "Pipe",
|
obj.addProperty("App::PropertyLength", "Diameter", "Pipe",
|
||||||
QT_TRANSLATE_NOOP("App::Property", "The diameter of this pipe, if not based on a profile")
|
QT_TRANSLATE_NOOP("App::Property", "The diameter of this pipe, if not based on a profile")
|
||||||
).Diameter = 48
|
).Diameter = 48
|
||||||
if not "Thickness" in pl:
|
'''if not "Thickness" in pl:
|
||||||
obj.addProperty("App::PropertyLength", "Thickness", "Pipe",
|
obj.addProperty("App::PropertyLength", "Thickness", "Pipe",
|
||||||
QT_TRANSLATE_NOOP("App::Property", "The Thickness of this pipe, if not based on a profile")
|
QT_TRANSLATE_NOOP("App::Property", "The Thickness of this pipe, if not based on a profile")
|
||||||
).Thickness = 4
|
).Thickness = 4'''
|
||||||
if not "Length" in pl:
|
if not "Length" in pl:
|
||||||
obj.addProperty("App::PropertyLength", "Length", "Pipe",
|
obj.addProperty("App::PropertyLength", "Length", "Pipe",
|
||||||
QT_TRANSLATE_NOOP("App::Property", "The length of this pipe, if not based on an edge")
|
QT_TRANSLATE_NOOP("App::Property", "The length of this pipe, if not based on an edge")
|
||||||
@@ -94,86 +82,49 @@ class _FencePost(ArchComponent.Component):
|
|||||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
|
|
||||||
|
def get_axis(self, obj, lip_heigth):
|
||||||
|
wire = Part.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, obj.Length.Value - lip_heigth))
|
||||||
|
#wire = Part.makePolygon(FreeCAD.Vector(0, 0, obj.Length.Value - lip_heigth),)
|
||||||
|
return Part.Wire(wire)
|
||||||
|
|
||||||
def execute(self, obj):
|
def execute(self, obj):
|
||||||
import Part
|
|
||||||
pl = obj.Placement
|
pl = obj.Placement
|
||||||
|
|
||||||
if obj.CloneOf:
|
lip_heigth = 20
|
||||||
obj.Shape = obj.CloneOf.Shape
|
radius = obj.Diameter.Value / 2
|
||||||
else:
|
|
||||||
w = self.getProfile(obj)
|
|
||||||
try:
|
|
||||||
# sh = w.makePipeShell([p], True, False, 2)
|
|
||||||
sh = w.revolve(FreeCAD.Vector(0.0, 0.0, 0.0), FreeCAD.Vector(0.0, 0.0, 1.0), 360)
|
|
||||||
sh = Part.Solid(sh)
|
|
||||||
except:
|
|
||||||
FreeCAD.Console.PrintError("Unable to build the pipe \n")
|
|
||||||
else:
|
|
||||||
obj.Shape = sh
|
|
||||||
obj.Placement = pl
|
|
||||||
|
|
||||||
|
# para que sea una función que sirva para los postes rectos y con curva:
|
||||||
|
axis = self.get_axis(obj, lip_heigth)
|
||||||
|
profile = Part.Wire([Part.Circle(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), radius).toShape()])
|
||||||
|
post = axis.makePipeShell([profile, ],True,True,2)
|
||||||
|
|
||||||
|
lip = Part.makeCylinder(radius + 2, lip_heigth)
|
||||||
|
lip = lip.makeFillet(5, [lip.Edges[0]])
|
||||||
|
|
||||||
|
# Obtener caras
|
||||||
|
face_post = post.Faces[2] # Cara superior del cilindro largo
|
||||||
|
face_lip = lip.Faces[2] # Cara inferior del cilindro corto
|
||||||
|
|
||||||
|
# Calcular centro y normal de las caras
|
||||||
|
face_post_center = face_post.CenterOfMass
|
||||||
|
face_post_normal = face_post.normalAt(0, 0)
|
||||||
|
face_lip_center = face_lip.CenterOfMass
|
||||||
|
face_lip_normal = face_lip.normalAt(0, 0)
|
||||||
|
|
||||||
|
# Calcular rotación para alinear normales (ajustar dirección)
|
||||||
|
rotacion = FreeCAD.Rotation(face_lip_normal, -face_post_normal) # Invertir normal del cilindro corto
|
||||||
|
lip.Placement.Rotation = rotacion.multiply(lip.Placement.Rotation)
|
||||||
|
|
||||||
|
# Calcular traslación: mover centro del cilindro corto al centro del cilindro largo
|
||||||
|
traslacion = face_post_center - rotacion.multVec(face_lip_center)
|
||||||
|
lip.Placement.Base = traslacion #face_post_center
|
||||||
|
|
||||||
|
obj.Shape = post.fuse(lip)
|
||||||
|
obj.Placement = pl
|
||||||
return
|
return
|
||||||
|
|
||||||
# ------------------------- Prueba para apoyos de refuerzo:
|
|
||||||
import math
|
|
||||||
L = math.pi / 2 * (obj.Diameter.Value - 2 * obj.Thickness.Value)
|
|
||||||
|
|
||||||
v1 = FreeCAD.Vector(L / 2, 0, obj.Thickness.Value)
|
class FenceReinforcePost(ArchComponent.Component):
|
||||||
vc1 = FreeCAD.Vector(L / 2 + obj.Thickness.Value, 0, 0)
|
|
||||||
v2 = FreeCAD.Vector(L / 2, 0, -obj.Thickness.Value)
|
|
||||||
|
|
||||||
v11 = FreeCAD.Vector(-L / 2, 0, obj.Thickness.Value)
|
|
||||||
vc11 = FreeCAD.Vector(-(L / 2 + obj.Thickness.Value), 0, 0)
|
|
||||||
v21 = FreeCAD.Vector(-L / 2, 0, -obj.Thickness.Value)
|
|
||||||
|
|
||||||
arc1 = Part.Arc(v1, vc1, v2).toShape()
|
|
||||||
arc11 = Part.Arc(v11, vc11, v21).toShape()
|
|
||||||
line1 = Part.LineSegment(v11, v1).toShape()
|
|
||||||
line2 = Part.LineSegment(v21, v2).toShape()
|
|
||||||
w = Part.Wire([arc1, line2, arc11, line1])
|
|
||||||
face = Part.Face(w)
|
|
||||||
pro = face.extrude(FreeCAD.Vector(0, 40, 0))
|
|
||||||
|
|
||||||
#Part.Circle(Center, Normal, Radius)
|
|
||||||
cir1 = Part.Face(Part.Wire(Part.Circle(FreeCAD.Vector(0, -200, 0), FreeCAD.Vector(0, 1, 0), obj.Diameter.Value / 2).toShape()))
|
|
||||||
ext = cir1.extrude(FreeCAD.Vector(0, 170, 0))
|
|
||||||
cir2 = Part.Circle(FreeCAD.Vector(0, -30, 0), FreeCAD.Vector(0, 1, 0), obj.Diameter.Value/2).toShape()
|
|
||||||
loft = Part.makeLoft([cir2, w], True)
|
|
||||||
ext = ext.fuse([loft, pro])
|
|
||||||
Part.show(ext)
|
|
||||||
|
|
||||||
|
|
||||||
def getProfile(self, obj):
|
|
||||||
import Part
|
|
||||||
sin45 = 0.707106781
|
|
||||||
radio = obj.Diameter.Value / 2
|
|
||||||
taph = 20
|
|
||||||
tapw = radio + 2
|
|
||||||
chamfer = 5
|
|
||||||
chamfer2 = chamfer * sin45
|
|
||||||
|
|
||||||
edge1 = Part.makeLine(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(radio, 0, 0))
|
|
||||||
edge2 = Part.makeLine(FreeCAD.Vector(radio, 0, 0), FreeCAD.Vector(radio, 0, obj.Length.Value - taph))
|
|
||||||
edge3 = Part.makeLine(FreeCAD.Vector(radio, 0, obj.Length.Value - taph),
|
|
||||||
FreeCAD.Vector(tapw, 0, obj.Length.Value - taph))
|
|
||||||
edge4 = Part.makeLine(FreeCAD.Vector(tapw, 0, obj.Length.Value - taph),
|
|
||||||
FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer))
|
|
||||||
if True:
|
|
||||||
edge5 = Part.makeLine(FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer),
|
|
||||||
FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value))
|
|
||||||
else:
|
|
||||||
edge5 = Part.Arc(FreeCAD.Vector(tapw, 0, obj.Length.Value - chamfer),
|
|
||||||
FreeCAD.Vector(tapw - chamfer2, 0, obj.Length.Value - chamfer2),
|
|
||||||
FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value)
|
|
||||||
).toShape()
|
|
||||||
edge6 = Part.makeLine(FreeCAD.Vector(tapw - chamfer, 0, obj.Length.Value),
|
|
||||||
FreeCAD.Vector(0, 0, obj.Length.Value))
|
|
||||||
w = Part.Wire([edge1, edge2, edge3, edge4, edge5, edge6])
|
|
||||||
|
|
||||||
return w
|
|
||||||
|
|
||||||
|
|
||||||
class _FenceReinforcePost(ArchComponent.Component):
|
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
ArchComponent.Component.__init__(self, obj)
|
ArchComponent.Component.__init__(self, obj)
|
||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
@@ -199,10 +150,18 @@ class _FenceReinforcePost(ArchComponent.Component):
|
|||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
|
|
||||||
def execute(self, obj):
|
def execute(self, obj):
|
||||||
|
|
||||||
pl = obj.Placement
|
pl = obj.Placement
|
||||||
w = self.getWire(obj)
|
|
||||||
|
|
||||||
|
lip_heigth = 20
|
||||||
|
post = Part.makeCylinder(obj.Diameter.Value / 2, obj.Length.Value - lip_heigth)
|
||||||
|
lip = Part.makeCylinder(obj.Diameter.Value / 2 + 2, lip_heigth)
|
||||||
|
lip = lip.makeFillet(5, [lip.Edges[0]])
|
||||||
|
lip.translate(FreeCAD.Vector(0, 0, obj.Length.Value - lip_heigth))
|
||||||
|
obj.Shape = post.fuse(lip)
|
||||||
|
obj.Placement = pl
|
||||||
|
return
|
||||||
|
|
||||||
|
w = self.getWire(obj)
|
||||||
try:
|
try:
|
||||||
# sh = w.makePipeShell([p], True, False, 2)
|
# sh = w.makePipeShell([p], True, False, 2)
|
||||||
sh = w.revolve(FreeCAD.Vector(0.0, 0.0, 0.0), FreeCAD.Vector(0.0, 0.0, 1.0), 360)
|
sh = w.revolve(FreeCAD.Vector(0.0, 0.0, 0.0), FreeCAD.Vector(0.0, 0.0, 1.0), 360)
|
||||||
@@ -244,7 +203,7 @@ class _FenceReinforcePost(ArchComponent.Component):
|
|||||||
return w
|
return w
|
||||||
|
|
||||||
|
|
||||||
class _ViewProviderFencePost(ArchComponent.ViewProviderComponent):
|
class ViewProviderFencePost(ArchComponent.ViewProviderComponent):
|
||||||
"A View Provider for the Pipe object"
|
"A View Provider for the Pipe object"
|
||||||
|
|
||||||
def __init__(self, vobj):
|
def __init__(self, vobj):
|
||||||
@@ -254,7 +213,7 @@ class _ViewProviderFencePost(ArchComponent.ViewProviderComponent):
|
|||||||
return ":/icons/Arch_Pipe_Tree.svg"
|
return ":/icons/Arch_Pipe_Tree.svg"
|
||||||
|
|
||||||
|
|
||||||
class _CommandFencePost:
|
class CommandFencePost:
|
||||||
"the Arch Pipe command definition"
|
"the Arch Pipe command definition"
|
||||||
|
|
||||||
def GetResources(self):
|
def GetResources(self):
|
||||||
@@ -269,17 +228,5 @@ class _CommandFencePost:
|
|||||||
return not FreeCAD.ActiveDocument is None
|
return not FreeCAD.ActiveDocument is None
|
||||||
|
|
||||||
def Activated(self):
|
def Activated(self):
|
||||||
if True:
|
makeFencePost()
|
||||||
makeFencePost()
|
|
||||||
else:
|
|
||||||
FreeCAD.ActiveDocument.openTransaction(translate("Arch", "Create Pipe"))
|
|
||||||
FreeCADGui.addModule("Arch")
|
|
||||||
FreeCADGui.doCommand("obj = Arch.makePipe()")
|
|
||||||
FreeCADGui.addModule("Draft")
|
|
||||||
FreeCADGui.doCommand("Draft.autogroup(obj)")
|
|
||||||
FreeCAD.ActiveDocument.commitTransaction()
|
|
||||||
FreeCAD.ActiveDocument.recompute()
|
FreeCAD.ActiveDocument.recompute()
|
||||||
|
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
|
||||||
FreeCADGui.addCommand('FencePost', _CommandFencePost())
|
|
||||||
200
Electrical/PowerConverter/PowerConverter.py
Normal file
200
Electrical/PowerConverter/PowerConverter.py
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# /**********************************************************************
|
||||||
|
# * *
|
||||||
|
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com> *
|
||||||
|
# * *
|
||||||
|
# * This program is free software; you can redistribute it and/or modify*
|
||||||
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||||
|
# * as published by the Free Software Foundation; either version 2 of *
|
||||||
|
# * the License, or (at your option) any later version. *
|
||||||
|
# * for detail see the LICENCE text file. *
|
||||||
|
# * *
|
||||||
|
# * This program is distributed in the hope that it will be useful, *
|
||||||
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
# * GNU Library General Public License for more details. *
|
||||||
|
# * *
|
||||||
|
# * You should have received a copy of the GNU Library General Public *
|
||||||
|
# * License along with this program; if not, write to the Free Software *
|
||||||
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307*
|
||||||
|
# * USA *
|
||||||
|
# * *
|
||||||
|
# ***********************************************************************
|
||||||
|
|
||||||
|
import FreeCAD
|
||||||
|
import ArchComponent
|
||||||
|
import Part
|
||||||
|
import os
|
||||||
|
import zipfile
|
||||||
|
import re
|
||||||
|
|
||||||
|
if FreeCAD.GuiUp:
|
||||||
|
import FreeCADGui
|
||||||
|
from DraftTools import translate
|
||||||
|
else:
|
||||||
|
# \cond
|
||||||
|
def translate(ctxt,txt):
|
||||||
|
return txt
|
||||||
|
def QT_TRANSLATE_NOOP(ctxt,txt):
|
||||||
|
return txt
|
||||||
|
# \endcond
|
||||||
|
|
||||||
|
import os
|
||||||
|
from PVPlantResources import DirIcons as DirIcons
|
||||||
|
|
||||||
|
__title__ = "PVPlant Areas"
|
||||||
|
__author__ = "Javier Braña"
|
||||||
|
__url__ = "http://www.sogos-solar.com"
|
||||||
|
|
||||||
|
import PVPlantResources
|
||||||
|
from PVPlantResources import DirIcons as DirIcons
|
||||||
|
Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects")
|
||||||
|
|
||||||
|
vector = ["Y", "YN", "Z", "ZN", "D"]
|
||||||
|
|
||||||
|
def makePCS():
|
||||||
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PowerConversionSystem")
|
||||||
|
PowerConverter(obj)
|
||||||
|
ViewProviderPowerConverter(obj.ViewObject)
|
||||||
|
|
||||||
|
try:
|
||||||
|
folder = FreeCAD.ActiveDocument.PowerConversionSystemGroup
|
||||||
|
except:
|
||||||
|
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'PowerConversionSystemGroup')
|
||||||
|
folder.Label = "PowerConversionSystemGroup"
|
||||||
|
folder.addObject(obj)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
class PowerConverter(ArchComponent.Component):
|
||||||
|
def __init__(self, obj):
|
||||||
|
''' Initialize the Area object '''
|
||||||
|
ArchComponent.Component.__init__(self, obj)
|
||||||
|
self.Type = None
|
||||||
|
self.obj = None
|
||||||
|
self.setProperties(obj)
|
||||||
|
|
||||||
|
def setProperties(self, obj):
|
||||||
|
pl = obj.PropertiesList
|
||||||
|
|
||||||
|
# Transformer properties
|
||||||
|
if not "Technology" in pl:
|
||||||
|
obj.addProperty("App::PropertyEnumeration",
|
||||||
|
"Technology",
|
||||||
|
"Transformer",
|
||||||
|
"Number of phases and type of transformer"
|
||||||
|
).Technology = ["Single Phase Transformer", "Three Phase Transformer"]
|
||||||
|
|
||||||
|
if not "PowerPrimary" in pl:
|
||||||
|
obj.addProperty("App::PropertyPower",
|
||||||
|
"PowerPrimary",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").PowerPrimary = 6000000000
|
||||||
|
|
||||||
|
if not "PowerSecundary1" in pl:
|
||||||
|
obj.addProperty("App::PropertyPower",
|
||||||
|
"PowerSecundary1",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").PowerSecundary1 = 3000000000
|
||||||
|
|
||||||
|
if not "PowerSecundary2" in pl:
|
||||||
|
obj.addProperty("App::PropertyPower",
|
||||||
|
"PowerSecundary2",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").PowerSecundary2 = 3000000000
|
||||||
|
|
||||||
|
if not "VoltagePrimary" in pl:
|
||||||
|
obj.addProperty("App::PropertyElectricPotential",
|
||||||
|
"VoltagePrimary",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").VoltagePrimary = 33000000000
|
||||||
|
|
||||||
|
if not "VoltageSecundary1" in pl:
|
||||||
|
obj.addProperty("App::PropertyElectricPotential",
|
||||||
|
"VoltageSecundary1",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").VoltageSecundary1 = 11000000000
|
||||||
|
|
||||||
|
if not "VoltageSecundary2" in pl:
|
||||||
|
obj.addProperty("App::PropertyElectricPotential",
|
||||||
|
"VoltageSecundary2",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").VoltageSecundary2 = 11000000000
|
||||||
|
|
||||||
|
if not "VectorPrimary" in pl:
|
||||||
|
obj.addProperty("App::PropertyEnumeration",
|
||||||
|
"VectorPrimary",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").VectorPrimary = vector
|
||||||
|
|
||||||
|
if not "VectorSecundary1" in pl:
|
||||||
|
obj.addProperty("App::PropertyEnumeration",
|
||||||
|
"VectorSecundary1",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").VectorSecundary1 = vector
|
||||||
|
|
||||||
|
if not "VectorSecundary2" in pl:
|
||||||
|
obj.addProperty("App::PropertyEnumeration",
|
||||||
|
"VectorSecundary2",
|
||||||
|
"Transformer",
|
||||||
|
"The base file this component is built upon").VectorSecundary2 = vector
|
||||||
|
|
||||||
|
self.Type = "PowerConverter"
|
||||||
|
obj.Proxy = self
|
||||||
|
|
||||||
|
def onDocumentRestored(self, obj):
|
||||||
|
""" Method run when the document is restored """
|
||||||
|
self.setProperties(obj)
|
||||||
|
|
||||||
|
def onBeforeChange(self, obj, prop):
|
||||||
|
''' '''
|
||||||
|
# This method is called before a property is changed.
|
||||||
|
# It can be used to validate the property value or to update other properties.
|
||||||
|
# If the property is not valid, you can raise an exception.
|
||||||
|
# If you want to prevent the change, you can return False.
|
||||||
|
# Otherwise, return True to allow the change.
|
||||||
|
return True
|
||||||
|
|
||||||
|
def onChanged(self, obj, prop):
|
||||||
|
''' '''
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, obj):
|
||||||
|
''' '''
|
||||||
|
# obj.Shape: compound
|
||||||
|
# |- body: compound
|
||||||
|
# |- transformer: solid
|
||||||
|
# |- primary switchgear: compound
|
||||||
|
# |- secundary 1 switchgear: compound
|
||||||
|
# |- secundary 2 switchgear: compound
|
||||||
|
|
||||||
|
pl = obj.Placement
|
||||||
|
obj.Shape = Part.makeBox(6058, 2438, 2591) # Placeholder for the shape
|
||||||
|
obj.Placement = pl
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ViewProviderPowerConverter(ArchComponent.ViewProviderComponent):
|
||||||
|
def __init__(self, vobj):
|
||||||
|
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||||
|
|
||||||
|
def getIcon(self):
|
||||||
|
return str(os.path.join(PVPlantResources.DirIcons, "Inverter.svg"))
|
||||||
|
|
||||||
|
class CommandPowerConverter:
|
||||||
|
|
||||||
|
def GetResources(self):
|
||||||
|
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "Inverter.svg")),
|
||||||
|
'Accel': "E, P",
|
||||||
|
'MenuText': "Power Converter",
|
||||||
|
'ToolTip': "Power Converter",}
|
||||||
|
|
||||||
|
def Activated(self):
|
||||||
|
sinverter = makePCS()
|
||||||
|
|
||||||
|
def IsActive(self):
|
||||||
|
active = not (FreeCAD.ActiveDocument is None)
|
||||||
|
return active
|
||||||
|
|
||||||
|
if FreeCAD.GuiUp:
|
||||||
|
FreeCADGui.addCommand('PowerConverter', CommandPowerConverter())
|
||||||
|
|
||||||
392
Electrical/group.py
Normal file
392
Electrical/group.py
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
def groupTrackersToTransformers(transformer_power, max_distance):
|
||||||
|
import numpy as np
|
||||||
|
from scipy.spatial import KDTree
|
||||||
|
import FreeCAD
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
# 1. Obtener todos los trackers válidos
|
||||||
|
valid_trackers = []
|
||||||
|
valid_points = []
|
||||||
|
valid_power = []
|
||||||
|
|
||||||
|
for tracker in FreeCAD.ActiveDocument.Objects:
|
||||||
|
if hasattr(tracker, 'Proxy') and (tracker.Proxy.Type == "Tracker"):
|
||||||
|
base = tracker.Placement.Base
|
||||||
|
if all(np.isfinite([base.x, base.y, base.z])):
|
||||||
|
valid_trackers.append(tracker)
|
||||||
|
valid_points.append([base.x, base.y])
|
||||||
|
valid_power.append(tracker.Setup.TotalPower)
|
||||||
|
|
||||||
|
if not valid_trackers:
|
||||||
|
FreeCAD.Console.PrintWarning("No se encontraron trackers válidos para agrupar\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. Obtener parámetros de los CTs
|
||||||
|
target_power = transformer_power * 1.2
|
||||||
|
points = np.array(valid_points)
|
||||||
|
power_values = np.array(valid_power)
|
||||||
|
|
||||||
|
# 3. Determinar dirección de barrido (oeste a este por defecto)
|
||||||
|
min_x, min_y = np.min(points, axis=0)
|
||||||
|
max_x, max_y = np.max(points, axis=0)
|
||||||
|
|
||||||
|
# 4. Ordenar trackers de oeste a este (menor X a mayor X)
|
||||||
|
'''
|
||||||
|
Norte a Sur: sorted_indices = np.argsort(-points[:, 1]) (Y descendente)
|
||||||
|
Sur a Norte: sorted_indices = np.argsort(points[:, 1]) (Y ascendente)
|
||||||
|
Este a Oeste: sorted_indices = np.argsort(-points[:, 0]) (X descendente)
|
||||||
|
'''
|
||||||
|
sorted_indices = np.argsort(points[:, 0])
|
||||||
|
sorted_points = points[sorted_indices]
|
||||||
|
sorted_power = power_values[sorted_indices]
|
||||||
|
sorted_trackers = [valid_trackers[i] for i in sorted_indices]
|
||||||
|
|
||||||
|
# 5. Crear KDTree para búsquedas rápidas
|
||||||
|
kdtree = KDTree(sorted_points)
|
||||||
|
|
||||||
|
# 6. Algoritmo de barrido espacial
|
||||||
|
transformer_groups = []
|
||||||
|
used_indices = set()
|
||||||
|
|
||||||
|
# Función para expandir un grupo desde un punto inicial
|
||||||
|
def expand_group(start_idx):
|
||||||
|
group = []
|
||||||
|
total_power = 0
|
||||||
|
queue = deque([start_idx])
|
||||||
|
|
||||||
|
while queue and total_power < target_power:
|
||||||
|
idx = queue.popleft()
|
||||||
|
if idx in used_indices:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Añadir tracker al grupo si no excede la potencia
|
||||||
|
tracker_power = sorted_power[idx]
|
||||||
|
if total_power + tracker_power > target_power * 1.05:
|
||||||
|
continue
|
||||||
|
|
||||||
|
group.append(sorted_trackers[idx])
|
||||||
|
total_power += tracker_power
|
||||||
|
used_indices.add(idx)
|
||||||
|
|
||||||
|
# Buscar vecinos cercanos
|
||||||
|
neighbors = kdtree.query_ball_point(
|
||||||
|
sorted_points[idx],
|
||||||
|
max_distance
|
||||||
|
)
|
||||||
|
|
||||||
|
# Filtrar vecinos no usados y ordenar por proximidad al punto inicial
|
||||||
|
neighbors = [n for n in neighbors if n not in used_indices]
|
||||||
|
neighbors.sort(key=lambda n: abs(n - start_idx))
|
||||||
|
queue.extend(neighbors)
|
||||||
|
|
||||||
|
return group, total_power
|
||||||
|
|
||||||
|
# 7. Barrido principal de oeste a este
|
||||||
|
for i in range(len(sorted_points)):
|
||||||
|
if i in used_indices:
|
||||||
|
continue
|
||||||
|
|
||||||
|
group, total_power = expand_group(i)
|
||||||
|
|
||||||
|
if group:
|
||||||
|
# Calcular centro del grupo
|
||||||
|
group_points = np.array([t.Placement.Base[:2] for t in group])
|
||||||
|
center = np.mean(group_points, axis=0)
|
||||||
|
|
||||||
|
transformer_groups.append({
|
||||||
|
'trackers': group,
|
||||||
|
'total_power': total_power,
|
||||||
|
'center': center
|
||||||
|
})
|
||||||
|
|
||||||
|
# 8. Manejar grupos residuales (si los hay)
|
||||||
|
unused_indices = set(range(len(sorted_points))) - used_indices
|
||||||
|
if unused_indices:
|
||||||
|
# Intentar añadir trackers residuales a grupos existentes
|
||||||
|
for idx in unused_indices:
|
||||||
|
point = sorted_points[idx]
|
||||||
|
tracker_power = sorted_power[idx]
|
||||||
|
|
||||||
|
# Buscar el grupo más cercano que pueda aceptar este tracker
|
||||||
|
best_group = None
|
||||||
|
min_distance = float('inf')
|
||||||
|
|
||||||
|
for group in transformer_groups:
|
||||||
|
if group['total_power'] + tracker_power <= target_power * 1.05:
|
||||||
|
dist = np.linalg.norm(point - group['center'])
|
||||||
|
if dist < min_distance and dist < max_distance * 1.5:
|
||||||
|
min_distance = dist
|
||||||
|
best_group = group
|
||||||
|
|
||||||
|
# Añadir al grupo si se encontró uno adecuado
|
||||||
|
if best_group:
|
||||||
|
best_group['trackers'].append(sorted_trackers[idx])
|
||||||
|
best_group['total_power'] += tracker_power
|
||||||
|
# Actualizar centro del grupo
|
||||||
|
group_points = np.array([t.Placement.Base[:2] for t in best_group['trackers']])
|
||||||
|
best_group['center'] = np.mean(group_points, axis=0)
|
||||||
|
else:
|
||||||
|
# Crear un nuevo grupo con este tracker residual
|
||||||
|
group = [sorted_trackers[idx]]
|
||||||
|
center = point
|
||||||
|
transformer_groups.append({
|
||||||
|
'trackers': group,
|
||||||
|
'total_power': tracker_power,
|
||||||
|
'center': center
|
||||||
|
})
|
||||||
|
|
||||||
|
# 9. Crear los grupos en FreeCAD
|
||||||
|
transformer_group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Transformers")
|
||||||
|
transformer_group.Label = "Centros de Transformación"
|
||||||
|
|
||||||
|
for i, group in enumerate(transformer_groups):
|
||||||
|
# Crear la esfera que representará el CT
|
||||||
|
ct_shape = FreeCAD.ActiveDocument.addObject("Part::Box", f"CT_{i + 1}")
|
||||||
|
ct_shape.Length = 6058
|
||||||
|
ct_shape.Width = 2438
|
||||||
|
ct_shape.Height = 2591
|
||||||
|
ct_shape.Placement.Base = FreeCAD.Vector(group['center'][0], group['center'][1], 0)
|
||||||
|
|
||||||
|
# Añadir propiedades personalizadas
|
||||||
|
ct_shape.addProperty("App::PropertyLinkList", "Trackers", "CT",
|
||||||
|
"Lista de trackers asociados a este CT")
|
||||||
|
ct_shape.addProperty("App::PropertyFloat", "TotalPower", "CT",
|
||||||
|
"Potencia total del grupo (W)")
|
||||||
|
ct_shape.addProperty("App::PropertyFloat", "NominalPower", "CT",
|
||||||
|
"Potencia nominal del transformador (W)")
|
||||||
|
ct_shape.addProperty("App::PropertyFloat", "Utilization", "CT",
|
||||||
|
"Porcentaje de utilización (Total/Nominal)")
|
||||||
|
|
||||||
|
# Establecer valores de las propiedades
|
||||||
|
ct_shape.Trackers = group['trackers']
|
||||||
|
ct_shape.TotalPower = group['total_power'].Value
|
||||||
|
ct_shape.NominalPower = transformer_power
|
||||||
|
ct_shape.Utilization = (group['total_power'].Value / transformer_power) * 100
|
||||||
|
|
||||||
|
# Configurar visualización
|
||||||
|
# Calcular color basado en utilización (verde < 100%, amarillo < 110%, rojo > 110%)
|
||||||
|
utilization = ct_shape.Utilization
|
||||||
|
if utilization <= 100:
|
||||||
|
color = (0.0, 1.0, 0.0) # Verde
|
||||||
|
elif utilization <= 110:
|
||||||
|
color = (1.0, 1.0, 0.0) # Amarillo
|
||||||
|
else:
|
||||||
|
color = (1.0, 0.0, 0.0) # Rojo
|
||||||
|
|
||||||
|
ct_shape.ViewObject.ShapeColor = color
|
||||||
|
ct_shape.ViewObject.Transparency = 40 # 40% de transparencia
|
||||||
|
|
||||||
|
# Añadir etiqueta con información
|
||||||
|
ct_shape.ViewObject.DisplayMode = "Shaded"
|
||||||
|
ct_shape.Label = f"CT {i + 1} ({ct_shape.TotalPower / 1000:.1f}kW/{ct_shape.NominalPower / 1000:.1f}kW)"
|
||||||
|
|
||||||
|
# Añadir al grupo principal
|
||||||
|
transformer_group.addObject(ct_shape)
|
||||||
|
|
||||||
|
FreeCAD.Console.PrintMessage(f"Se crearon {len(transformer_groups)} centros de transformación\n")
|
||||||
|
|
||||||
|
#onSelectGatePoint()
|
||||||
|
|
||||||
|
|
||||||
|
import FreeCAD, FreeCADGui, Part
|
||||||
|
import numpy as np
|
||||||
|
from scipy.stats import linregress
|
||||||
|
from PySide import QtGui
|
||||||
|
|
||||||
|
class InternalPathCreator:
|
||||||
|
def __init__(self, gate_point, strategy=1, path_width=4000):
|
||||||
|
self.gate_point = gate_point
|
||||||
|
self.strategy = strategy
|
||||||
|
self.path_width = path_width
|
||||||
|
self.ct_shapes = []
|
||||||
|
self.ct_positions = []
|
||||||
|
|
||||||
|
def get_transformers(self):
|
||||||
|
transformers_group = FreeCAD.ActiveDocument.getObject("Transformers")
|
||||||
|
if not transformers_group:
|
||||||
|
FreeCAD.Console.PrintError("No se encontró el grupo 'Transformers'\n")
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.ct_shapes = transformers_group.Group
|
||||||
|
if not self.ct_shapes:
|
||||||
|
FreeCAD.Console.PrintWarning("No hay Centros de Transformación en el grupo\n")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Obtener las posiciones de los CTs
|
||||||
|
for sphere in self.ct_shapes:
|
||||||
|
base = sphere.Placement.Base
|
||||||
|
self.ct_positions.append(FreeCAD.Vector(base.x, base.y, 0))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_paths(self):
|
||||||
|
if not self.get_transformers():
|
||||||
|
return []
|
||||||
|
|
||||||
|
if self.strategy == 1:
|
||||||
|
return self.create_direct_paths()
|
||||||
|
elif self.strategy == 2:
|
||||||
|
return self.create_unified_path()
|
||||||
|
else:
|
||||||
|
FreeCAD.Console.PrintError("Estrategia no válida. Use 1 o 2.\n")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def create_direct_paths(self):
|
||||||
|
"""Estrategia 1: Caminos independientes desde cada CT hasta la puerta"""
|
||||||
|
paths = []
|
||||||
|
for ct in self.ct_positions:
|
||||||
|
paths.append([ct, self.gate_point])
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def create_unified_path(self):
|
||||||
|
"""Estrategia 2: Único camino que une todos los CTs y la puerta usando regresión lineal"""
|
||||||
|
if not self.ct_positions:
|
||||||
|
return []
|
||||||
|
|
||||||
|
all_points = self.ct_positions + [self.gate_point]
|
||||||
|
x = [p.x for p in all_points]
|
||||||
|
y = [p.y for p in all_points]
|
||||||
|
|
||||||
|
# Manejar caso de puntos alineados verticalmente
|
||||||
|
if np.std(x) < 1e-6:
|
||||||
|
sorted_points = sorted(all_points, key=lambda p: p.y)
|
||||||
|
paths = []
|
||||||
|
for i in range(len(sorted_points) - 1):
|
||||||
|
paths.append([sorted_points[i], sorted_points[i + 1]])
|
||||||
|
return paths
|
||||||
|
|
||||||
|
# Calcular regresión lineal
|
||||||
|
slope, intercept, _, _, _ = linregress(x, y)
|
||||||
|
|
||||||
|
# Función para proyectar puntos
|
||||||
|
def project_point(point):
|
||||||
|
x0, y0 = point.x, point.y
|
||||||
|
if abs(slope) > 1e6:
|
||||||
|
return FreeCAD.Vector(x0, intercept, 0)
|
||||||
|
x_proj = (x0 + slope * (y0 - intercept)) / (1 + slope ** 2)
|
||||||
|
y_proj = slope * x_proj + intercept
|
||||||
|
return FreeCAD.Vector(x_proj, y_proj, 0)
|
||||||
|
|
||||||
|
# return slope * x + intercept --> desde placement
|
||||||
|
|
||||||
|
projected_points = [project_point(p) for p in all_points]
|
||||||
|
|
||||||
|
# Calcular distancias a lo largo de la línea
|
||||||
|
ref_point = projected_points[0]
|
||||||
|
direction_vector = FreeCAD.Vector(1, slope).normalize()
|
||||||
|
distances = []
|
||||||
|
for p in projected_points:
|
||||||
|
vec_to_point = p - ref_point
|
||||||
|
distance = vec_to_point.dot(direction_vector)
|
||||||
|
distances.append(distance)
|
||||||
|
|
||||||
|
# Ordenar por distancia
|
||||||
|
sorted_indices = np.argsort(distances)
|
||||||
|
sorted_points = [all_points[i] for i in sorted_indices]
|
||||||
|
|
||||||
|
# Crear caminos
|
||||||
|
paths = []
|
||||||
|
for i in range(len(sorted_points) - 1):
|
||||||
|
paths.append([sorted_points[i], sorted_points[i + 1]])
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def create_3d_path(self, path_poly):
|
||||||
|
"""Crea geometría 3D para el camino adaptada a orientación norte-sur"""
|
||||||
|
segments = []
|
||||||
|
for i in range(len(path_poly.Vertexes) - 1):
|
||||||
|
start = path_poly.Vertexes[i].Point
|
||||||
|
end = path_poly.Vertexes[i + 1].Point
|
||||||
|
direction = end - start
|
||||||
|
|
||||||
|
# Determinar orientación predominante
|
||||||
|
if abs(direction.x) > abs(direction.y):
|
||||||
|
normal = FreeCAD.Vector(0, 1, 0) # Norte-sur
|
||||||
|
else:
|
||||||
|
normal = FreeCAD.Vector(1, 0, 0) # Este-oeste
|
||||||
|
|
||||||
|
offset = normal * self.path_width / 2
|
||||||
|
|
||||||
|
# Crear puntos para la sección transversal
|
||||||
|
p1 = start + offset
|
||||||
|
p2 = start - offset
|
||||||
|
p3 = end - offset
|
||||||
|
p4 = end + offset
|
||||||
|
|
||||||
|
# Crear cara
|
||||||
|
wire = Part.makePolygon([p1, p2, p3, p4, p1])
|
||||||
|
face = Part.Face(wire)
|
||||||
|
segments.append(face)
|
||||||
|
|
||||||
|
if segments:
|
||||||
|
'''road_shape = segments[0].fuse(segments[1:])
|
||||||
|
return road_shape.removeSplitter()'''
|
||||||
|
return Part.makeCompound(segments)
|
||||||
|
return Part.Shape()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
paths = self.create_paths()
|
||||||
|
if not paths:
|
||||||
|
return
|
||||||
|
|
||||||
|
path_group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "InternalPaths")
|
||||||
|
path_group.Label = f"Caminos Internos (Estrategia {self.strategy})"
|
||||||
|
|
||||||
|
for i, path in enumerate(paths):
|
||||||
|
poly = Part.makePolygon(path)
|
||||||
|
|
||||||
|
# Objeto para la línea central
|
||||||
|
path_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", f"Path_{i + 1}")
|
||||||
|
path_obj.Shape = poly
|
||||||
|
path_obj.ViewObject.LineWidth = 3.0
|
||||||
|
path_obj.ViewObject.LineColor = (0.0, 0.0, 1.0)
|
||||||
|
|
||||||
|
# Objeto para la superficie 3D
|
||||||
|
road_shape = self.create_3d_path(poly)
|
||||||
|
road_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", f"Road_{i + 1}")
|
||||||
|
road_obj.Shape = road_shape
|
||||||
|
road_obj.ViewObject.ShapeColor = (0.7, 0.7, 0.7)
|
||||||
|
|
||||||
|
path_group.addObject(path_obj)
|
||||||
|
path_group.addObject(road_obj)
|
||||||
|
|
||||||
|
FreeCAD.Console.PrintMessage(f"Se crearon {len(paths)} segmentos de caminos internos\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Función para mostrar el diálogo de estrategia
|
||||||
|
def show_path_strategy_dialog(gate_point):
|
||||||
|
dialog = QtGui.QDialog()
|
||||||
|
dialog.setWindowTitle("Seleccionar Estrategia de Caminos")
|
||||||
|
layout = QtGui.QVBoxLayout(dialog)
|
||||||
|
|
||||||
|
label = QtGui.QLabel("Seleccione la estrategia para crear los caminos internos:")
|
||||||
|
layout.addWidget(label)
|
||||||
|
|
||||||
|
rb1 = QtGui.QRadioButton("Estrategia 1: Caminos independientes desde cada CT hasta la puerta")
|
||||||
|
rb1.setChecked(True)
|
||||||
|
layout.addWidget(rb1)
|
||||||
|
|
||||||
|
rb2 = QtGui.QRadioButton("Estrategia 2: Único camino que une todos los CTs y la puerta")
|
||||||
|
layout.addWidget(rb2)
|
||||||
|
|
||||||
|
btn_box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
|
||||||
|
layout.addWidget(btn_box)
|
||||||
|
|
||||||
|
def on_accept():
|
||||||
|
strategy = 1 if rb1.isChecked() else 2
|
||||||
|
dialog.accept()
|
||||||
|
creator = InternalPathCreator(gate_point, strategy)
|
||||||
|
creator.build()
|
||||||
|
|
||||||
|
btn_box.accepted.connect(on_accept)
|
||||||
|
btn_box.rejected.connect(dialog.reject)
|
||||||
|
|
||||||
|
dialog.exec_()
|
||||||
|
|
||||||
|
|
||||||
|
# Uso: seleccionar un punto para la puerta de entrada
|
||||||
|
def onSelectGatePoint():
|
||||||
|
'''gate = FreeCAD.ActiveDocument.findObjects(Name="FenceGate")[0]
|
||||||
|
gate_point = gate.Placement.Base
|
||||||
|
show_path_strategy_dialog(gate_point)'''
|
||||||
|
|
||||||
|
sel = FreeCADGui.Selection.getSelectionEx()[0]
|
||||||
|
show_path_strategy_dialog(sel.SubObjects[0].CenterOfMass)
|
||||||
@@ -224,7 +224,6 @@ def spreadsheetBOQPoles(sheet: Worksheet, selection: List[FreeCAD.DocumentObject
|
|||||||
# Write data to sheet
|
# Write data to sheet
|
||||||
row = 3
|
row = 3
|
||||||
group_from = row
|
group_from = row
|
||||||
print(poles_data[0])
|
|
||||||
f = poles_data[0]['frame']
|
f = poles_data[0]['frame']
|
||||||
for i, data in enumerate(poles_data):
|
for i, data in enumerate(poles_data):
|
||||||
if f != data["frame"]:
|
if f != data["frame"]:
|
||||||
@@ -255,6 +254,7 @@ def spreadsheetBOQPoles(sheet: Worksheet, selection: List[FreeCAD.DocumentObject
|
|||||||
sheet['F{0}'.format(row)].number_format = "0.000"
|
sheet['F{0}'.format(row)].number_format = "0.000"
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
sheet['G{0}'.format(row)] = round(data['head_z']) * scale
|
sheet['G{0}'.format(row)] = round(data['head_z']) * scale
|
||||||
sheet['G{0}'.format(row)].number_format = "0.000"
|
sheet['G{0}'.format(row)].number_format = "0.000"
|
||||||
sheet['H{0}'.format(row)] = data["length"] * scale
|
sheet['H{0}'.format(row)] = data["length"] * scale
|
||||||
|
|||||||
1138
Export/exportDXF.py
1138
Export/exportDXF.py
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>715</width>
|
<width>462</width>
|
||||||
<height>520</height>
|
<height>282</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -78,6 +78,11 @@
|
|||||||
<string>Lineweight</string>
|
<string>Lineweight</string>
|
||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -204,6 +209,21 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="widget" native="true">
|
<widget class="QWidget" name="widget" native="true">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="buttonAcept">
|
<widget class="QPushButton" name="buttonAcept">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ import Part
|
|||||||
import numpy
|
import numpy
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from xml.etree.ElementTree import Element, SubElement
|
||||||
|
import xml.etree.ElementTree as ElementTree
|
||||||
|
import datetime
|
||||||
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
from numpy.matrixlib.defmatrix import matrix
|
||||||
|
|
||||||
|
from Utils import PVPlantUtils as utils
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
import FreeCADGui
|
import FreeCADGui
|
||||||
from PySide import QtCore
|
from PySide import QtCore
|
||||||
@@ -44,33 +53,30 @@ except AttributeError:
|
|||||||
def _fromUtf8(s):
|
def _fromUtf8(s):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
from PVPlantResources import DirIcons as DirIcons
|
try:
|
||||||
|
import collada
|
||||||
|
COLLADA_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
COLLADA_AVAILABLE = False
|
||||||
|
|
||||||
__title__ = "FreeCAD PVSyst importer"
|
__title__ = "FreeCAD PVSyst importer"
|
||||||
__author__ = "Javier"
|
__author__ = "Javier Braña"
|
||||||
#__url__ = "http://www.freecadweb.org"
|
#__url__ = "http://www.freecadweb.org"
|
||||||
|
|
||||||
try:
|
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||||
# Python 2 forward compatibility
|
|
||||||
range = xrange
|
|
||||||
except NameError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def checkCollada():
|
def check_collada():
|
||||||
"checks if collada if available"
|
"""Verifica la disponibilidad de pycollada"""
|
||||||
|
if not COLLADA_AVAILABLE:
|
||||||
global collada
|
FreeCAD.Console.PrintError(translate("PVPlant", "pycollada no encontrado, soporte Collada desactivado.") + "\n")
|
||||||
COLLADA = None
|
return COLLADA_AVAILABLE
|
||||||
try:
|
|
||||||
import collada
|
|
||||||
except ImportError:
|
|
||||||
FreeCAD.Console.PrintError(translate("Arch", "pycollada not found, collada support is disabled.") + "\n")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
# Asegurar que el texto es Unicode válido
|
||||||
|
def safe_text(text):
|
||||||
|
if isinstance(text, bytes):
|
||||||
|
return text.decode('utf-8', errors='replace')
|
||||||
|
return text
|
||||||
|
|
||||||
# from ARCH:
|
# from ARCH:
|
||||||
def triangulate(shape):
|
def triangulate(shape):
|
||||||
@@ -79,6 +85,7 @@ def triangulate(shape):
|
|||||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||||
mesher = p.GetInt("ColladaMesher", 0)
|
mesher = p.GetInt("ColladaMesher", 0)
|
||||||
tessellation = p.GetFloat("ColladaTessellation", 1.0)
|
tessellation = p.GetFloat("ColladaTessellation", 1.0)
|
||||||
|
|
||||||
grading = p.GetFloat("ColladaGrading", 0.3)
|
grading = p.GetFloat("ColladaGrading", 0.3)
|
||||||
segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
|
segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
|
||||||
segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
|
segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
|
||||||
@@ -91,9 +98,15 @@ def triangulate(shape):
|
|||||||
elif mesher == 1:
|
elif mesher == 1:
|
||||||
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
|
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
|
||||||
else:
|
else:
|
||||||
return MeshPart.meshFromShape(Shape=shape, GrowthRate=grading, SegPerEdge=segsperedge,
|
return MeshPart.meshFromShape(Shape=shape,
|
||||||
SegPerRadius=segsperradius, SecondOrder=secondorder, Optimize=optimize,
|
GrowthRate=grading,
|
||||||
AllowQuad=allowquads).Topology
|
SegPerEdge=segsperedge,
|
||||||
|
SegPerRadius=segsperradius,
|
||||||
|
SecondOrder=secondorder,
|
||||||
|
Optimize=optimize,
|
||||||
|
AllowQuad=allowquads
|
||||||
|
).Topology
|
||||||
|
|
||||||
|
|
||||||
def export(exportList, filename, tessellation=1, colors=None):
|
def export(exportList, filename, tessellation=1, colors=None):
|
||||||
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
|
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
|
||||||
@@ -249,12 +262,308 @@ def export(exportList, filename, tessellation=1, colors=None):
|
|||||||
|
|
||||||
FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename)
|
FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename)
|
||||||
|
|
||||||
def exportToDAE(path):
|
|
||||||
filename = path + ".dae"
|
|
||||||
|
|
||||||
def exportToPVC(path, exportTerrain = False):
|
def exportToPVC(path, exportTerrain=False):
|
||||||
filename = path + ".pvc"
|
filename = f"{path}.pvc"
|
||||||
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
|
||||||
|
# 1. Validación inicial de objetos esenciales
|
||||||
|
site = None
|
||||||
|
for obj in FreeCAD.ActiveDocument.Objects:
|
||||||
|
if obj.Name.startswith("Site") and hasattr(obj, 'Terrain'):
|
||||||
|
site = obj
|
||||||
|
break
|
||||||
|
|
||||||
|
if not site:
|
||||||
|
FreeCAD.Console.PrintError("No se encontró objeto 'Site' válido\n")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. Configuración de metadatos y autor
|
||||||
|
generated_on = str(datetime.datetime.now())
|
||||||
|
|
||||||
|
try:
|
||||||
|
author = FreeCAD.ActiveDocument.CreatedBy
|
||||||
|
except (AttributeError, UnicodeEncodeError):
|
||||||
|
author = "Unknown"
|
||||||
|
|
||||||
|
author = author.replace("<", "").replace(">", "")
|
||||||
|
ver = FreeCAD.Version()
|
||||||
|
appli = f"PVPlant for FreeCAD {ver[0]}.{ver[1]} build{ver[2]}"
|
||||||
|
|
||||||
|
# 3. Creación estructura XML base
|
||||||
|
root = Element('COLLADA')
|
||||||
|
root.set('xmlns', 'http://www.collada.org/2005/11/COLLADASchema')
|
||||||
|
root.set('version', '1.4.1')
|
||||||
|
|
||||||
|
# 4. Sección <asset>
|
||||||
|
asset = SubElement(root, 'asset')
|
||||||
|
contrib = SubElement(asset, 'contributor')
|
||||||
|
SubElement(contrib, 'author').text = safe_text(author)
|
||||||
|
SubElement(contrib, 'authoring_tool').text = safe_text(appli)
|
||||||
|
SubElement(asset, 'created').text = generated_on
|
||||||
|
SubElement(asset, 'modified').text = generated_on
|
||||||
|
SubElement(asset, 'title').text = safe_text(FreeCAD.ActiveDocument.Name)
|
||||||
|
unit = SubElement(asset, 'unit')
|
||||||
|
unit.set('name', 'meter')
|
||||||
|
unit.set('meter', '1')
|
||||||
|
|
||||||
|
# 5. Materiales y efectos
|
||||||
|
materials = ["Frames", "Tree_trunk", "Tree_crown", "Topography_mesh"]
|
||||||
|
|
||||||
|
# Library materials
|
||||||
|
lib_materials = SubElement(root, 'library_materials')
|
||||||
|
for i, name in enumerate(materials):
|
||||||
|
mat = SubElement(lib_materials, 'material')
|
||||||
|
mat.set('id', f'Material{i}')
|
||||||
|
mat.set('name', name)
|
||||||
|
SubElement(mat, 'instance_effect').set('url', f'#Material{i}-fx')
|
||||||
|
|
||||||
|
# Library effects
|
||||||
|
lib_effects = SubElement(root, 'library_effects')
|
||||||
|
for i, _ in enumerate(materials):
|
||||||
|
effect = SubElement(lib_effects, 'effect')
|
||||||
|
effect.set('id', f'Material{i}-fx')
|
||||||
|
effect.set('name', f'Material{i}')
|
||||||
|
profile = SubElement(effect, 'profile_COMMON')
|
||||||
|
technique = SubElement(profile, 'technique')
|
||||||
|
technique.set('sid', 'standard')
|
||||||
|
lambert = SubElement(technique, 'lambert')
|
||||||
|
|
||||||
|
# Componentes del material
|
||||||
|
color = SubElement(SubElement(lambert, 'emission'), 'color')
|
||||||
|
color.set('sid', 'emission')
|
||||||
|
color.text = '0.000000 0.000000 0.000000 1.000000'
|
||||||
|
color = SubElement(SubElement(lambert, 'ambient'), 'color')
|
||||||
|
color.set('sid', 'ambient')
|
||||||
|
color.text = '0.200000 0.200000 0.200000 1.000000'
|
||||||
|
color = SubElement(SubElement(lambert, 'diffuse'), 'color')
|
||||||
|
color.set('sid', 'diffuse')
|
||||||
|
color.text = '0.250000 0.500000 0.000000 1.000000'
|
||||||
|
transparent = SubElement(lambert, 'transparent')
|
||||||
|
transparent.set('opaque', 'RGB_ZERO')
|
||||||
|
color = SubElement(transparent, 'color')
|
||||||
|
color.set('sid', 'transparent')
|
||||||
|
color.text = '0.000000 0.000000 0.000000 1.000000'
|
||||||
|
value = SubElement(SubElement(lambert, 'transparency'), 'float')
|
||||||
|
value.set('sid', 'transparency')
|
||||||
|
value.text = '0.000000'
|
||||||
|
|
||||||
|
# 6. Geometrías
|
||||||
|
lib_geometries = SubElement(root, 'library_geometries')
|
||||||
|
|
||||||
|
# 7. Escena visual
|
||||||
|
lib_visual = SubElement(root, 'library_visual_scenes')
|
||||||
|
visual_scene = SubElement(lib_visual, 'visual_scene')
|
||||||
|
visual_scene.set('id', 'Scene') # cambiar a visual_scene_0
|
||||||
|
visual_scene.set('name', 'Scene') # cambiar a Default visual scene
|
||||||
|
|
||||||
|
scene_node = SubElement(visual_scene, 'node')
|
||||||
|
scene_node.set('id', 'node_0_id')
|
||||||
|
scene_node.set('name', 'node_0_name')
|
||||||
|
scene_node.set('sid', 'node_0_sid')
|
||||||
|
|
||||||
|
scene_matrix = SubElement(scene_node, 'matrix')
|
||||||
|
scene_matrix.set('sid', 'matrix_0')
|
||||||
|
scene_matrix.text = '1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000'
|
||||||
|
|
||||||
|
root_node = SubElement(scene_node, 'node')
|
||||||
|
root_node.set('id', 'node_1_id')
|
||||||
|
root_node.set('name', 'node_1_name')
|
||||||
|
root_node.set('sid', 'node_1_sid')
|
||||||
|
|
||||||
|
# 8. Función para procesar geometrías
|
||||||
|
def create_geometry(name, vindex, findex, material_id, objind=0, frame_data=None, isTracker = False, axis_line=None):
|
||||||
|
"""Crea elementos COLLADA para una geometría"""
|
||||||
|
# Source (vertices)
|
||||||
|
source_mesh = SubElement(geom, 'mesh')
|
||||||
|
source = SubElement(source_mesh, 'source')
|
||||||
|
source.set('id', f'{name}-mesh_source')
|
||||||
|
float_array = SubElement(source, 'float_array')
|
||||||
|
float_array.set('id', f'{name}-float_array')
|
||||||
|
float_array.set('count', str(len(vindex)))
|
||||||
|
float_array.text = ' '.join(f'{v:.6f}' for v in vindex)
|
||||||
|
|
||||||
|
technique = SubElement(source, 'technique_common')
|
||||||
|
accessor = SubElement(technique, 'accessor')
|
||||||
|
accessor.set('count', str(len(vindex)))
|
||||||
|
accessor.set('source', f'#{name}-float_array')
|
||||||
|
accessor.set('stride', '3')
|
||||||
|
for ax in ['X', 'Y', 'Z']:
|
||||||
|
param = SubElement(accessor, 'param')
|
||||||
|
param.set('name', ax)
|
||||||
|
param.set('type', 'float')
|
||||||
|
|
||||||
|
# Vertices
|
||||||
|
vertices = SubElement(source_mesh, 'vertices')
|
||||||
|
vertices.set('id', f'{name}-vertices_source')
|
||||||
|
vertices = SubElement(vertices, 'input')
|
||||||
|
vertices.set('semantic', 'POSITION')
|
||||||
|
vertices.set('source', f'#{name}-mesh_source')
|
||||||
|
|
||||||
|
# Triangles
|
||||||
|
triangles = SubElement(source_mesh, 'triangles')
|
||||||
|
triangles.set('count', '0')
|
||||||
|
triangles.set('material', f'Material{material_id}')
|
||||||
|
triangles_input = SubElement(triangles, 'input')
|
||||||
|
triangles_input.set('offset', '0')
|
||||||
|
triangles_input.set('semantic', 'VERTEX')
|
||||||
|
triangles_input.set('source', f'#{name}-vertices_source')
|
||||||
|
|
||||||
|
p = SubElement(triangles, 'p')
|
||||||
|
p.text = ' '.join(map(str, findex))
|
||||||
|
|
||||||
|
# Parámetros especiales para estructuras
|
||||||
|
|
||||||
|
frame_params = SubElement(source_mesh, 'tracker_parameters')
|
||||||
|
if frame_data:
|
||||||
|
for key, val in frame_data.items():
|
||||||
|
elem = SubElement(frame_params, key)
|
||||||
|
elem.text = str(val)
|
||||||
|
|
||||||
|
if isTracker:
|
||||||
|
axis_parameter = SubElement(frame_params, 'axis_vertices')
|
||||||
|
if axis_line:
|
||||||
|
for idx, vert in enumerate(axis_line):
|
||||||
|
array = SubElement(axis_parameter, 'float_array')
|
||||||
|
array.set('id', f'{name}-axis_float_array{idx}')
|
||||||
|
array.set('count', '3')
|
||||||
|
array.text = ' '.join(f'{v:.6f}' for v in vert)
|
||||||
|
|
||||||
|
# 9. Procesar estructuras (frames/trackers)
|
||||||
|
center = FreeCAD.Vector()
|
||||||
|
if site.Terrain:
|
||||||
|
center = site.Terrain.Mesh.BoundBox.Center
|
||||||
|
|
||||||
|
objind = 0
|
||||||
|
for frame_type in site.Frames:
|
||||||
|
is_tracker = "tracker" in frame_type.Proxy.Type.lower()
|
||||||
|
|
||||||
|
modules = frame_type.Shape.SubShapes[0].SubShapes[0]
|
||||||
|
pts = []
|
||||||
|
for i in range(4):
|
||||||
|
pts.append(modules.BoundBox.getPoint(i))
|
||||||
|
|
||||||
|
new_shape = Part.Face(Part.makePolygon(pts))
|
||||||
|
mesh = Mesh.Mesh(triangulate(new_shape))
|
||||||
|
axis = Part.makeLine(FreeCAD.Vector(modules.BoundBox.XMin, 0, modules.BoundBox.ZMax),
|
||||||
|
FreeCAD.Vector(modules.BoundBox.XMax, 0, modules.BoundBox.ZMax))
|
||||||
|
|
||||||
|
for obj in FreeCAD.ActiveDocument.Objects:
|
||||||
|
if hasattr(obj, "Setup") and obj.Setup == frame_type:
|
||||||
|
# Procesar geometría
|
||||||
|
mesh.Placement = obj.getGlobalPlacement()
|
||||||
|
axis.Placement = obj.getGlobalPlacement()
|
||||||
|
|
||||||
|
# Transformar vértices
|
||||||
|
vindex = []
|
||||||
|
for point in mesh.Points:
|
||||||
|
adjusted = (point.Vector - center) * scale
|
||||||
|
vindex.extend([
|
||||||
|
-adjusted.x,
|
||||||
|
adjusted.z,
|
||||||
|
adjusted.y
|
||||||
|
])
|
||||||
|
|
||||||
|
# Índices de caras
|
||||||
|
findex = []
|
||||||
|
for facet in mesh.Facets:
|
||||||
|
findex.extend(facet.PointIndices)
|
||||||
|
|
||||||
|
# AXIS
|
||||||
|
# TODO: revisar si es así:
|
||||||
|
vaxis = []
|
||||||
|
for vert in axis.Vertexes:
|
||||||
|
adjusted = (vert.Point - center) * scale
|
||||||
|
vaxis.append([
|
||||||
|
-adjusted.x,
|
||||||
|
adjusted.z,
|
||||||
|
adjusted.y
|
||||||
|
])
|
||||||
|
|
||||||
|
# Crear geometría COLLADA
|
||||||
|
geom = SubElement(lib_geometries, 'geometry')
|
||||||
|
geom.set('id', f'Frame_{objind}')
|
||||||
|
|
||||||
|
# Parámetros específicos de estructura
|
||||||
|
frame_data = {
|
||||||
|
'module_width': obj.Setup.ModuleWidth.Value,
|
||||||
|
'module_height': obj.Setup.ModuleHeight.Value,
|
||||||
|
'module_x_spacing': obj.Setup.ModuleColGap.Value,
|
||||||
|
'module_y_spacing': obj.Setup.ModuleRowGap.Value,
|
||||||
|
'module_name': 'Generic'
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_tracker:
|
||||||
|
frame_data.update({
|
||||||
|
'tracker_type': 'single_axis_trackers',
|
||||||
|
'min_phi': obj.Setup.MinPhi.Value,
|
||||||
|
'max_phi': obj.Setup.MaxPhi.Value,
|
||||||
|
'min_theta': 0,
|
||||||
|
'max_theta': 0
|
||||||
|
})
|
||||||
|
|
||||||
|
create_geometry(
|
||||||
|
name=f'Frame_{objind}',
|
||||||
|
vindex=vindex,
|
||||||
|
findex=findex,
|
||||||
|
material_id=0,
|
||||||
|
objind=objind,
|
||||||
|
frame_data=frame_data,
|
||||||
|
isTracker = is_tracker,
|
||||||
|
axis_line=vaxis
|
||||||
|
)
|
||||||
|
|
||||||
|
# Instancia en escena
|
||||||
|
instance = SubElement(root_node, 'instance_geometry')
|
||||||
|
instance.set('url', f'#Frame_{objind}')
|
||||||
|
|
||||||
|
bind_material = SubElement(instance, 'bind_material')
|
||||||
|
technique_common = SubElement(bind_material, 'technique_common')
|
||||||
|
instance_material = SubElement(technique_common, 'instance_material')
|
||||||
|
instance_material.set('symbol', 'Material0')
|
||||||
|
instance_material.set('target', '#Material0')
|
||||||
|
|
||||||
|
objind += 1
|
||||||
|
|
||||||
|
# 10. Procesar terreno si está habilitado
|
||||||
|
if exportTerrain and site.Terrain:
|
||||||
|
mesh = site.Terrain.Mesh
|
||||||
|
vindex = []
|
||||||
|
for point in mesh.Points:
|
||||||
|
point = point.Vector
|
||||||
|
vindex.extend([
|
||||||
|
-point.x * SCALE,
|
||||||
|
point.z * SCALE,
|
||||||
|
point.y * SCALE
|
||||||
|
])
|
||||||
|
|
||||||
|
findex = []
|
||||||
|
for facet in mesh.Facets:
|
||||||
|
findex.extend(facet.PointIndices)
|
||||||
|
|
||||||
|
geom = SubElement(lib_geometries, 'geometry')
|
||||||
|
geom.set('id', 'Terrain')
|
||||||
|
create_geometry('Terrain', vindex, findex, material_id=3)
|
||||||
|
|
||||||
|
instance = SubElement(root_node, 'instance_geometry')
|
||||||
|
instance.set('url', '#Terrain')
|
||||||
|
|
||||||
|
# 11. Escena principal
|
||||||
|
scene = SubElement(root, 'scene')
|
||||||
|
SubElement(scene, 'instance_visual_scene').set('url', '#Scene')
|
||||||
|
|
||||||
|
# 12. Exportar a archivo
|
||||||
|
xml_str = minidom.parseString(
|
||||||
|
ElementTree.tostring(root, encoding='utf-8')
|
||||||
|
).toprettyxml(indent=" ")
|
||||||
|
|
||||||
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(xml_str)
|
||||||
|
|
||||||
|
FreeCAD.Console.PrintMessage(f"Archivo PVC generado: {filename}\n")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def exportToPVC_old(path, exportTerrain = False):
|
||||||
|
filename = f"{path}.pvc"
|
||||||
|
|
||||||
from xml.etree.ElementTree import Element, SubElement
|
from xml.etree.ElementTree import Element, SubElement
|
||||||
import datetime
|
import datetime
|
||||||
@@ -295,17 +604,18 @@ def exportToPVC(path, exportTerrain = False):
|
|||||||
|
|
||||||
# xml: 1. Asset:
|
# xml: 1. Asset:
|
||||||
asset = SubElement(root, 'asset')
|
asset = SubElement(root, 'asset')
|
||||||
|
|
||||||
asset_contributor = SubElement(asset, 'contributor')
|
asset_contributor = SubElement(asset, 'contributor')
|
||||||
asset_contributor_autor = SubElement(asset_contributor, 'autor')
|
asset_contributor_autor = SubElement(asset_contributor, 'author')
|
||||||
#asset_contributor_autor.text = author
|
asset_contributor_autor.text = author
|
||||||
asset_contributor_authoring_tool = SubElement(asset_contributor, 'authoring_tool')
|
asset_contributor_authoring_tool = SubElement(asset_contributor, 'authoring_tool')
|
||||||
#asset_contributor_authoring_tool.text = appli
|
asset_contributor_authoring_tool.text = appli
|
||||||
asset_contributor_comments = SubElement(asset_contributor, 'comments')
|
asset_contributor_comments = SubElement(asset_contributor, 'comments')
|
||||||
asset_keywords = SubElement(asset, 'keywords')
|
asset_keywords = SubElement(asset, 'keywords')
|
||||||
asset_revision = SubElement(asset, 'revision')
|
asset_revision = SubElement(asset, 'revision')
|
||||||
asset_subject = SubElement(asset, 'subject')
|
asset_subject = SubElement(asset, 'subject')
|
||||||
asset_tittle = SubElement(asset, 'title')
|
asset_tittle = SubElement(asset, 'title')
|
||||||
#asset_tittle.text = FreeCAD.ActiveDocument.Name
|
asset_tittle.text = FreeCAD.ActiveDocument.Name
|
||||||
asset_unit = SubElement(asset, 'unit')
|
asset_unit = SubElement(asset, 'unit')
|
||||||
asset_unit.set('meter', '0.001')
|
asset_unit.set('meter', '0.001')
|
||||||
asset_unit.set('name', 'millimeter')
|
asset_unit.set('name', 'millimeter')
|
||||||
@@ -363,7 +673,6 @@ def exportToPVC(path, exportTerrain = False):
|
|||||||
# xml: 4. library_geometries:
|
# xml: 4. library_geometries:
|
||||||
library_geometries = SubElement(root, 'library_geometries')
|
library_geometries = SubElement(root, 'library_geometries')
|
||||||
def add_geometry(objtype, vindex, findex, objind = 0, centers = None):
|
def add_geometry(objtype, vindex, findex, objind = 0, centers = None):
|
||||||
|
|
||||||
isFrame = False
|
isFrame = False
|
||||||
if objtype == 0:
|
if objtype == 0:
|
||||||
geometryName = 'Frame'
|
geometryName = 'Frame'
|
||||||
@@ -509,35 +818,20 @@ def exportToPVC(path, exportTerrain = False):
|
|||||||
end_time.text = '1.000000'
|
end_time.text = '1.000000'
|
||||||
|
|
||||||
# xml: 6. scene:
|
# xml: 6. scene:
|
||||||
scene = SubElement(root, 'scene')
|
'''scene = SubElement(root, 'scene')
|
||||||
instance = SubElement(scene, 'instance_visual_scene')
|
instance = SubElement(scene, 'instance_visual_scene')
|
||||||
instance.set('url', '#')
|
instance.set('url', '#')'''
|
||||||
|
|
||||||
full_list_of_objects = FreeCAD.ActiveDocument.Objects
|
|
||||||
|
|
||||||
# CASO 1 - FRAMES:
|
# CASO 1 - FRAMES:
|
||||||
frameType = site.Frames
|
frameType = site.Frames
|
||||||
frame_setup = {"type": [],
|
|
||||||
"footprint": []}
|
|
||||||
for obj in frameType:
|
|
||||||
frame_setup["type"] = obj
|
|
||||||
frame_setup["footprint"] = ""
|
|
||||||
|
|
||||||
objind = 0
|
objind = 0
|
||||||
|
|
||||||
# TODO: revisar
|
# TODO: revisar
|
||||||
for typ in frameType:
|
for typ in frameType:
|
||||||
isTracker = "tracker" in typ.Proxy.Type.lower()
|
isTracker = "tracker" in typ.Proxy.Type.lower()
|
||||||
|
#isTracker = False
|
||||||
|
|
||||||
objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
objectlist = utils.findObjects("Tracker")
|
||||||
tmp = []
|
|
||||||
for obj in objectlist:
|
|
||||||
if obj.Name.startswith("TrackerSetup"):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
tmp.append(obj)
|
|
||||||
objectlist = tmp.copy()
|
|
||||||
|
|
||||||
for obj in objectlist:
|
for obj in objectlist:
|
||||||
if obj.Setup == typ:
|
if obj.Setup == typ:
|
||||||
findex = numpy.array([])
|
findex = numpy.array([])
|
||||||
@@ -583,7 +877,6 @@ def exportToPVC(path, exportTerrain = False):
|
|||||||
v = Topology[0][i]
|
v = Topology[0][i]
|
||||||
vindex[list(range(i * 3, i * 3 + 3))] = (-(v.x - center.x) * scale, (v.z - center.z) * scale,
|
vindex[list(range(i * 3, i * 3 + 3))] = (-(v.x - center.x) * scale, (v.z - center.z) * scale,
|
||||||
(v.y - center.y) * scale)
|
(v.y - center.y) * scale)
|
||||||
|
|
||||||
# 2. face indices
|
# 2. face indices
|
||||||
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
findex = numpy.empty(len(Topology[1]) * 3, numpy.int64)
|
||||||
for i in range(len(Topology[1])):
|
for i in range(len(Topology[1])):
|
||||||
@@ -818,8 +1111,6 @@ class PVSystTaskPanel:
|
|||||||
name = date + "-" + name
|
name = date + "-" + name
|
||||||
filename = os.path.join(path, name)
|
filename = os.path.join(path, name)
|
||||||
|
|
||||||
#if self.form.cbDAE.isChecked():
|
|
||||||
# exportToDAE(filename)
|
|
||||||
|
|
||||||
if self.form.cbPVC.isChecked():
|
if self.form.cbPVC.isChecked():
|
||||||
exportToPVC(filename, self.form.cbTerrain.isChecked())
|
exportToPVC(filename, self.form.cbTerrain.isChecked())
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import urllib.request
|
|||||||
import math
|
import math
|
||||||
import utm
|
import utm
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import PVPlantImportGrid as ImportElevation
|
||||||
|
|
||||||
scale = 1000.0
|
scale = 1000.0
|
||||||
|
|
||||||
@@ -41,9 +42,10 @@ class OSMImporter:
|
|||||||
}
|
}
|
||||||
self.ssl_context = ssl.create_default_context(cafile=certifi.where())
|
self.ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||||
|
|
||||||
def transform_from_latlon(self, lat, lon):
|
def transform_from_latlon(self, coordinates):
|
||||||
x, y, _, _ = utm.from_latlon(lat, lon)
|
points = ImportElevation.getElevationFromOE(coordinates)
|
||||||
return FreeCAD.Vector(x, y, .0) * scale - self.Origin
|
pts = [FreeCAD.Vector(p.x, p.y, p.z).sub(self.Origin) for p in points]
|
||||||
|
return pts
|
||||||
|
|
||||||
def get_osm_data(self, bbox):
|
def get_osm_data(self, bbox):
|
||||||
query = f"""
|
query = f"""
|
||||||
@@ -67,7 +69,7 @@ class OSMImporter:
|
|||||||
headers={'User-Agent': 'FreeCAD-OSM-Importer/1.0'},
|
headers={'User-Agent': 'FreeCAD-OSM-Importer/1.0'},
|
||||||
method='POST'
|
method='POST'
|
||||||
)
|
)
|
||||||
return urllib.request.urlopen(req, context=self.ssl_context, timeout=30).read()
|
return urllib.request.urlopen(req, context=self.ssl_context, timeout=160).read()
|
||||||
|
|
||||||
def create_layer(self, name):
|
def create_layer(self, name):
|
||||||
if not FreeCAD.ActiveDocument.getObject(name):
|
if not FreeCAD.ActiveDocument.getObject(name):
|
||||||
@@ -78,11 +80,16 @@ class OSMImporter:
|
|||||||
root = ET.fromstring(osm_data)
|
root = ET.fromstring(osm_data)
|
||||||
|
|
||||||
# Almacenar nodos transformados
|
# Almacenar nodos transformados
|
||||||
|
coordinates = [[float(node.attrib['lat']), float(node.attrib['lon'])] for node in root.findall('node')]
|
||||||
|
coordinates = self.transform_from_latlon(coordinates)
|
||||||
|
for i, node in enumerate(root.findall('node')):
|
||||||
|
self. nodes[node.attrib['id']] = coordinates[i]
|
||||||
|
'''return
|
||||||
for node in root.findall('node'):
|
for node in root.findall('node'):
|
||||||
self.nodes[node.attrib['id']] = self.transform_from_latlon(
|
self.nodes[node.attrib['id']] = self.transform_from_latlon(
|
||||||
float(node.attrib['lat']),
|
float(node.attrib['lat']),
|
||||||
float(node.attrib['lon'])
|
float(node.attrib['lon'])
|
||||||
)
|
)'''
|
||||||
|
|
||||||
# Procesar ways
|
# Procesar ways
|
||||||
for way in root.findall('way'):
|
for way in root.findall('way'):
|
||||||
@@ -92,10 +99,24 @@ class OSMImporter:
|
|||||||
'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
|
'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print("1. Create Transportations")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_transportation()
|
self.create_transportation()
|
||||||
|
|
||||||
|
print("2. Create Buildings")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_buildings()
|
self.create_buildings()
|
||||||
|
|
||||||
|
print("3. Create Power Infrastructure")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_power_infrastructure()
|
self.create_power_infrastructure()
|
||||||
|
|
||||||
|
print("4. Create Vegetation")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_vegetation()
|
self.create_vegetation()
|
||||||
|
|
||||||
|
print("5. Create Water Bodies")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_water_bodies()
|
self.create_water_bodies()
|
||||||
|
|
||||||
def create_transportation(self):
|
def create_transportation(self):
|
||||||
@@ -118,27 +139,26 @@ class OSMImporter:
|
|||||||
'secondary': 5.0,
|
'secondary': 5.0,
|
||||||
'tertiary': 4.0
|
'tertiary': 4.0
|
||||||
}.get(highway_type, 3.0)
|
}.get(highway_type, 3.0)
|
||||||
|
self.create_road(nodes, width, highway_type, transport_layer, tags['name'] if 'name' in tags else "")
|
||||||
self.create_road(nodes, width, highway_type, transport_layer)
|
|
||||||
|
|
||||||
# Vías férreas
|
# Vías férreas
|
||||||
if 'railway' in tags:
|
if 'railway' in tags:
|
||||||
self.create_railway(nodes, transport_layer)
|
self.create_railway(nodes, transport_layer, tags['name'] if 'name' in tags else "")
|
||||||
|
|
||||||
def create_road(self, nodes, width, road_type, layer):
|
def create_road(self, nodes, width, road_type, layer, name=""):
|
||||||
points = [n for n in nodes]
|
points = [n for n in nodes]
|
||||||
polyline = Draft.make_wire(points, closed=False, face=False)
|
polyline = Draft.make_wire(points, closed=False, face=False)
|
||||||
polyline.Label = f"Road_{road_type}"
|
polyline.Label = f"Road_{name if (name != '') else road_type}"
|
||||||
polyline.ViewObject.LineWidth = 2.0
|
polyline.ViewObject.LineWidth = 2.0
|
||||||
polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5))
|
polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5))
|
||||||
polyline.addProperty("App::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
|
polyline.addProperty("App::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
|
||||||
polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
|
polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
|
||||||
layer.addObject(polyline)
|
layer.addObject(polyline)
|
||||||
|
|
||||||
def create_railway(self, nodes, layer):
|
def create_railway(self, nodes, layer, name=""):
|
||||||
points = [n for n in nodes]
|
points = [n for n in nodes]
|
||||||
rail_line = Draft.make_wire(points, closed=False, face=False)
|
rail_line = Draft.make_wire(points, closed=False, face=False)
|
||||||
rail_line.Label = "Railway"
|
rail_line.Label = f"{name if (name != '') else 'Railway'}"
|
||||||
rail_line.ViewObject.LineWidth = 1.5
|
rail_line.ViewObject.LineWidth = 1.5
|
||||||
rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
|
rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
|
||||||
layer.addObject(rail_line)
|
layer.addObject(rail_line)
|
||||||
@@ -146,6 +166,7 @@ class OSMImporter:
|
|||||||
def create_buildings(self):
|
def create_buildings(self):
|
||||||
building_layer = self.create_layer("Buildings")
|
building_layer = self.create_layer("Buildings")
|
||||||
for way_id, data in self.ways_data.items():
|
for way_id, data in self.ways_data.items():
|
||||||
|
print(data)
|
||||||
if 'building' not in data['tags']:
|
if 'building' not in data['tags']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -205,9 +226,12 @@ class OSMImporter:
|
|||||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||||
|
|
||||||
if 'power' in tags:
|
if 'power' in tags:
|
||||||
|
print("\n\n")
|
||||||
|
print(tags)
|
||||||
feature_type = tags['power']
|
feature_type = tags['power']
|
||||||
|
|
||||||
if feature_type == 'line':
|
if feature_type == 'line':
|
||||||
|
print("3.1. Create Power Lines")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_power_line(
|
self.create_power_line(
|
||||||
nodes=nodes,
|
nodes=nodes,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
@@ -215,6 +239,8 @@ class OSMImporter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif feature_type == 'substation':
|
elif feature_type == 'substation':
|
||||||
|
print("3.1. Create substations")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_substation(
|
self.create_substation(
|
||||||
way_id=way_id,
|
way_id=way_id,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
@@ -223,11 +249,17 @@ class OSMImporter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif feature_type == 'tower':
|
elif feature_type == 'tower':
|
||||||
|
print("3.1. Create power towers")
|
||||||
|
FreeCADGui.updateGui()
|
||||||
self.create_power_tower(
|
self.create_power_tower(
|
||||||
position=nodes[0] if nodes else None,
|
position=nodes[0] if nodes else None,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
layer=power_layer
|
layer=power_layer
|
||||||
)
|
)
|
||||||
|
'''self.create_power_tower_1(
|
||||||
|
way=way_id,
|
||||||
|
layer=power_layer
|
||||||
|
)'''
|
||||||
|
|
||||||
def create_power_line(self, nodes, tags, layer):
|
def create_power_line(self, nodes, tags, layer):
|
||||||
"""Crea líneas de transmisión eléctrica con propiedades técnicas"""
|
"""Crea líneas de transmisión eléctrica con propiedades técnicas"""
|
||||||
@@ -244,10 +276,10 @@ class OSMImporter:
|
|||||||
return
|
return
|
||||||
|
|
||||||
wire = Draft.make_wire(points, closed=False, face=False)
|
wire = Draft.make_wire(points, closed=False, face=False)
|
||||||
wire.Label = f"Power_Line_{voltage}V"
|
wire.Label = f"Power_Line_{int(voltage/1000000)}kV"
|
||||||
|
|
||||||
# Propiedades visuales
|
# Propiedades visuales
|
||||||
wire.ViewObject.LineWidth = 1 + (voltage / 100000)
|
wire.ViewObject.LineWidth = 6 #'''1 + (voltage / 100000)'''
|
||||||
color = self.feature_colors['power']['line']
|
color = self.feature_colors['power']['line']
|
||||||
wire.ViewObject.ShapeColor = color
|
wire.ViewObject.ShapeColor = color
|
||||||
|
|
||||||
@@ -260,7 +292,7 @@ class OSMImporter:
|
|||||||
layer.addObject(wire)
|
layer.addObject(wire)
|
||||||
|
|
||||||
# Añadir torres si es overhead
|
# Añadir torres si es overhead
|
||||||
if line_type == 'overhead':
|
'''if line_type == 'overhead':
|
||||||
distance_between_towers = 150 # metros por defecto
|
distance_between_towers = 150 # metros por defecto
|
||||||
if 'distance_between_towers' in tags:
|
if 'distance_between_towers' in tags:
|
||||||
try:
|
try:
|
||||||
@@ -273,7 +305,7 @@ class OSMImporter:
|
|||||||
voltage=voltage,
|
voltage=voltage,
|
||||||
distance=distance_between_towers,
|
distance=distance_between_towers,
|
||||||
layer=layer
|
layer=layer
|
||||||
)
|
)'''
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")
|
FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")
|
||||||
@@ -318,7 +350,8 @@ class OSMImporter:
|
|||||||
|
|
||||||
# Unir componentes
|
# Unir componentes
|
||||||
tower = base.fuse(mast)
|
tower = base.fuse(mast)
|
||||||
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
|
tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Power_Tower")
|
||||||
|
layer.addObject(tower_obj)
|
||||||
tower_obj.Shape = tower
|
tower_obj.Shape = tower
|
||||||
tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']
|
tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']
|
||||||
|
|
||||||
@@ -354,6 +387,164 @@ class OSMImporter:
|
|||||||
cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
|
cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
|
||||||
layer.addObject(cable)
|
layer.addObject(cable)
|
||||||
|
|
||||||
|
def create_power_tower_1(self, way, layer):
|
||||||
|
"""Crea una torre eléctrica según especificaciones OSM"""
|
||||||
|
tags = way['tags']
|
||||||
|
nodes = [self.nodes[ref] for ref in way['nodes'] if ref in self.nodes]
|
||||||
|
|
||||||
|
if not nodes:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Obtener parámetros principales
|
||||||
|
structure_type = tags.get('structure', 'lattice')
|
||||||
|
material = tags.get('material', 'steel')
|
||||||
|
height = float(tags.get('height', 30.0)) # Altura en metros
|
||||||
|
voltage = self.parse_voltage(tags.get('voltage', '132000')) # 132kV por defecto
|
||||||
|
lines = int(tags.get('lines', '3'))
|
||||||
|
operator = tags.get('operator', '')
|
||||||
|
tower_name = tags.get('name', f"Tower_{way['id']}")
|
||||||
|
|
||||||
|
# Calcular posición (usar primer nodo)
|
||||||
|
position = FreeCAD.Vector(*nodes[0])
|
||||||
|
|
||||||
|
# Configurar dimensiones basadas en parámetros
|
||||||
|
base_size = self.calculate_base_size(structure_type, height)
|
||||||
|
crossarm_length = self.calculate_crossarm_length(voltage, lines)
|
||||||
|
color = self.get_material_color(material)
|
||||||
|
|
||||||
|
# Crear geometría según tipo de estructura
|
||||||
|
if structure_type == 'lattice':
|
||||||
|
tower = self.create_lattice_tower(height, base_size, crossarm_length)
|
||||||
|
elif structure_type == 'tubular':
|
||||||
|
tower = self.create_tubular_tower(height, base_size, crossarm_length)
|
||||||
|
elif structure_type == 'portal':
|
||||||
|
tower = self.create_portal_tower(height, base_size, crossarm_length)
|
||||||
|
else:
|
||||||
|
tower = self.create_default_tower(height, base_size, crossarm_length)
|
||||||
|
|
||||||
|
# Crear objeto en FreeCAD
|
||||||
|
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
|
||||||
|
tower_obj.Shape = tower
|
||||||
|
tower_obj.ViewObject.ShapeColor = color
|
||||||
|
tower_obj.Placement.Base = position
|
||||||
|
|
||||||
|
# Añadir propiedades técnicas
|
||||||
|
tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Voltaje nominal (V)").Voltage = voltage
|
||||||
|
tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Altura total (m)").Height = height
|
||||||
|
tower_obj.addProperty("App::PropertyString", "StructureType", "Technical",
|
||||||
|
"Tipo de estructura").StructureType = structure_type
|
||||||
|
tower_obj.addProperty("App::PropertyString", "Material", "Technical",
|
||||||
|
"Material de construcción").Material = material
|
||||||
|
tower_obj.addProperty("App::PropertyInteger", "Lines", "Technical", "Número de circuitos").Lines = lines
|
||||||
|
if operator:
|
||||||
|
tower_obj.addProperty("App::PropertyString", "Operator", "General", "Operador").Operator = operator
|
||||||
|
|
||||||
|
# Añadir cables si existen nodos de conexión
|
||||||
|
if len(nodes) >= 2:
|
||||||
|
connection_points = [FreeCAD.Vector(*n) for n in nodes]
|
||||||
|
self.create_power_lines_between_towers(connection_points, voltage, layer)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
FreeCAD.Console.PrintError(f"Error creando torre {way['id']}: {str(e)}\n")
|
||||||
|
|
||||||
|
def create_lattice_tower(self, height, base_size, crossarm_length):
|
||||||
|
"""Crea torre de celosía tipo armazón"""
|
||||||
|
# Base
|
||||||
|
base = Part.makeBox(base_size, base_size, 3.0)
|
||||||
|
|
||||||
|
# Patas principales
|
||||||
|
leg_profile = Part.makeBox(0.5, 0.5, height)
|
||||||
|
legs = []
|
||||||
|
for x in [-base_size / 2, base_size / 2]:
|
||||||
|
for y in [-base_size / 2, base_size / 2]:
|
||||||
|
leg = leg_profile.copy()
|
||||||
|
leg.translate(FreeCAD.Vector(x, y, 3.0))
|
||||||
|
legs.append(leg)
|
||||||
|
|
||||||
|
# Travesaños horizontales
|
||||||
|
horizontal_bars = []
|
||||||
|
for z in [10.0, height / 2, height - 5.0]:
|
||||||
|
bar = Part.makeBox(base_size + 1.0, 0.3, 0.3, FreeCAD.Vector(-(base_size + 1) / 2, -0.15, z))
|
||||||
|
horizontal_bars.append(bar)
|
||||||
|
|
||||||
|
# Crucetas
|
||||||
|
crossarms = self.create_crossarms(height, crossarm_length)
|
||||||
|
|
||||||
|
# Unir todas las partes
|
||||||
|
tower = base.multiFuse(legs + horizontal_bars + crossarms)
|
||||||
|
return tower.removeSplitter()
|
||||||
|
|
||||||
|
def create_crossarms(self, height, length):
|
||||||
|
"""Crea crucetas para líneas eléctricas"""
|
||||||
|
crossarms = []
|
||||||
|
positions = [
|
||||||
|
(height - 5.0, 0), # Superior
|
||||||
|
(height - 15.0, 22.5), # Media con ángulo
|
||||||
|
(height - 25.0, -22.5) # Inferior con ángulo
|
||||||
|
]
|
||||||
|
|
||||||
|
for z, angle in positions:
|
||||||
|
crossarm = Part.makeBox(length, 0.3, 0.3)
|
||||||
|
crossarm.rotate(FreeCAD.Vector(0, 0, z), FreeCAD.Vector(0, 0, 1), angle)
|
||||||
|
crossarm.translate(FreeCAD.Vector(-length / 2, -0.15, z))
|
||||||
|
crossarms.append(crossarm)
|
||||||
|
|
||||||
|
return crossarms
|
||||||
|
|
||||||
|
def calculate_base_size(self, structure_type, height):
|
||||||
|
"""Calcula tamaño de base según tipo de estructura y altura"""
|
||||||
|
base_sizes = {
|
||||||
|
'lattice': 4.0 + (height * 0.1),
|
||||||
|
'tubular': 3.0 + (height * 0.05),
|
||||||
|
'portal': 6.0,
|
||||||
|
'default': 3.0
|
||||||
|
}
|
||||||
|
return base_sizes.get(structure_type, base_sizes['default'])
|
||||||
|
|
||||||
|
def calculate_crossarm_length(self, voltage, lines):
|
||||||
|
"""Calcula longitud de crucetas según voltaje y número de circuitos"""
|
||||||
|
return (voltage / 100000) * 8.0 + (lines * 2.0)
|
||||||
|
|
||||||
|
def get_material_color(self, material):
|
||||||
|
"""Devuelve color según material de construcción"""
|
||||||
|
colors = {
|
||||||
|
'steel': (0.65, 0.65, 0.7),
|
||||||
|
'concrete': (0.8, 0.8, 0.8),
|
||||||
|
'wood': (0.5, 0.3, 0.2),
|
||||||
|
'aluminum': (0.9, 0.9, 0.9),
|
||||||
|
'default': (0.5, 0.5, 0.5)
|
||||||
|
}
|
||||||
|
return colors.get(material.lower(), colors['default'])
|
||||||
|
|
||||||
|
def create_power_lines_between_towers(self, points, voltage, layer):
|
||||||
|
"""Crea cables entre torres"""
|
||||||
|
cable_thickness = 0.1 + (voltage / 500000)
|
||||||
|
|
||||||
|
for i in range(len(points) - 1):
|
||||||
|
start = points[i]
|
||||||
|
end = points[i + 1]
|
||||||
|
|
||||||
|
# Crear cable curvo (catenaria)
|
||||||
|
cable = self.create_catenary(start, end, sag=5.0)
|
||||||
|
cable_obj = layer.addObject("Part::Feature", f"Power_Line_{i}")
|
||||||
|
cable_obj.Shape = cable
|
||||||
|
cable_obj.ViewObject.LineWidth = cable_thickness * 1000 # Convertir a mm
|
||||||
|
cable_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)
|
||||||
|
|
||||||
|
def create_catenary(self, start, end, sag=5.0):
|
||||||
|
"""Crea curva de catenaria para cables eléctricos"""
|
||||||
|
mid_point = (start + end) / 2
|
||||||
|
mid_point.z -= sag
|
||||||
|
|
||||||
|
arc = Part.Arc(
|
||||||
|
start,
|
||||||
|
mid_point,
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
return Part.Edge(arc.toShape())
|
||||||
|
|
||||||
def create_substation(self, way_id, tags, nodes, layer):
|
def create_substation(self, way_id, tags, nodes, layer):
|
||||||
"""Crea subestaciones con todos los componentes detallados"""
|
"""Crea subestaciones con todos los componentes detallados"""
|
||||||
try:
|
try:
|
||||||
@@ -551,27 +742,163 @@ class OSMImporter:
|
|||||||
def create_vegetation(self):
|
def create_vegetation(self):
|
||||||
vegetation_layer = self.create_layer("Vegetation")
|
vegetation_layer = self.create_layer("Vegetation")
|
||||||
|
|
||||||
# Árboles individuales
|
# Procesar nodos de vegetación individual
|
||||||
for node_id, coords in self.nodes.items():
|
for way_id, tags in self.ways_data.items():
|
||||||
# Verificar si es un árbol
|
coords = self.nodes.get(way_id)
|
||||||
# (Necesitarías procesar los tags de los nodos, implementación simplificada)
|
if not coords:
|
||||||
cylinder = Part.makeCylinder(0.5, 5.0, FreeCAD.Vector(coords[0], coords[1], 0))# * scale - self.Origin )
|
continue
|
||||||
tree = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
|
|
||||||
vegetation_layer.addObject(tree)
|
|
||||||
tree.Shape = cylinder
|
|
||||||
tree.ViewObject.ShapeColor = self.feature_colors['vegetation']
|
|
||||||
|
|
||||||
# Áreas verdes
|
pos = FreeCAD.Vector(*coords)
|
||||||
|
|
||||||
|
if tags.get('natural') == 'tree':
|
||||||
|
self.create_tree(pos, tags, vegetation_layer)
|
||||||
|
elif tags.get('natural') == 'shrub':
|
||||||
|
self.create_shrub(pos, tags, vegetation_layer)
|
||||||
|
"""elif tags.get('natural') == 'tree_stump':
|
||||||
|
self.create_tree_stump(pos, vegetation_layer)"""
|
||||||
|
|
||||||
|
# Procesar áreas vegetales
|
||||||
for way_id, data in self.ways_data.items():
|
for way_id, data in self.ways_data.items():
|
||||||
if 'natural' in data['tags'] or 'landuse' in data['tags']:
|
tags = data['tags']
|
||||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||||
if len(nodes) > 2:
|
|
||||||
polygon_points = [n for n in nodes]
|
if not nodes or len(nodes) < 3:
|
||||||
polygon = Part.makePolygon(polygon_points)
|
continue
|
||||||
face = Part.Face(polygon)
|
|
||||||
area = vegetation_layer.addObject("Part::Feature", "GreenArea")
|
if tags.get('natural') == 'wood' or tags.get('landuse') == 'forest':
|
||||||
area.Shape = face
|
self.create_forest(nodes, tags, vegetation_layer)
|
||||||
area.ViewObject.ShapeColor = self.feature_colors['vegetation']
|
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):
|
def create_water_bodies(self):
|
||||||
water_layer = self.create_layer("Water")
|
water_layer = self.create_layer("Water")
|
||||||
@@ -583,7 +910,8 @@ class OSMImporter:
|
|||||||
polygon_points = [n for n in nodes]
|
polygon_points = [n for n in nodes]
|
||||||
polygon = Part.makePolygon(polygon_points)
|
polygon = Part.makePolygon(polygon_points)
|
||||||
face = Part.Face(polygon)
|
face = Part.Face(polygon)
|
||||||
water = water_layer.addObject("Part::Feature", "WaterBody")
|
water = FreeCAD.ActiveDocument.addObject("Part::Feature", "WaterBody")
|
||||||
|
water_layer.addObject(water)
|
||||||
water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin )
|
water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin )
|
||||||
water.ViewObject.ShapeColor = self.feature_colors['water']
|
water.ViewObject.ShapeColor = self.feature_colors['water']
|
||||||
|
|
||||||
|
|||||||
16
InitGui.py
16
InitGui.py
@@ -48,7 +48,7 @@ class PVPlantWorkbench(Workbench):
|
|||||||
self.projectlist = PVPlantTools.projectlist
|
self.projectlist = PVPlantTools.projectlist
|
||||||
self.projectlist.insert(0, 'Reload')
|
self.projectlist.insert(0, 'Reload')
|
||||||
self.projectlist.insert(1, 'Separator')
|
self.projectlist.insert(1, 'Separator')
|
||||||
self.framelist = PVPlantTools.pv_list
|
self.framelist = PVPlantTools.pv_mechanical
|
||||||
|
|
||||||
from Export import ExporterCommands
|
from Export import ExporterCommands
|
||||||
self.inportExportlist = ExporterCommands.Exportlist
|
self.inportExportlist = ExporterCommands.Exportlist
|
||||||
@@ -59,6 +59,7 @@ class PVPlantWorkbench(Workbench):
|
|||||||
"PVPlantBuilding",
|
"PVPlantBuilding",
|
||||||
"PVPlantFenceGroup",
|
"PVPlantFenceGroup",
|
||||||
]'''
|
]'''
|
||||||
|
from Electrical.PowerConverter import PowerConverter
|
||||||
self.electricalList = ["PVPlantStringBox",
|
self.electricalList = ["PVPlantStringBox",
|
||||||
"PVPlantCable",
|
"PVPlantCable",
|
||||||
"PVPlanElectricalLine",
|
"PVPlanElectricalLine",
|
||||||
@@ -66,6 +67,8 @@ class PVPlantWorkbench(Workbench):
|
|||||||
"Stringing",
|
"Stringing",
|
||||||
"Separator",
|
"Separator",
|
||||||
"StringInverter",
|
"StringInverter",
|
||||||
|
"Separator",
|
||||||
|
"PowerConverter"
|
||||||
]
|
]
|
||||||
|
|
||||||
self.roads = ["PVPlantRoad",
|
self.roads = ["PVPlantRoad",
|
||||||
@@ -77,14 +80,14 @@ class PVPlantWorkbench(Workbench):
|
|||||||
|
|
||||||
# Toolbar
|
# Toolbar
|
||||||
self.appendToolbar("Civil", self.projectlist) # creates a new toolbar with your commands
|
self.appendToolbar("Civil", self.projectlist) # creates a new toolbar with your commands
|
||||||
self.appendToolbar("PVPlant", self.framelist) # creates a new toolbar with your commands
|
self.appendToolbar("Mechanical", self.framelist) # creates a new toolbar with your commands
|
||||||
self.appendToolbar("Shadow", self.objectlist) # creates a new toolbar with your commands
|
self.appendToolbar("Shadow", self.objectlist) # creates a new toolbar with your commands
|
||||||
self.appendToolbar("Outputs", self.inportExportlist) # creates a new toolbar with your commands
|
self.appendToolbar("Outputs", self.inportExportlist) # creates a new toolbar with your commands
|
||||||
self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands
|
self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands
|
||||||
|
|
||||||
# Menu
|
# Menu
|
||||||
self.appendMenu("&Civil", self.projectlist) # creates a new menu
|
self.appendMenu("&Civil", self.projectlist) # creates a new menu
|
||||||
self.appendMenu("&PVPlant", self.framelist) # creates a new menu
|
self.appendMenu("&Mechanical", self.framelist) # creates a new menu
|
||||||
self.appendMenu("&Shadow", self.objectlist) # creates a new menu
|
self.appendMenu("&Shadow", self.objectlist) # creates a new menu
|
||||||
self.appendMenu("&Outputs", self.inportExportlist) # creates a new menu
|
self.appendMenu("&Outputs", self.inportExportlist) # creates a new menu
|
||||||
self.appendMenu("&Electrical", self.electricalList) # creates a new menu
|
self.appendMenu("&Electrical", self.electricalList) # creates a new menu
|
||||||
@@ -145,13 +148,14 @@ class PVPlantWorkbench(Workbench):
|
|||||||
import SelectionObserver
|
import SelectionObserver
|
||||||
import FreeCADGui
|
import FreeCADGui
|
||||||
|
|
||||||
self.observer = SelectionObserver.SelObserver()
|
#self.observer = SelectionObserver.SelObserver()
|
||||||
FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
|
#FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
|
||||||
return
|
return
|
||||||
|
|
||||||
def Deactivated(self):
|
def Deactivated(self):
|
||||||
"This function is executed when the workbench is deactivated"
|
"This function is executed when the workbench is deactivated"
|
||||||
FreeCADGui.Selection.removeObserver(self.observer)
|
|
||||||
|
#FreeCADGui.Selection.removeObserver(self.observer)
|
||||||
return
|
return
|
||||||
|
|
||||||
def ContextMenu(self, recipient):
|
def ContextMenu(self, recipient):
|
||||||
|
|||||||
877
Mechanical/Frame/PVPlantFixedFrame.ui
Normal file
877
Mechanical/Frame/PVPlantFixedFrame.ui
Normal file
@@ -0,0 +1,877 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>formRack</class>
|
||||||
|
<widget class="QDialog" name="formRack">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>476</width>
|
||||||
|
<height>1032</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Fixed Frame:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Módulos:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>Altura (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editModuleHeight">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>5.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>1.990000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>Largura (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editModuleLenght">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>5.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.960000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>Anchura (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editModuleWidth">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.030000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="text">
|
||||||
|
<string>Potencia (wp)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSpinBox" name="editModulePower">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>150</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>1000</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>350</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>Estructura</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Columnas (un)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editFrontHeight">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>5.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.800000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Orientación del módulo</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editVerticalGap">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.900000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.020000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="editRows">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="comboFrameType">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Fija</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Tracker 1 Eje</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editLeftOffset">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.900000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.050000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="0">
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>Offset borde derecha (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ángulo de inclinación (º)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editRightOffset">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.900000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.050000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="1">
|
||||||
|
<widget class="QSpinBox" name="editTilt">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>60</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="1">
|
||||||
|
<widget class="QSpinBox" name="editInclination">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>60</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Filas (un)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Distancia al suelo en el frente (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSpinBox" name="editCols">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>20</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="labelVerticalGap">
|
||||||
|
<property name="text">
|
||||||
|
<string>Separación vertical entre módulos (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="comboModuleOrientation">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Landscape</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Portrait</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Separación horizontal entre módulos (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ängulo máximo de inclinación longitudinal (ª)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editHorizontalGap">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.900000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.020000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Tipo de estructura</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="text">
|
||||||
|
<string>Offset borde izquierda (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="0" colspan="2">
|
||||||
|
<widget class="QWidget" name="widgetTracker" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="labelVerticalGap_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Separación entre uniones (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="checkBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Separación Motor (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="editInternalGapNumber">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_19">
|
||||||
|
<property name="text">
|
||||||
|
<string>Número de uniones</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editInternalGap">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.900000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.020000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editMotorGap">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>0.900000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.010000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.020000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
|
<property name="title">
|
||||||
|
<string>Resultado</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_13">
|
||||||
|
<property name="text">
|
||||||
|
<string>Total de módulos</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="editTotalModules">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="text">
|
||||||
|
<string>Potencia total (wp)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="editTotalPower">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="text">
|
||||||
|
<string>Longitud (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="editTotalLength">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="text">
|
||||||
|
<string>Anchura (m)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="editTotalWidth">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>140</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,9 @@ class MapWindow(QtGui.QWidget):
|
|||||||
self.maxLat = None
|
self.maxLat = None
|
||||||
self.minLon = None
|
self.minLon = None
|
||||||
self.maxLon = None
|
self.maxLon = None
|
||||||
|
self.zoom = None
|
||||||
self.WinTitle = WinTitle
|
self.WinTitle = WinTitle
|
||||||
|
self.georeference_coordinates = {'lat': None, 'lon': None}
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
|
|
||||||
def setupUi(self):
|
def setupUi(self):
|
||||||
@@ -152,6 +154,9 @@ class MapWindow(QtGui.QWidget):
|
|||||||
self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
|
self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
|
||||||
RightLayout.addWidget(self.checkboxImportGis)
|
RightLayout.addWidget(self.checkboxImportGis)
|
||||||
|
|
||||||
|
self.checkboxImportSatelitalImagen = QtGui.QCheckBox("Importar Imagen Satelital")
|
||||||
|
RightLayout.addWidget(self.checkboxImportSatelitalImagen)
|
||||||
|
|
||||||
verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||||
RightLayout.addItem(verticalSpacer)
|
RightLayout.addItem(verticalSpacer)
|
||||||
|
|
||||||
@@ -192,6 +197,7 @@ class MapWindow(QtGui.QWidget):
|
|||||||
"var data = drawnItems.toGeoJSON();"
|
"var data = drawnItems.toGeoJSON();"
|
||||||
"MyApp.shapes(JSON.stringify(data));"
|
"MyApp.shapes(JSON.stringify(data));"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
@QtCore.Slot(float, float)
|
@QtCore.Slot(float, float)
|
||||||
@@ -203,17 +209,22 @@ class MapWindow(QtGui.QWidget):
|
|||||||
' | UTM: ' + str(zone_number) + zone_letter +
|
' | UTM: ' + str(zone_number) + zone_letter +
|
||||||
', {:.5f}m E, {:.5f}m N'.format(x, y))
|
', {:.5f}m E, {:.5f}m N'.format(x, y))
|
||||||
|
|
||||||
@QtCore.Slot(float, float, float, float)
|
@QtCore.Slot(float, float, float, float, int)
|
||||||
def onMapZoom(self, minLat, minLon, maxLat, maxLon):
|
def onMapZoom(self, minLat, minLon, maxLat, maxLon, zoom):
|
||||||
self.minLat = min([minLat, maxLat])
|
self.minLat = min([minLat, maxLat])
|
||||||
self.maxLat = max([minLat, maxLat])
|
self.maxLat = max([minLat, maxLat])
|
||||||
self.minLon = min([minLon, maxLon])
|
self.minLon = min([minLon, maxLon])
|
||||||
self.maxLon = max([minLon, maxLon])
|
self.maxLon = max([minLon, maxLon])
|
||||||
|
self.zoom = zoom
|
||||||
|
|
||||||
@QtCore.Slot(float, float)
|
@QtCore.Slot(float, float)
|
||||||
def georeference(self, lat, lng):
|
def georeference(self, lat, lng):
|
||||||
import PVPlantSite
|
import PVPlantSite
|
||||||
from geopy.geocoders import Nominatim
|
from geopy.geocoders import Nominatim
|
||||||
|
|
||||||
|
self.georeference_coordinates['lat'] = lat
|
||||||
|
self.georeference_coordinates['lon'] = lng
|
||||||
|
|
||||||
Site = PVPlantSite.get(create=True)
|
Site = PVPlantSite.get(create=True)
|
||||||
Site.Proxy.setLatLon(lat, lng)
|
Site.Proxy.setLatLon(lat, lng)
|
||||||
|
|
||||||
@@ -278,7 +289,7 @@ class MapWindow(QtGui.QWidget):
|
|||||||
pts = [p.sub(offset) for p in tmp]
|
pts = [p.sub(offset) for p in tmp]
|
||||||
|
|
||||||
obj = Draft.makeWire(pts, closed=cw, face=False)
|
obj = Draft.makeWire(pts, closed=cw, face=False)
|
||||||
#obj.Placement.Base = offset
|
#obj.Placement.Base = Site.Origin
|
||||||
obj.Label = name
|
obj.Label = name
|
||||||
Draft.autogroup(obj)
|
Draft.autogroup(obj)
|
||||||
|
|
||||||
@@ -288,6 +299,170 @@ class MapWindow(QtGui.QWidget):
|
|||||||
if self.checkboxImportGis.isChecked():
|
if self.checkboxImportGis.isChecked():
|
||||||
self.getDataFromOSM(self.minLat, self.minLon, self.maxLat, self.maxLon)
|
self.getDataFromOSM(self.minLat, self.minLon, self.maxLat, self.maxLon)
|
||||||
|
|
||||||
|
if self.checkboxImportSatelitalImagen.isChecked():
|
||||||
|
# Usar los límites reales del terreno (rectangular)
|
||||||
|
'''s_lat = self.minLat
|
||||||
|
s_lon = self.minLon
|
||||||
|
n_lat = self.maxLat
|
||||||
|
n_lon = self.maxLon
|
||||||
|
|
||||||
|
# Obtener puntos UTM para las esquinas
|
||||||
|
corners = ImportElevation.getElevationFromOE([
|
||||||
|
[s_lat, s_lon], # Esquina suroeste
|
||||||
|
[n_lat, s_lon], # Esquina sureste
|
||||||
|
[n_lat, n_lon], # Esquina noreste
|
||||||
|
[s_lat, n_lon] # Esquina noroeste
|
||||||
|
])
|
||||||
|
|
||||||
|
if not corners or len(corners) < 4:
|
||||||
|
FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Descargar imagen satelital
|
||||||
|
from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
|
||||||
|
downloader = GoogleMapDownloader(
|
||||||
|
zoom= 18, #self.zoom,
|
||||||
|
layer='raw_satellite'
|
||||||
|
)
|
||||||
|
img = downloader.generateImage(
|
||||||
|
sw_lat=s_lat,
|
||||||
|
sw_lng=s_lon,
|
||||||
|
ne_lat=n_lat,
|
||||||
|
ne_lng=n_lon
|
||||||
|
)
|
||||||
|
|
||||||
|
# Guardar imagen en el directorio del documento
|
||||||
|
doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
|
||||||
|
if not doc_path:
|
||||||
|
doc_path = FreeCAD.ConfigGet("UserAppData")
|
||||||
|
|
||||||
|
filename = os.path.join(doc_path, "background.jpeg")
|
||||||
|
img.save(filename)
|
||||||
|
|
||||||
|
ancho, alto = img.size
|
||||||
|
|
||||||
|
# Crear objeto de imagen en FreeCAD
|
||||||
|
doc = FreeCAD.ActiveDocument
|
||||||
|
img_obj = doc.addObject('Image::ImagePlane', 'Background')
|
||||||
|
img_obj.ImageFile = filename
|
||||||
|
img_obj.Label = 'Background'
|
||||||
|
|
||||||
|
# Calcular dimensiones en metros usando las coordenadas UTM
|
||||||
|
# Extraer las coordenadas de las esquinas
|
||||||
|
sw = corners[0] # Suroeste
|
||||||
|
se = corners[1] # Sureste
|
||||||
|
ne = corners[2] # Noreste
|
||||||
|
nw = corners[3] # Noroeste
|
||||||
|
|
||||||
|
# Calcular ancho (promedio de los lados superior e inferior)
|
||||||
|
width_bottom = se.x - sw.x
|
||||||
|
width_top = ne.x - nw.x
|
||||||
|
width_m = (width_bottom + width_top) / 2
|
||||||
|
|
||||||
|
# Calcular alto (promedio de los lados izquierdo y derecho)
|
||||||
|
height_left = nw.y - sw.y
|
||||||
|
height_right = ne.y - se.y
|
||||||
|
height_m = (height_left + height_right) / 2
|
||||||
|
|
||||||
|
img_obj.XSize = width_m
|
||||||
|
img_obj.YSize = height_m
|
||||||
|
|
||||||
|
# Posicionar el centro de la imagen en (0,0,0)
|
||||||
|
img_obj.Placement.Base = FreeCAD.Vector(-width_m / 2, -height_m / 2, 0)'''
|
||||||
|
|
||||||
|
# Definir área rectangular
|
||||||
|
s_lat = self.minLat
|
||||||
|
s_lon = self.minLon
|
||||||
|
n_lat = self.maxLat
|
||||||
|
n_lon = self.maxLon
|
||||||
|
|
||||||
|
# Obtener puntos UTM para las esquinas y el punto de referencia
|
||||||
|
points = [
|
||||||
|
[s_lat, s_lon], # Suroeste
|
||||||
|
[n_lat, n_lon], # Noreste
|
||||||
|
[self.georeference_coordinates['lat'], self.georeference_coordinates['lon']] # Punto de referencia
|
||||||
|
]
|
||||||
|
utm_points = ImportElevation.getElevationFromOE(points)
|
||||||
|
|
||||||
|
if not utm_points or len(utm_points) < 3:
|
||||||
|
FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas y referencia\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
sw_utm, ne_utm, ref_utm = utm_points
|
||||||
|
|
||||||
|
# Descargar imagen satelital
|
||||||
|
from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
|
||||||
|
downloader = GoogleMapDownloader(
|
||||||
|
zoom=self.zoom,
|
||||||
|
layer='raw_satellite'
|
||||||
|
)
|
||||||
|
img = downloader.generateImage(
|
||||||
|
sw_lat=s_lat,
|
||||||
|
sw_lng=s_lon,
|
||||||
|
ne_lat=n_lat,
|
||||||
|
ne_lng=n_lon
|
||||||
|
)
|
||||||
|
|
||||||
|
# Guardar imagen
|
||||||
|
doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
|
||||||
|
if not doc_path:
|
||||||
|
doc_path = FreeCAD.ConfigGet("UserAppData")
|
||||||
|
|
||||||
|
filename = os.path.join(doc_path, "background.jpeg")
|
||||||
|
img.save(filename)
|
||||||
|
|
||||||
|
# Calcular dimensiones reales en metros
|
||||||
|
width_m = ne_utm.x - sw_utm.x # Ancho en metros (este-oeste)
|
||||||
|
height_m = ne_utm.y - sw_utm.y # Alto en metros (norte-sur)
|
||||||
|
|
||||||
|
# Calcular posición relativa del punto de referencia dentro de la imagen
|
||||||
|
rel_x = (ref_utm.x - sw_utm.x) / width_m if width_m != 0 else 0.5
|
||||||
|
rel_y = (ref_utm.y - sw_utm.y) / height_m if height_m != 0 else 0.5
|
||||||
|
|
||||||
|
# Crear objeto de imagen en FreeCAD
|
||||||
|
doc = FreeCAD.ActiveDocument
|
||||||
|
img_obj = doc.addObject('Image::ImagePlane', 'Background')
|
||||||
|
img_obj.ImageFile = filename
|
||||||
|
img_obj.Label = 'Background'
|
||||||
|
|
||||||
|
# Convertir dimensiones a milímetros (FreeCAD trabaja en mm)
|
||||||
|
img_obj.XSize = width_m * 1000
|
||||||
|
img_obj.YSize = height_m * 1000
|
||||||
|
|
||||||
|
# Posicionar para que el punto de referencia esté en (0,0,0)
|
||||||
|
# La esquina inferior izquierda debe estar en:
|
||||||
|
# x = -rel_x * ancho_total
|
||||||
|
# y = -rel_y * alto_total
|
||||||
|
img_obj.Placement.Base = FreeCAD.Vector(
|
||||||
|
-rel_x * width_m * 1000,
|
||||||
|
-rel_y * height_m * 1000,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
# Refrescar el documento
|
||||||
|
doc.recompute()
|
||||||
|
|
||||||
|
def calculate_texture_transform(self, mesh_obj, width_m, height_m):
|
||||||
|
"""Calcula la transformación precisa para la textura"""
|
||||||
|
try:
|
||||||
|
# Obtener coordenadas reales de las esquinas
|
||||||
|
import utm
|
||||||
|
sw = utm.from_latlon(self.minLat, self.minLon)
|
||||||
|
ne = utm.from_latlon(self.maxLat, self.maxLon)
|
||||||
|
|
||||||
|
# Crear matriz de transformación
|
||||||
|
scale_x = (ne[0] - sw[0]) / width_m
|
||||||
|
scale_y = (ne[1] - sw[1]) / height_m
|
||||||
|
|
||||||
|
# Aplicar transformación (solo si se usa textura avanzada)
|
||||||
|
if hasattr(mesh_obj.ViewObject, "TextureMapping"):
|
||||||
|
mesh_obj.ViewObject.TextureMapping = "PLANE"
|
||||||
|
mesh_obj.ViewObject.TextureScale = (scale_x, scale_y)
|
||||||
|
mesh_obj.ViewObject.TextureOffset = (sw[0], sw[1])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
FreeCAD.Console.PrintWarning(f"No se pudo calcular transformación: {str(e)}\n")
|
||||||
|
|
||||||
def getDataFromOSM(self, min_lat, min_lon, max_lat, max_lon):
|
def getDataFromOSM(self, min_lat, min_lon, max_lat, max_lon):
|
||||||
import Importer.importOSM as importOSM
|
import Importer.importOSM as importOSM
|
||||||
import PVPlantSite
|
import PVPlantSite
|
||||||
|
|||||||
@@ -122,12 +122,13 @@ def getElevationFromOE(coordinates):
|
|||||||
try:
|
try:
|
||||||
r = get(query, timeout=20, verify=certifi.where()) # <-- Corrección aquí
|
r = get(query, timeout=20, verify=certifi.where()) # <-- Corrección aquí
|
||||||
except RequestException as e:
|
except RequestException as e:
|
||||||
|
print(f"Error en la solicitud: {str(e)}")
|
||||||
points = []
|
points = []
|
||||||
for i, point in enumerate(coordinates):
|
for i, point in enumerate(coordinates):
|
||||||
c = utm.from_latlon(point[0], point[1])
|
c = utm.from_latlon(point[0], point[1])
|
||||||
points.append(FreeCAD.Vector(round(c[0] * 1000, 0),
|
points.append(FreeCAD.Vector(round(c[0], 0),
|
||||||
round(c[1] * 1000, 0),
|
round(c[1], 0),
|
||||||
0))
|
0) * 1000)
|
||||||
return points
|
return points
|
||||||
|
|
||||||
# Only get the json response in case of 200 or 201
|
# Only get the json response in case of 200 or 201
|
||||||
@@ -136,14 +137,16 @@ def getElevationFromOE(coordinates):
|
|||||||
results = r.json()
|
results = r.json()
|
||||||
for point in results["results"]:
|
for point in results["results"]:
|
||||||
c = utm.from_latlon(point["latitude"], point["longitude"])
|
c = utm.from_latlon(point["latitude"], point["longitude"])
|
||||||
v = FreeCAD.Vector(round(c[0] * 1000, 0),
|
v = FreeCAD.Vector(round(c[0], 0),
|
||||||
round(c[1] * 1000, 0),
|
round(c[1], 0),
|
||||||
round(point["elevation"] * 1000, 0))
|
round(point["elevation"], 0)) * 1000
|
||||||
points.append(v)
|
points.append(v)
|
||||||
return points
|
return points
|
||||||
|
|
||||||
def getSinglePointElevationFromBing(lat, lng):
|
def getSinglePointElevationFromBing(lat, lng):
|
||||||
#http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey}
|
#http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey}
|
||||||
|
import utm
|
||||||
|
|
||||||
source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points="
|
source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points="
|
||||||
source += str(lat) + "," + str(lng)
|
source += str(lat) + "," + str(lng)
|
||||||
source += "&heights=sealevel"
|
source += "&heights=sealevel"
|
||||||
@@ -153,11 +156,9 @@ def getSinglePointElevationFromBing(lat, lng):
|
|||||||
response = requests.get(source)
|
response = requests.get(source)
|
||||||
ans = response.text
|
ans = response.text
|
||||||
|
|
||||||
# +# to do: error handling - wait and try again
|
|
||||||
s = json.loads(ans)
|
s = json.loads(ans)
|
||||||
|
print(s)
|
||||||
res = s['resourceSets'][0]['resources'][0]['elevations']
|
res = s['resourceSets'][0]['resources'][0]['elevations']
|
||||||
|
|
||||||
import utm
|
|
||||||
for elevation in res:
|
for elevation in res:
|
||||||
c = utm.from_latlon(lat, lng)
|
c = utm.from_latlon(lat, lng)
|
||||||
v = FreeCAD.Vector(
|
v = FreeCAD.Vector(
|
||||||
@@ -324,7 +325,6 @@ def getSinglePointElevationUtm(lat, lon):
|
|||||||
print (v)
|
print (v)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
def getElevationUTM(polygon, lat, lng, resolution = 10000):
|
def getElevationUTM(polygon, lat, lng, resolution = 10000):
|
||||||
|
|
||||||
import utm
|
import utm
|
||||||
@@ -448,47 +448,6 @@ def getElevation(lat, lon, b=50.35, le=11.17, size=40):
|
|||||||
FreeCADGui.updateGui()
|
FreeCADGui.updateGui()
|
||||||
return FreeCAD.activeDocument().ActiveObject
|
return FreeCAD.activeDocument().ActiveObject
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
# original::
|
|
||||||
def getElevation(lat, lon, b=50.35, le=11.17, size=40):
|
|
||||||
tm.lat = lat
|
|
||||||
tm.lon = lon
|
|
||||||
baseheight = 0 #getheight(tm.lat, tm.lon)
|
|
||||||
center = tm.fromGeographic(tm.lat, tm.lon)
|
|
||||||
|
|
||||||
#https://maps.googleapis.com/maps/api/elevation/json?path=36.578581,-118.291994|36.23998,-116.83171&samples=3&key=YOUR_API_KEY
|
|
||||||
#https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key=YOUR_API_KEY
|
|
||||||
|
|
||||||
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
|
|
||||||
source += str(b-size*0.001) + "," + str(le) + "|" + str(b+size*0.001) + "," + str(le)
|
|
||||||
source += "&samples=" + str(100)
|
|
||||||
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
|
|
||||||
|
|
||||||
response = urllib.request.urlopen(source)
|
|
||||||
ans = response.read()
|
|
||||||
|
|
||||||
# +# to do: error handling - wait and try again
|
|
||||||
s = json.loads(ans)
|
|
||||||
res = s['results']
|
|
||||||
|
|
||||||
points = []
|
|
||||||
for r in res:
|
|
||||||
c = tm.fromGeographic(r['location']['lat'], r['location']['lng'])
|
|
||||||
v = FreeCAD.Vector(
|
|
||||||
round(c[0], 2),
|
|
||||||
round(c[1], 2),
|
|
||||||
round(r['elevation'] * 1000, 2) - baseheight
|
|
||||||
)
|
|
||||||
points.append(v)
|
|
||||||
|
|
||||||
line = Draft.makeWire(points, closed=False, face=False, support=None)
|
|
||||||
line.ViewObject.Visibility = False
|
|
||||||
#FreeCAD.activeDocument().recompute()
|
|
||||||
FreeCADGui.updateGui()
|
|
||||||
return FreeCAD.activeDocument().ActiveObject
|
|
||||||
'''
|
|
||||||
|
|
||||||
class _ImportPointsTaskPanel:
|
class _ImportPointsTaskPanel:
|
||||||
|
|
||||||
def __init__(self, obj = None):
|
def __init__(self, obj = None):
|
||||||
|
|||||||
@@ -138,8 +138,6 @@ class _Manhole(ArchComponent.Component):
|
|||||||
obj.Shape = ext_sol.cut([ins_sol, ], 0.0)
|
obj.Shape = ext_sol.cut([ins_sol, ], 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
|
class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
|
||||||
"A View Provider for the Pipe object"
|
"A View Provider for the Pipe object"
|
||||||
|
|
||||||
|
|||||||
1644
PVPlantPlacement.py
1644
PVPlantPlacement.py
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,318 @@
|
|||||||
<string>Park Settings</string>
|
<string>Park Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" alignment="Qt::AlignmentFlag::AlignTop">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Estructura:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QWidget" name="widget_2" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<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>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QPushButton" name="buttonPVArea">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="3">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Configuración</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QComboBox" name="comboDirV">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>De arriba a abajo</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>De abajo a arriba</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Del centro a los lados</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Dirección Horizontal</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QComboBox" name="comboDirH">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>De izquierda a derecha</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>De derecha a izquiera</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>De centro a los lados</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<widget class="QCheckBox" name="cbAlignFrames">
|
||||||
|
<property name="text">
|
||||||
|
<string>Alinear estructuras</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QSpinBox" name="editGapRows">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> mm</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>500</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Pitch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editOffsetHorizontal">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> m</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-10000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>10000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Orientación</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Offset Horizontal</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Offset Vertical</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editOffsetVertical">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> m</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-10000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>10000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editGapCols">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> m</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>100.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>5.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="comboOrientation">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Norte - Sur</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Este - Oeste</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Dirección Vertical</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Espacio entre filas</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="editInnerSpacing">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string> - Inner Spacing</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="6" column="0" colspan="3">
|
<item row="6" column="0" colspan="3">
|
||||||
<widget class="QGroupBox" name="groupCorridor">
|
<widget class="QGroupBox" name="groupCorridor">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@@ -59,10 +371,10 @@
|
|||||||
<item row="3" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QDoubleSpinBox" name="editRowGap">
|
<widget class="QDoubleSpinBox" name="editRowGap">
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="prefix">
|
<property name="prefix">
|
||||||
<string/>
|
<string/>
|
||||||
@@ -110,10 +422,10 @@
|
|||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QDoubleSpinBox" name="editColGap">
|
<widget class="QDoubleSpinBox" name="editColGap">
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="prefix">
|
<property name="prefix">
|
||||||
<string/>
|
<string/>
|
||||||
@@ -135,10 +447,10 @@
|
|||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QSpinBox" name="editRowCount">
|
<widget class="QSpinBox" name="editRowCount">
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>4</number>
|
<number>4</number>
|
||||||
@@ -148,10 +460,10 @@
|
|||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QSpinBox" name="editColCount">
|
<widget class="QSpinBox" name="editColCount">
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>8</number>
|
<number>8</number>
|
||||||
@@ -161,45 +473,6 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QPushButton" name="buttonPVArea">
|
|
||||||
<property name="text">
|
|
||||||
<string>Add</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QWidget" name="widget_2" native="true">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<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="buttonAddFrame">
|
|
||||||
<property name="text">
|
|
||||||
<string>Add</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="buttonRemoveFrame">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -207,282 +480,9 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="editPVArea"/>
|
<widget class="QLineEdit" name="editPVArea"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0" colspan="3">
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Configuración</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
|
||||||
<property name="horizontalSpacing">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<property name="verticalSpacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QLabel" name="label_9">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Offset Vertical</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Orientación</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="1">
|
|
||||||
<widget class="QCheckBox" name="cbAlignFrames">
|
|
||||||
<property name="text">
|
|
||||||
<string>Alinear estructuras</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Espacio entre filas</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Dirección Horizontal</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="0">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Offset Horizontal</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Pitch</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="label_11">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>120</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Dirección Vertical</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QComboBox" name="comboOrientation">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Norte - Sur</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Este - Oeste</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="editGapCols">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> m</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>3</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>100.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>5.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="QComboBox" name="comboDirH">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>De izquierda a derecha</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>De derecha a izquiera</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>De centro a los lados</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
|
||||||
<widget class="QComboBox" name="comboDirV">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>De arriba a abajo</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>De abajo a arriba</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Del centro a los lados</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="editOffsetHorizontal">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> m</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>3</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>-10000.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>10000.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="editOffsetVertical">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> m</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>3</number>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<double>-10000.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>10000.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QSpinBox" name="editGapRows">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> mm</string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>10000</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>500</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" alignment="Qt::AlignTop">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Estructura:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QListWidget" name="listFrameSetups">
|
<widget class="QListWidget" name="listFrameSetups">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
@@ -493,11 +493,19 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="cbSubfolders">
|
||||||
|
<property name="text">
|
||||||
|
<string>Organizar en subcarpetas</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>buttonAddFrame</tabstop>
|
|
||||||
<tabstop>buttonRemoveFrame</tabstop>
|
|
||||||
<tabstop>editPVArea</tabstop>
|
<tabstop>editPVArea</tabstop>
|
||||||
<tabstop>buttonPVArea</tabstop>
|
<tabstop>buttonPVArea</tabstop>
|
||||||
<tabstop>comboOrientation</tabstop>
|
<tabstop>comboOrientation</tabstop>
|
||||||
|
|||||||
@@ -578,22 +578,22 @@ class _PVPlantSite(ArchSite._Site):
|
|||||||
|
|
||||||
obj.addProperty("App::PropertyLink",
|
obj.addProperty("App::PropertyLink",
|
||||||
"Boundary",
|
"Boundary",
|
||||||
"Site",
|
"PVPlant",
|
||||||
"Boundary of land")
|
"Boundary of land")
|
||||||
|
|
||||||
obj.addProperty("App::PropertyLinkList",
|
obj.addProperty("App::PropertyLinkList",
|
||||||
"Frames",
|
"Frames",
|
||||||
"Site",
|
"PVPlant",
|
||||||
"Frames templates")
|
"Frames templates")
|
||||||
|
|
||||||
obj.addProperty("App::PropertyEnumeration",
|
obj.addProperty("App::PropertyEnumeration",
|
||||||
"UtmZone",
|
"UtmZone",
|
||||||
"Base",
|
"PVPlant",
|
||||||
"UTM zone").UtmZone = zone_list
|
"UTM zone").UtmZone = zone_list
|
||||||
|
|
||||||
obj.addProperty("App::PropertyVector",
|
obj.addProperty("App::PropertyVector",
|
||||||
"Origin",
|
"Origin",
|
||||||
"Base",
|
"PVPlant",
|
||||||
"Origin point.").Origin = (0, 0, 0)
|
"Origin point.").Origin = (0, 0, 0)
|
||||||
|
|
||||||
def onDocumentRestored(self, obj):
|
def onDocumentRestored(self, obj):
|
||||||
@@ -771,10 +771,12 @@ class _PVPlantSite(ArchSite._Site):
|
|||||||
import PVPlantImportGrid
|
import PVPlantImportGrid
|
||||||
x, y, zone_number, zone_letter = utm.from_latlon(lat, lon)
|
x, y, zone_number, zone_letter = utm.from_latlon(lat, lon)
|
||||||
self.obj.UtmZone = zone_list[zone_number - 1]
|
self.obj.UtmZone = zone_list[zone_number - 1]
|
||||||
# self.obj.UtmZone = "Z"+str(zone_number)
|
|
||||||
#z = PVPlantImportGrid.get_elevation(lat, lon)
|
point = PVPlantImportGrid.getElevationFromOE([[lat, lon]])
|
||||||
zz = PVPlantImportGrid.getSinglePointElevationFromBing(lat, lon)
|
self.obj.Origin = FreeCAD.Vector(point[0].x, point[0].y, point[0].z)
|
||||||
self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz.z)
|
self.obj.Latitude = lat
|
||||||
|
self.obj.Longitude = lon
|
||||||
|
self.obj.Elevation = point[0].z
|
||||||
|
|
||||||
|
|
||||||
class _ViewProviderSite(ArchSite._ViewProviderSite):
|
class _ViewProviderSite(ArchSite._ViewProviderSite):
|
||||||
|
|||||||
@@ -73,6 +73,42 @@ line_patterns = {
|
|||||||
"Dot (.5x) ...............................": 0x5555,
|
"Dot (.5x) ...............................": 0x5555,
|
||||||
"Dot (2x) . . . . . . . . . . .": 0x8888}
|
"Dot (2x) . . . . . . . . . . .": 0x8888}
|
||||||
|
|
||||||
|
|
||||||
|
def open_xyz_mmap(archivo_path):
|
||||||
|
"""
|
||||||
|
Usa memory-mapping para archivos muy grandes (máxima velocidad)
|
||||||
|
"""
|
||||||
|
# Primera pasada: contar líneas válidas
|
||||||
|
total_puntos = 0
|
||||||
|
with open(archivo_path, 'r') as f:
|
||||||
|
for linea in f:
|
||||||
|
partes = linea.strip().split()
|
||||||
|
if len(partes) >= 3:
|
||||||
|
try:
|
||||||
|
float(partes[0]);
|
||||||
|
float(partes[1]);
|
||||||
|
float(partes[2])
|
||||||
|
total_puntos += 1
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Segunda pasada: cargar datos
|
||||||
|
puntos = np.empty((total_puntos, 3))
|
||||||
|
idx = 0
|
||||||
|
|
||||||
|
with open(archivo_path, 'r') as f:
|
||||||
|
for linea in f:
|
||||||
|
partes = linea.strip().split()
|
||||||
|
if len(partes) >= 3:
|
||||||
|
try:
|
||||||
|
x, y, z = float(partes[0]), float(partes[1]), float(partes[2])
|
||||||
|
puntos[idx] = [x, y, z]
|
||||||
|
idx += 1
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return puntos
|
||||||
|
|
||||||
def makeTerrain(name="Terrain"):
|
def makeTerrain(name="Terrain"):
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Terrain")
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Terrain")
|
||||||
obj.Label = name
|
obj.Label = name
|
||||||
@@ -81,7 +117,6 @@ def makeTerrain(name="Terrain"):
|
|||||||
FreeCAD.ActiveDocument.recompute()
|
FreeCAD.ActiveDocument.recompute()
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class Terrain(ArchComponent.Component):
|
class Terrain(ArchComponent.Component):
|
||||||
"A Shadow Terrain Obcject"
|
"A Shadow Terrain Obcject"
|
||||||
|
|
||||||
@@ -161,101 +196,110 @@ class Terrain(ArchComponent.Component):
|
|||||||
if prop == "DEM" or prop == "CuttingBoundary":
|
if prop == "DEM" or prop == "CuttingBoundary":
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
if obj.DEM and obj.CuttingBoundary:
|
if obj.DEM and obj.CuttingBoundary:
|
||||||
'''
|
from pathlib import Path
|
||||||
Parámetro Descripción Requisitos
|
suffix = Path(obj.DEM).suffix
|
||||||
NCOLS: Cantidad de columnas de celdas Entero mayor que 0.
|
if suffix == '.asc':
|
||||||
NROWS: Cantidad de filas de celdas Entero mayor que 0.
|
'''
|
||||||
XLLCENTER o XLLCORNER: Coordenada X del origen (por el centro o la esquina inferior izquierda de la celda) Hacer coincidir con el tipo de coordenada y.
|
ASC format:
|
||||||
YLLCENTER o YLLCORNER: Coordenada Y del origen (por el centro o la esquina inferior izquierda de la celda) Hacer coincidir con el tipo de coordenada x.
|
|
||||||
CELLSIZE: Tamaño de celda Mayor que 0.
|
|
||||||
NODATA_VALUE: Los valores de entrada que serán NoData en el ráster de salida Opcional. El valor predeterminado es -9999
|
|
||||||
'''
|
|
||||||
grid_space = 1
|
|
||||||
file = open(obj.DEM, "r")
|
|
||||||
templist = [line.split() for line in file.readlines()]
|
|
||||||
file.close()
|
|
||||||
del file
|
|
||||||
|
|
||||||
# Read meta data:
|
Parámetro Descripción Requisitos
|
||||||
meta = templist[0:6]
|
NCOLS: Cantidad de columnas de celdas Entero mayor que 0.
|
||||||
nx = int(meta[0][1]) # NCOLS
|
NROWS: Cantidad de filas de celdas Entero mayor que 0.
|
||||||
ny = int(meta[1][1]) # NROWS
|
XLLCENTER o XLLCORNER: Coordenada X del origen (por el centro o la esquina inferior izquierda de la celda) Hacer coincidir con el tipo de coordenada y.
|
||||||
xllref = meta[2][0] # XLLCENTER / XLLCORNER
|
YLLCENTER o YLLCORNER: Coordenada Y del origen (por el centro o la esquina inferior izquierda de la celda) Hacer coincidir con el tipo de coordenada x.
|
||||||
xllvalue = round(float(meta[2][1]), 3)
|
CELLSIZE: Tamaño de celda Mayor que 0.
|
||||||
yllref = meta[3][0] # YLLCENTER / XLLCORNER
|
NODATA_VALUE: Los valores de entrada que serán NoData en el ráster de salida Opcional. El valor predeterminado es -9999
|
||||||
yllvalue = round(float(meta[3][1]), 3)
|
'''
|
||||||
cellsize = round(float(meta[4][1]), 3) # CELLSIZE
|
grid_space = 1
|
||||||
nodata_value = float(meta[5][1]) # NODATA_VALUE
|
file = open(obj.DEM, "r")
|
||||||
|
templist = [line.split() for line in file.readlines()]
|
||||||
|
file.close()
|
||||||
|
del file
|
||||||
|
|
||||||
# set coarse_factor
|
# Read meta data:
|
||||||
coarse_factor = max(round(grid_space / cellsize), 1)
|
meta = templist[0:6]
|
||||||
|
nx = int(meta[0][1]) # NCOLS
|
||||||
|
ny = int(meta[1][1]) # NROWS
|
||||||
|
xllref = meta[2][0] # XLLCENTER / XLLCORNER
|
||||||
|
xllvalue = round(float(meta[2][1]), 3)
|
||||||
|
yllref = meta[3][0] # YLLCENTER / XLLCORNER
|
||||||
|
yllvalue = round(float(meta[3][1]), 3)
|
||||||
|
cellsize = round(float(meta[4][1]), 3) # CELLSIZE
|
||||||
|
nodata_value = float(meta[5][1]) # NODATA_VALUE
|
||||||
|
|
||||||
# Get z values
|
# set coarse_factor
|
||||||
templist = templist[6:(6 + ny)]
|
coarse_factor = max(round(grid_space / cellsize), 1)
|
||||||
templist = [templist[i][0::coarse_factor] for i in np.arange(0, len(templist), coarse_factor)]
|
|
||||||
datavals = np.array(templist).astype(float)
|
|
||||||
del templist
|
|
||||||
|
|
||||||
# create xy coordinates
|
# Get z values
|
||||||
offset = self.site.Origin
|
templist = templist[6:(6 + ny)]
|
||||||
x = (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) * 1000 - offset.x
|
templist = [templist[i][0::coarse_factor] for i in np.arange(0, len(templist), coarse_factor)]
|
||||||
y = (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) * 1000 - offset.y
|
datavals = np.array(templist).astype(float)
|
||||||
datavals = datavals * 1000 # Ajuste de altura
|
del templist
|
||||||
|
|
||||||
# remove points out of area
|
# create xy coordinates
|
||||||
# 1. coarse:
|
offset = self.site.Origin
|
||||||
if obj.CuttingBoundary:
|
x = (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) * 1000 - offset.x
|
||||||
inc_x = obj.CuttingBoundary.Shape.BoundBox.XLength * 0.0
|
y = (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) * 1000 - offset.y
|
||||||
inc_y = obj.CuttingBoundary.Shape.BoundBox.YLength * 0.0
|
datavals = datavals * 1000 # Ajuste de altura
|
||||||
tmp = np.where(np.logical_and(x >= (obj.CuttingBoundary.Shape.BoundBox.XMin - inc_x),
|
|
||||||
x <= (obj.CuttingBoundary.Shape.BoundBox.XMax + inc_x)))[0]
|
|
||||||
x_max = np.ndarray.max(tmp)
|
|
||||||
x_min = np.ndarray.min(tmp)
|
|
||||||
|
|
||||||
tmp = np.where(np.logical_and(y >= (obj.CuttingBoundary.Shape.BoundBox.YMin - inc_y),
|
# remove points out of area
|
||||||
y <= (obj.CuttingBoundary.Shape.BoundBox.YMax + inc_y)))[0]
|
# 1. coarse:
|
||||||
y_max = np.ndarray.max(tmp)
|
if obj.CuttingBoundary:
|
||||||
y_min = np.ndarray.min(tmp)
|
inc_x = obj.CuttingBoundary.Shape.BoundBox.XLength * 0.0
|
||||||
del tmp
|
inc_y = obj.CuttingBoundary.Shape.BoundBox.YLength * 0.0
|
||||||
|
tmp = np.where(np.logical_and(x >= (obj.CuttingBoundary.Shape.BoundBox.XMin - inc_x),
|
||||||
|
x <= (obj.CuttingBoundary.Shape.BoundBox.XMax + inc_x)))[0]
|
||||||
|
x_max = np.ndarray.max(tmp)
|
||||||
|
x_min = np.ndarray.min(tmp)
|
||||||
|
|
||||||
x = x[x_min:x_max+1]
|
tmp = np.where(np.logical_and(y >= (obj.CuttingBoundary.Shape.BoundBox.YMin - inc_y),
|
||||||
y = y[y_min:y_max+1]
|
y <= (obj.CuttingBoundary.Shape.BoundBox.YMax + inc_y)))[0]
|
||||||
datavals = datavals[y_min:y_max+1, x_min:x_max+1]
|
y_max = np.ndarray.max(tmp)
|
||||||
|
y_min = np.ndarray.min(tmp)
|
||||||
|
del tmp
|
||||||
|
|
||||||
# Create mesh - surface:
|
x = x[x_min:x_max+1]
|
||||||
import MeshTools.Triangulation as Triangulation
|
y = y[y_min:y_max+1]
|
||||||
import Mesh
|
datavals = datavals[y_min:y_max+1, x_min:x_max+1]
|
||||||
stepsize = 75
|
|
||||||
stepx = math.ceil(nx / stepsize)
|
|
||||||
stepy = math.ceil(ny / stepsize)
|
|
||||||
|
|
||||||
mesh = Mesh.Mesh()
|
# Create mesh - surface:
|
||||||
for indx in range(stepx):
|
import MeshTools.Triangulation as Triangulation
|
||||||
inix = indx * stepsize - 1
|
import Mesh
|
||||||
finx = min([stepsize * (indx + 1), len(x)-1])
|
stepsize = 75
|
||||||
for indy in range(stepy):
|
stepx = math.ceil(nx / stepsize)
|
||||||
iniy = indy * stepsize - 1
|
stepy = math.ceil(ny / stepsize)
|
||||||
finy = min([stepsize * (indy + 1), len(y) - 1])
|
|
||||||
pts = []
|
mesh = Mesh.Mesh()
|
||||||
for i in range(inix, finx):
|
for indx in range(stepx):
|
||||||
for j in range(iniy, finy):
|
inix = indx * stepsize - 1
|
||||||
if datavals[j][i] != nodata_value:
|
finx = min([stepsize * (indx + 1), len(x)-1])
|
||||||
if obj.CuttingBoundary:
|
for indy in range(stepy):
|
||||||
if obj.CuttingBoundary.Shape.isInside(FreeCAD.Vector(x[i], y[j], 0), 0, True):
|
iniy = indy * stepsize - 1
|
||||||
|
finy = min([stepsize * (indy + 1), len(y) - 1])
|
||||||
|
pts = []
|
||||||
|
for i in range(inix, finx):
|
||||||
|
for j in range(iniy, finy):
|
||||||
|
if datavals[j][i] != nodata_value:
|
||||||
|
if obj.CuttingBoundary:
|
||||||
|
if obj.CuttingBoundary.Shape.isInside(FreeCAD.Vector(x[i], y[j], 0), 0, True):
|
||||||
|
pts.append([x[i], y[j], datavals[j][i]])
|
||||||
|
else:
|
||||||
pts.append([x[i], y[j], datavals[j][i]])
|
pts.append([x[i], y[j], datavals[j][i]])
|
||||||
else:
|
if len(pts) > 3:
|
||||||
pts.append([x[i], y[j], datavals[j][i]])
|
try:
|
||||||
if len(pts) > 3:
|
triangulated = Triangulation.Triangulate(pts)
|
||||||
try:
|
mesh.addMesh(triangulated)
|
||||||
triangulated = Triangulation.Triangulate(pts)
|
except TypeError:
|
||||||
mesh.addMesh(triangulated)
|
print(f"Error al procesar {len(pts)} puntos: {str(e)}")
|
||||||
except TypeError:
|
|
||||||
print(f"Error al procesar {len(pts)} puntos: {str(e)}")
|
mesh.removeDuplicatedPoints()
|
||||||
|
mesh.removeFoldsOnSurface()
|
||||||
|
obj.InitialMesh = mesh.copy()
|
||||||
|
Mesh.show(mesh)
|
||||||
|
elif suffix in ['.xyz']:
|
||||||
|
data = open_xyz_mmap(obj.DEM)
|
||||||
|
|
||||||
|
|
||||||
mesh.removeDuplicatedPoints()
|
|
||||||
mesh.removeFoldsOnSurface()
|
|
||||||
obj.InitialMesh = mesh.copy()
|
|
||||||
Mesh.show(mesh)
|
|
||||||
|
|
||||||
if prop == "PointsGroup" or prop == "CuttingBoundary":
|
if prop == "PointsGroup" or prop == "CuttingBoundary":
|
||||||
if obj.PointsGroup and obj.CuttingBoundary:
|
if obj.PointsGroup and obj.CuttingBoundary:
|
||||||
|
|||||||
@@ -54,30 +54,6 @@ class CommandPVPlantSite:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
'''class CommandPVPlantGeoreferencing:
|
|
||||||
@staticmethod
|
|
||||||
def GetResources():
|
|
||||||
return {'Pixmap': str(os.path.join(DirIcons, "Location.svg")),
|
|
||||||
'Accel': "G, R",
|
|
||||||
'MenuText': QT_TRANSLATE_NOOP("Georeferencing","Georeferencing"),
|
|
||||||
'ToolTip': QT_TRANSLATE_NOOP("Georeferencing","Referenciar el lugar")}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def IsActive():
|
|
||||||
if FreeCAD.ActiveDocument:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def Activated():
|
|
||||||
import PVPlantGeoreferencing
|
|
||||||
taskd = PVPlantGeoreferencing.MapWindow()
|
|
||||||
#taskd.setParent(FreeCADGui.getMainWindow())
|
|
||||||
#taskd.setWindowFlags(QtCore.Qt.Window)
|
|
||||||
taskd.show()#exec_()'''
|
|
||||||
|
|
||||||
|
|
||||||
class CommandProjectSetup:
|
class CommandProjectSetup:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def GetResources():
|
def GetResources():
|
||||||
@@ -651,6 +627,31 @@ if FreeCAD.GuiUp:
|
|||||||
import Project.GenerateExternalDocument as GED
|
import Project.GenerateExternalDocument as GED
|
||||||
FreeCADGui.addCommand('newExternalDocument', GED.CommandGenerateExternalDocument())
|
FreeCADGui.addCommand('newExternalDocument', GED.CommandGenerateExternalDocument())
|
||||||
|
|
||||||
|
from Mechanical.Frame import PVPlantFrame
|
||||||
|
class CommandRackGroup:
|
||||||
|
|
||||||
|
def GetCommands(self):
|
||||||
|
return tuple(['PVPlantFixedRack',
|
||||||
|
'PVPlantTrackerSetup',
|
||||||
|
'PVPlantTracker'
|
||||||
|
])
|
||||||
|
|
||||||
|
def GetResources(self):
|
||||||
|
return {'MenuText': QT_TRANSLATE_NOOP("", 'Rack Types'),
|
||||||
|
'ToolTip': QT_TRANSLATE_NOOP("", 'Rack Types')
|
||||||
|
}
|
||||||
|
|
||||||
|
def IsActive(self):
|
||||||
|
return not FreeCAD.ActiveDocument is None
|
||||||
|
|
||||||
|
FreeCADGui.addCommand('PVPlantFixedRack', PVPlantFrame.CommandFixedRack())
|
||||||
|
FreeCADGui.addCommand('PVPlantTrackerSetup', PVPlantFrame.CommandTrackerSetup())
|
||||||
|
FreeCADGui.addCommand('PVPlantTracker', PVPlantFrame.CommandTracker())
|
||||||
|
FreeCADGui.addCommand('RackType', CommandRackGroup())
|
||||||
|
|
||||||
|
from Civil.Fence import PVPlantFence
|
||||||
|
FreeCADGui.addCommand('PVPlantFenceGroup', PVPlantFence.CommandFenceGroup())
|
||||||
|
|
||||||
projectlist = [ # "Reload",
|
projectlist = [ # "Reload",
|
||||||
"PVPlantSite",
|
"PVPlantSite",
|
||||||
"ProjectSetup",
|
"ProjectSetup",
|
||||||
@@ -678,14 +679,12 @@ projectlist = [ # "Reload",
|
|||||||
'newExternalDocument',
|
'newExternalDocument',
|
||||||
]
|
]
|
||||||
|
|
||||||
pv_list = [
|
pv_mechanical = [
|
||||||
# "RackType",
|
"RackType",
|
||||||
# "PVPlantRackCheck",
|
|
||||||
# "Separator",
|
|
||||||
"PVPlantPlacement",
|
"PVPlantPlacement",
|
||||||
"PVPlantAdjustToTerrain",
|
"PVPlantAdjustToTerrain",
|
||||||
"PVPlantConvertTo",
|
"PVPlantConvertTo",
|
||||||
# "PVArea"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
objectlist = ['PVPlantTree',]
|
objectlist = ['PVPlantTree',
|
||||||
|
'PVPlantFenceGroup',]
|
||||||
@@ -26,6 +26,9 @@ import PVPlantSite
|
|||||||
import Utils.PVPlantUtils as utils
|
import Utils.PVPlantUtils as utils
|
||||||
import MeshPart as mp
|
import MeshPart as mp
|
||||||
|
|
||||||
|
import pivy
|
||||||
|
from pivy import coin
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
import FreeCADGui
|
import FreeCADGui
|
||||||
from DraftTools import translate
|
from DraftTools import translate
|
||||||
@@ -69,6 +72,7 @@ class _Area:
|
|||||||
''' Initialize the Area object '''
|
''' Initialize the Area object '''
|
||||||
self.Type = None
|
self.Type = None
|
||||||
self.obj = None
|
self.obj = None
|
||||||
|
self.setProperties(obj)
|
||||||
|
|
||||||
def setProperties(self, obj):
|
def setProperties(self, obj):
|
||||||
pl = obj.PropertiesList
|
pl = obj.PropertiesList
|
||||||
@@ -101,18 +105,18 @@ class _Area:
|
|||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def execute(self, obj):
|
||||||
|
''' Execute the area object '''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _ViewProviderArea:
|
class _ViewProviderArea:
|
||||||
def __init__(self, vobj):
|
def __init__(self, vobj):
|
||||||
self.Object = vobj.Object
|
|
||||||
vobj.Proxy = self
|
vobj.Proxy = self
|
||||||
|
|
||||||
def attach(self, vobj):
|
def attach(self, vobj):
|
||||||
'''
|
''' Create Object visuals in 3D view. '''
|
||||||
Create Object visuals in 3D view.
|
self.ViewObject = vobj
|
||||||
'''
|
|
||||||
self.Object = vobj.Object
|
|
||||||
return
|
|
||||||
|
|
||||||
def getIcon(self):
|
def getIcon(self):
|
||||||
'''
|
'''
|
||||||
@@ -120,6 +124,7 @@ class _ViewProviderArea:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
return str(os.path.join(DirIcons, "area.svg"))
|
return str(os.path.join(DirIcons, "area.svg"))
|
||||||
|
|
||||||
'''
|
'''
|
||||||
def claimChildren(self):
|
def claimChildren(self):
|
||||||
"""
|
"""
|
||||||
@@ -159,17 +164,10 @@ class _ViewProviderArea:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
"""
|
|
||||||
Save variables to file.
|
|
||||||
"""
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
"""
|
pass
|
||||||
Get variables from file.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
''' Frame Area '''
|
''' Frame Area '''
|
||||||
|
|
||||||
@@ -311,17 +309,14 @@ class ViewProviderFrameArea(_ViewProviderArea):
|
|||||||
|
|
||||||
|
|
||||||
''' offsets '''
|
''' offsets '''
|
||||||
|
|
||||||
|
|
||||||
def makeOffsetArea(base = None, val=None):
|
def makeOffsetArea(base = None, val=None):
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "OffsetArea")
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "OffsetArea")
|
||||||
OffsetArea(obj)
|
OffsetArea(obj)
|
||||||
obj.Base = base
|
obj.Base = base
|
||||||
ViewProviderOffsetArea(obj.ViewObject)
|
ViewProviderOffsetArea(obj.ViewObject)
|
||||||
if val:
|
if val:
|
||||||
obj.Distance = val
|
obj.OffsetDistance = val
|
||||||
|
|
||||||
offsets = None
|
|
||||||
try:
|
try:
|
||||||
offsetsgroup = FreeCAD.ActiveDocument.Offsets
|
offsetsgroup = FreeCAD.ActiveDocument.Offsets
|
||||||
except:
|
except:
|
||||||
@@ -334,11 +329,13 @@ def makeOffsetArea(base = None, val=None):
|
|||||||
|
|
||||||
class OffsetArea(_Area):
|
class OffsetArea(_Area):
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
_Area.__init__(self, obj)
|
'''_Area.__init__(self, obj)
|
||||||
self.setProperties(obj)
|
self.setProperties(obj)'''
|
||||||
|
super().__init__(obj) # Llama al constructor de _Area
|
||||||
|
|
||||||
def setProperties(self, obj):
|
def setProperties(self, obj):
|
||||||
_Area.setProperties(self, obj)
|
super().setProperties(obj) # Propiedades de la clase base
|
||||||
|
|
||||||
pl = obj.PropertiesList
|
pl = obj.PropertiesList
|
||||||
if not ("OffsetDistance" in pl):
|
if not ("OffsetDistance" in pl):
|
||||||
obj.addProperty("App::PropertyDistance",
|
obj.addProperty("App::PropertyDistance",
|
||||||
@@ -354,24 +351,28 @@ class OffsetArea(_Area):
|
|||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
|
|
||||||
def execute(self, obj):
|
def execute(self, obj):
|
||||||
import Utils.PVPlantUtils as utils
|
# Comprobar dependencias críticas
|
||||||
|
if not hasattr(obj, "Base") or not obj.Base or not obj.Base.Shape:
|
||||||
|
return
|
||||||
|
if not hasattr(PVPlantSite, "get") or not PVPlantSite.get().Terrain:
|
||||||
|
return
|
||||||
|
|
||||||
base = obj.Base.Shape
|
base = obj.Base.Shape
|
||||||
land = PVPlantSite.get().Terrain.Mesh
|
land = PVPlantSite.get().Terrain.Mesh
|
||||||
vec = FreeCAD.Vector(0, 0, 1)
|
vec = FreeCAD.Vector(0, 0, 1)
|
||||||
|
|
||||||
wire = utils.getProjected(base, vec)
|
wire = utils.getProjected(base, vec)
|
||||||
wire = wire.makeOffset2D(obj.OffsetDistance.Value, 2, False, False, True)
|
wire = wire.makeOffset2D(obj.OffsetDistance.Value, 2, False, False, True)
|
||||||
tmp = mp.projectShapeOnMesh(wire, land, vec)
|
sections = mp.projectShapeOnMesh(wire, land, vec)
|
||||||
|
print(" javi ", sections)
|
||||||
pts = []
|
pts = []
|
||||||
for section in tmp:
|
for section in sections:
|
||||||
pts.extend(section)
|
pts.extend(section)
|
||||||
obj.Shape = Part.makePolygon(pts)
|
# Crear forma solo si hay resultados
|
||||||
|
if len(pts)>0:
|
||||||
def __getstate__(self):
|
obj.Shape = Part.makePolygon(pts)
|
||||||
return None
|
else:
|
||||||
|
obj.Shape = Part.Shape() # Forma vacía si falla
|
||||||
def __setstate__(self, state):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderOffsetArea(_ViewProviderArea):
|
class ViewProviderOffsetArea(_ViewProviderArea):
|
||||||
@@ -382,14 +383,12 @@ class ViewProviderOffsetArea(_ViewProviderArea):
|
|||||||
def claimChildren(self):
|
def claimChildren(self):
|
||||||
""" Provides object grouping """
|
""" Provides object grouping """
|
||||||
children = []
|
children = []
|
||||||
if self.Object.Base:
|
if self.ViewObject and self.ViewObject.Object.Base:
|
||||||
children.append(self.Object.Base)
|
children.append(self.ViewObject.Object.Base)
|
||||||
return children
|
return children
|
||||||
|
|
||||||
|
|
||||||
''' Forbidden Area: '''
|
''' Forbidden Area: '''
|
||||||
|
|
||||||
|
|
||||||
def makeProhibitedArea(base = None):
|
def makeProhibitedArea(base = None):
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ExclusionArea")
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ExclusionArea")
|
||||||
ProhibitedArea(obj)
|
ProhibitedArea(obj)
|
||||||
@@ -420,29 +419,443 @@ class ProhibitedArea(OffsetArea):
|
|||||||
"""Method run when the document is restored."""
|
"""Method run when the document is restored."""
|
||||||
self.setProperties(obj)
|
self.setProperties(obj)
|
||||||
|
|
||||||
|
def execute(self, obj):
|
||||||
|
# Comprobar dependencias
|
||||||
|
if not hasattr(obj, "Base") or not obj.Base or not obj.Base.Shape:
|
||||||
|
return
|
||||||
|
if not hasattr(PVPlantSite, "get") or not PVPlantSite.get().Terrain:
|
||||||
|
return
|
||||||
|
|
||||||
|
base = obj.Base.Shape
|
||||||
|
land = PVPlantSite.get().Terrain.Mesh
|
||||||
|
vec = FreeCAD.Vector(0, 0, 1)
|
||||||
|
|
||||||
|
# 1. Crear wire original
|
||||||
|
original_wire = utils.getProjected(base, vec)
|
||||||
|
sections_original = mp.projectShapeOnMesh(original_wire, land, vec)
|
||||||
|
|
||||||
|
# 2. Crear wire offset
|
||||||
|
offset_wire = original_wire.makeOffset2D(obj.OffsetDistance.Value, 2, False, False, True)
|
||||||
|
sections_offset = mp.projectShapeOnMesh(offset_wire, land, vec)
|
||||||
|
|
||||||
|
# Crear formas compuestas
|
||||||
|
def make_polygon(sections):
|
||||||
|
if not sections:
|
||||||
|
return Part.Shape()
|
||||||
|
pts = []
|
||||||
|
for section in sections:
|
||||||
|
pts.extend(section)
|
||||||
|
return Part.makePolygon(pts)
|
||||||
|
|
||||||
|
compounds = []
|
||||||
|
if sections_original:
|
||||||
|
compounds.append(make_polygon(sections_original))
|
||||||
|
if sections_offset:
|
||||||
|
compounds.append(make_polygon(sections_offset))
|
||||||
|
|
||||||
|
if compounds:
|
||||||
|
obj.Shape = Part.makeCompound(compounds)
|
||||||
|
else:
|
||||||
|
obj.Shape = Part.Shape()
|
||||||
|
|
||||||
|
# Actualizar colores en la vista
|
||||||
|
"""if FreeCAD.GuiUp and obj.ViewObject:
|
||||||
|
obj.ViewObject.Proxy.updateVisual()"""
|
||||||
|
|
||||||
|
|
||||||
|
class ViewProviderForbiddenArea_old:
|
||||||
|
def __init__(self, vobj):
|
||||||
|
vobj.Proxy = self
|
||||||
|
self.setProperties(vobj)
|
||||||
|
|
||||||
|
def setProperties(self, vobj):
|
||||||
|
# Propiedades de color
|
||||||
|
if not hasattr(vobj, "OriginalColor"):
|
||||||
|
vobj.addProperty("App::PropertyColor",
|
||||||
|
"OriginalColor",
|
||||||
|
"ObjectStyle",
|
||||||
|
"Color for original wire")
|
||||||
|
vobj.OriginalColor = (1.0, 0.0, 0.0) # Rojo
|
||||||
|
|
||||||
|
if not hasattr(vobj, "OffsetColor"):
|
||||||
|
vobj.addProperty("App::PropertyColor",
|
||||||
|
"OffsetColor",
|
||||||
|
"ObjectStyle",
|
||||||
|
"Color for offset wire")
|
||||||
|
vobj.OffsetColor = (1.0, 0.0, 0.0) # Rojo
|
||||||
|
|
||||||
|
# Propiedades de grosor
|
||||||
|
if not hasattr(vobj, "OriginalWidth"):
|
||||||
|
vobj.addProperty("App::PropertyFloat",
|
||||||
|
"OriginalWidth",
|
||||||
|
"ObjectStyle",
|
||||||
|
"Line width for original wire")
|
||||||
|
vobj.OriginalWidth = 4.0
|
||||||
|
|
||||||
|
if not hasattr(vobj, "OffsetWidth"):
|
||||||
|
vobj.addProperty("App::PropertyFloat",
|
||||||
|
"OffsetWidth",
|
||||||
|
"ObjectStyle",
|
||||||
|
"Line width for offset wire")
|
||||||
|
vobj.OffsetWidth = 4.0
|
||||||
|
|
||||||
|
# Deshabilitar el color por defecto
|
||||||
|
vobj.setPropertyStatus("LineColor", "Hidden")
|
||||||
|
vobj.setPropertyStatus("PointColor", "Hidden")
|
||||||
|
vobj.setPropertyStatus("ShapeAppearance", "Hidden")
|
||||||
|
|
||||||
|
def attach(self, vobj):
|
||||||
|
self.ViewObject = vobj
|
||||||
|
self.Object = vobj.Object
|
||||||
|
|
||||||
|
# Crear la estructura de escena Coin3D
|
||||||
|
self.root = coin.SoGroup()
|
||||||
|
|
||||||
|
# Switch para habilitar/deshabilitar la selección
|
||||||
|
self.switch = coin.SoSwitch()
|
||||||
|
self.switch.whichChild = coin.SO_SWITCH_ALL
|
||||||
|
|
||||||
|
# Separador para el wire original
|
||||||
|
self.original_sep = coin.SoSeparator()
|
||||||
|
self.original_color = coin.SoBaseColor()
|
||||||
|
self.original_coords = coin.SoCoordinate3()
|
||||||
|
self.original_line_set = coin.SoLineSet()
|
||||||
|
self.original_draw_style = coin.SoDrawStyle()
|
||||||
|
|
||||||
|
# Separador para el wire offset
|
||||||
|
self.offset_sep = coin.SoSeparator()
|
||||||
|
self.offset_color = coin.SoBaseColor()
|
||||||
|
self.offset_coords = coin.SoCoordinate3()
|
||||||
|
self.offset_line_set = coin.SoLineSet()
|
||||||
|
self.offset_draw_style = coin.SoDrawStyle()
|
||||||
|
|
||||||
|
# Construir la jerarquía de escena
|
||||||
|
self.original_sep.addChild(self.original_color)
|
||||||
|
self.original_sep.addChild(self.original_draw_style)
|
||||||
|
self.original_sep.addChild(self.original_coords)
|
||||||
|
self.original_sep.addChild(self.original_line_set)
|
||||||
|
|
||||||
|
self.offset_sep.addChild(self.offset_color)
|
||||||
|
self.offset_sep.addChild(self.offset_draw_style)
|
||||||
|
self.offset_sep.addChild(self.offset_coords)
|
||||||
|
self.offset_sep.addChild(self.offset_line_set)
|
||||||
|
|
||||||
|
self.switch.addChild(self.original_sep)
|
||||||
|
self.switch.addChild(self.offset_sep)
|
||||||
|
self.root.addChild(self.switch)
|
||||||
|
|
||||||
|
vobj.addDisplayMode(self.root, "Wireframe")
|
||||||
|
|
||||||
|
# Inicializar estilos de dibujo
|
||||||
|
self.original_draw_style.style = coin.SoDrawStyle.LINES
|
||||||
|
self.offset_draw_style.style = coin.SoDrawStyle.LINES
|
||||||
|
|
||||||
|
# Actualizar visualización inicial
|
||||||
|
if hasattr(self.Object, 'Shape'):
|
||||||
|
self.updateData(self.Object, "Shape")
|
||||||
|
self.updateVisual()
|
||||||
|
|
||||||
|
def updateData(self, obj, prop):
|
||||||
|
if prop == "Shape" and obj.Shape and not obj.Shape.isNull():
|
||||||
|
self.updateGeometry()
|
||||||
|
|
||||||
|
def updateGeometry(self):
|
||||||
|
"""Actualiza la geometría en la escena 3D"""
|
||||||
|
if not hasattr(self, 'Object') or not self.Object.Shape or self.Object.Shape.isNull():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Limpiar coordenadas existentes
|
||||||
|
self.original_coords.point.deleteValues(0)
|
||||||
|
self.offset_coords.point.deleteValues(0)
|
||||||
|
|
||||||
|
# Obtener los sub-shapes
|
||||||
|
subshapes = []
|
||||||
|
if hasattr(self.Object.Shape, 'SubShapes') and self.Object.Shape.SubShapes:
|
||||||
|
subshapes = self.Object.Shape.SubShapes
|
||||||
|
elif hasattr(self.Object.Shape, 'ChildShapes') and self.Object.Shape.ChildShapes:
|
||||||
|
subshapes = self.Object.Shape.ChildShapes
|
||||||
|
|
||||||
|
# Procesar wire original (primer sub-shape)
|
||||||
|
if len(subshapes) > 0:
|
||||||
|
self.processShape(subshapes[0], self.original_coords, self.original_line_set)
|
||||||
|
|
||||||
|
# Procesar wire offset (segundo sub-shape)
|
||||||
|
if len(subshapes) > 1:
|
||||||
|
self.processShape(subshapes[1], self.offset_coords, self.offset_line_set)
|
||||||
|
|
||||||
|
# Actualizar colores y grosores
|
||||||
|
self.updateVisual()
|
||||||
|
|
||||||
|
def processShape(self, shape, coords_node, lineset_node):
|
||||||
|
"""Procesa una forma y la añade al nodo de coordenadas"""
|
||||||
|
if not shape or shape.isNull():
|
||||||
|
return
|
||||||
|
|
||||||
|
points = []
|
||||||
|
line_indices = []
|
||||||
|
current_index = 0
|
||||||
|
|
||||||
|
# Obtener todos los edges de la forma
|
||||||
|
edges = []
|
||||||
|
if hasattr(shape, 'Edges'):
|
||||||
|
edges = shape.Edges
|
||||||
|
elif hasattr(shape, 'ChildShapes'):
|
||||||
|
for child in shape.ChildShapes:
|
||||||
|
if hasattr(child, 'Edges'):
|
||||||
|
edges.extend(child.Edges)
|
||||||
|
|
||||||
|
for edge in edges:
|
||||||
|
try:
|
||||||
|
# Discretizar la curva para obtener puntos
|
||||||
|
vertices = edge.discretize(Number=50)
|
||||||
|
|
||||||
|
for i, vertex in enumerate(vertices):
|
||||||
|
points.append([vertex.x, vertex.y, vertex.z])
|
||||||
|
line_indices.append(current_index)
|
||||||
|
current_index += 1
|
||||||
|
|
||||||
|
# Añadir -1 para indicar fin de línea
|
||||||
|
line_indices.append(-1)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing edge: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Configurar coordenadas y líneas
|
||||||
|
if points:
|
||||||
|
coords_node.point.setValues(0, len(points), points)
|
||||||
|
lineset_node.numVertices.deleteValues(0)
|
||||||
|
lineset_node.numVertices.setValues(0, len(line_indices), line_indices)
|
||||||
|
|
||||||
|
def updateVisual(self):
|
||||||
|
"""Actualiza colores y grosores según las propiedades"""
|
||||||
|
if not hasattr(self, 'ViewObject') or not self.ViewObject:
|
||||||
|
return
|
||||||
|
|
||||||
|
vobj = self.ViewObject
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Configurar wire original
|
||||||
|
if hasattr(vobj, "OriginalColor"):
|
||||||
|
original_color = vobj.OriginalColor
|
||||||
|
self.original_color.rgb.setValue(original_color[0], original_color[1], original_color[2])
|
||||||
|
|
||||||
|
if hasattr(vobj, "OriginalWidth"):
|
||||||
|
self.original_draw_style.lineWidth = vobj.OriginalWidth
|
||||||
|
|
||||||
|
# Configurar wire offset
|
||||||
|
if hasattr(vobj, "OffsetColor"):
|
||||||
|
offset_color = vobj.OffsetColor
|
||||||
|
self.offset_color.rgb.setValue(offset_color[0], offset_color[1], offset_color[2])
|
||||||
|
|
||||||
|
if hasattr(vobj, "OffsetWidth"):
|
||||||
|
self.offset_draw_style.lineWidth = vobj.OffsetWidth
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error updating visual: {e}")
|
||||||
|
|
||||||
|
def onChanged(self, vobj, prop):
|
||||||
|
"""Maneja cambios en propiedades"""
|
||||||
|
if prop in ["OriginalColor", "OffsetColor", "OriginalWidth", "OffsetWidth"]:
|
||||||
|
self.updateVisual()
|
||||||
|
|
||||||
|
def getDisplayModes(self, obj):
|
||||||
|
return ["Wireframe"]
|
||||||
|
|
||||||
|
def getDefaultDisplayMode(self):
|
||||||
|
return "Wireframe"
|
||||||
|
|
||||||
|
def setDisplayMode(self, mode):
|
||||||
|
return mode
|
||||||
|
|
||||||
|
def claimChildren(self):
|
||||||
|
"""Proporciona agrupamiento de objetos"""
|
||||||
|
children = []
|
||||||
|
if hasattr(self, 'Object') and self.Object and hasattr(self.Object, "Base"):
|
||||||
|
children.append(self.Object.Base)
|
||||||
|
return children
|
||||||
|
|
||||||
|
def getIcon(self):
|
||||||
|
'''Return object treeview icon'''
|
||||||
|
return str(os.path.join(DirIcons, "area_forbidden.svg"))
|
||||||
|
|
||||||
|
def onDocumentRestored(self, vobj):
|
||||||
|
"""Método ejecutado cuando el documento es restaurado"""
|
||||||
|
self.ViewObject = vobj
|
||||||
|
self.Object = vobj.Object
|
||||||
|
self.setProperties(vobj)
|
||||||
|
self.attach(vobj)
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
pass
|
return None
|
||||||
|
|
||||||
|
|
||||||
class ViewProviderForbiddenArea(_ViewProviderArea):
|
class ViewProviderForbiddenArea:
|
||||||
|
def __init__(self, vobj):
|
||||||
|
vobj.Proxy = self
|
||||||
|
self.ViewObject = vobj
|
||||||
|
|
||||||
|
# Inicializar propiedades PRIMERO
|
||||||
|
self.setProperties(vobj)
|
||||||
|
|
||||||
|
# Configurar colores iniciales
|
||||||
|
self.updateColors(vobj)
|
||||||
|
|
||||||
|
def setProperties(self, vobj):
|
||||||
|
if not hasattr(vobj, "OriginalColor"):
|
||||||
|
vobj.addProperty("App::PropertyColor",
|
||||||
|
"OriginalColor",
|
||||||
|
"Display",
|
||||||
|
"Color for original wire")
|
||||||
|
vobj.OriginalColor = (1.0, 0.0, 0.0) # Rojo
|
||||||
|
|
||||||
|
if not hasattr(vobj, "OffsetColor"):
|
||||||
|
vobj.addProperty("App::PropertyColor",
|
||||||
|
"OffsetColor",
|
||||||
|
"Display",
|
||||||
|
"Color for offset wire")
|
||||||
|
vobj.OffsetColor = (1.0, 0.5, 0.0) # Naranja
|
||||||
|
|
||||||
|
def updateColors(self, vobj):
|
||||||
|
"""Actualiza los colores desde las propiedades"""
|
||||||
|
try:
|
||||||
|
if hasattr(vobj, "OriginalColor"):
|
||||||
|
self.original_color.rgb.setValue(*vobj.OriginalColor)
|
||||||
|
else:
|
||||||
|
self.original_color.rgb.setValue(1.0, 0.0, 0.0)
|
||||||
|
|
||||||
|
if hasattr(vobj, "OffsetColor"):
|
||||||
|
self.offset_color.rgb.setValue(*vobj.OffsetColor)
|
||||||
|
else:
|
||||||
|
self.offset_color.rgb.setValue(1.0, 0.5, 0.0)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error en updateColors: {e}")
|
||||||
|
|
||||||
|
def onDocumentRestored(self, vobj):
|
||||||
|
self.setProperties(vobj)
|
||||||
|
# No llamar a __init__ de nuevo, solo actualizar propiedades
|
||||||
|
self.updateColors(vobj)
|
||||||
|
|
||||||
def getIcon(self):
|
def getIcon(self):
|
||||||
''' Return object treeview icon '''
|
|
||||||
return str(os.path.join(DirIcons, "area_forbidden.svg"))
|
return str(os.path.join(DirIcons, "area_forbidden.svg"))
|
||||||
|
|
||||||
|
def attach(self, vobj):
|
||||||
|
self.ViewObject = vobj
|
||||||
|
|
||||||
|
# Inicializar nodos Coin3D
|
||||||
|
self.root = coin.SoGroup()
|
||||||
|
self.original_coords = coin.SoCoordinate3()
|
||||||
|
self.offset_coords = coin.SoCoordinate3()
|
||||||
|
self.original_color = coin.SoBaseColor()
|
||||||
|
self.offset_color = coin.SoBaseColor()
|
||||||
|
self.original_lineset = coin.SoLineSet()
|
||||||
|
self.offset_lineset = coin.SoLineSet()
|
||||||
|
|
||||||
|
# Añadir un nodo de dibujo para establecer el estilo de línea
|
||||||
|
self.draw_style = coin.SoDrawStyle()
|
||||||
|
self.draw_style.style = coin.SoDrawStyle.LINES
|
||||||
|
self.draw_style.lineWidth = 3.0
|
||||||
|
|
||||||
|
# Construir la escena
|
||||||
|
self.root.addChild(self.draw_style)
|
||||||
|
|
||||||
|
# Grupo para el polígono original
|
||||||
|
original_group = coin.SoGroup()
|
||||||
|
original_group.addChild(self.original_color)
|
||||||
|
original_group.addChild(self.original_coords)
|
||||||
|
original_group.addChild(self.original_lineset)
|
||||||
|
|
||||||
|
# Grupo para el polígono offset
|
||||||
|
offset_group = coin.SoGroup()
|
||||||
|
offset_group.addChild(self.offset_color)
|
||||||
|
offset_group.addChild(self.offset_coords)
|
||||||
|
offset_group.addChild(self.offset_lineset)
|
||||||
|
|
||||||
|
self.root.addChild(original_group)
|
||||||
|
self.root.addChild(offset_group)
|
||||||
|
|
||||||
|
vobj.addDisplayMode(self.root, "Standard")
|
||||||
|
# Asegurar que la visibilidad esté activada
|
||||||
|
vobj.Visibility = True
|
||||||
|
|
||||||
|
def updateData(self, obj, prop):
|
||||||
|
if prop == "Shape":
|
||||||
|
self.updateVisual(obj)
|
||||||
|
|
||||||
|
def updateVisual(self, obj):
|
||||||
|
"""Actualiza la representación visual basada en la forma del objeto"""
|
||||||
|
if not hasattr(obj, 'Shape') or not obj.Shape or obj.Shape.isNull():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Obtener todos los bordes de la forma compuesta
|
||||||
|
all_edges = obj.Shape.Edges
|
||||||
|
|
||||||
|
# Separar bordes por polígono (asumimos que el primer polígono es el original)
|
||||||
|
# Esto es una simplificación - podrías necesitar una lógica más sofisticada
|
||||||
|
if len(all_edges) >= 2:
|
||||||
|
# Polígono original - primer conjunto de bordes
|
||||||
|
original_edges = [all_edges[0]]
|
||||||
|
original_points = []
|
||||||
|
for edge in original_edges:
|
||||||
|
for vertex in edge.Vertexes:
|
||||||
|
original_points.append((vertex.Point.x, vertex.Point.y, vertex.Point.z))
|
||||||
|
|
||||||
|
# Polígono offset - segundo conjunto de bordes
|
||||||
|
offset_edges = [all_edges[1]]
|
||||||
|
offset_points = []
|
||||||
|
for edge in offset_edges:
|
||||||
|
for vertex in edge.Vertexes:
|
||||||
|
offset_points.append((vertex.Point.x, vertex.Point.y, vertex.Point.z))
|
||||||
|
|
||||||
|
# Asignar puntos a los nodos Coordinate3
|
||||||
|
if original_points:
|
||||||
|
self.original_coords.point.setValues(0, len(original_points), original_points)
|
||||||
|
self.original_lineset.numVertices.setValue(len(original_points))
|
||||||
|
|
||||||
|
if offset_points:
|
||||||
|
self.offset_coords.point.setValues(0, len(offset_points), offset_points)
|
||||||
|
self.offset_lineset.numVertices.setValue(len(offset_points))
|
||||||
|
|
||||||
|
# Actualizar colores
|
||||||
|
if hasattr(obj, 'ViewObject') and obj.ViewObject:
|
||||||
|
self.updateColors(obj.ViewObject)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error en updateVisual: {e}")
|
||||||
|
|
||||||
|
def onChanged(self, vobj, prop):
|
||||||
|
if prop in ["OriginalColor", "OffsetColor"]:
|
||||||
|
self.updateColors(vobj)
|
||||||
|
elif prop == "Visibility" and vobj.Visibility:
|
||||||
|
# Cuando la visibilidad cambia a True, actualizar visual
|
||||||
|
self.updateVisual(vobj.Object)
|
||||||
|
|
||||||
|
def getDisplayModes(self, obj):
|
||||||
|
return ["Standard"]
|
||||||
|
|
||||||
|
def getDefaultDisplayMode(self):
|
||||||
|
return "Standard"
|
||||||
|
|
||||||
|
def setDisplayMode(self, mode):
|
||||||
|
return mode
|
||||||
|
|
||||||
def claimChildren(self):
|
def claimChildren(self):
|
||||||
""" Provides object grouping """
|
|
||||||
children = []
|
children = []
|
||||||
if self.Object.Base:
|
if hasattr(self, 'ViewObject') and self.ViewObject and hasattr(self.ViewObject.Object, 'Base'):
|
||||||
children.append(self.Object.Base)
|
children.append(self.ViewObject.Object.Base)
|
||||||
return children
|
return children
|
||||||
|
|
||||||
|
def dumps(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def loads(self, state):
|
||||||
|
return None
|
||||||
|
|
||||||
''' PV Area: '''
|
''' PV Area: '''
|
||||||
|
|
||||||
|
|
||||||
def makePVSubplant():
|
def makePVSubplant():
|
||||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PVSubplant")
|
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PVSubplant")
|
||||||
PVSubplant(obj)
|
PVSubplant(obj)
|
||||||
|
|||||||
@@ -39,6 +39,16 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>9</number>
|
<number>9</number>
|
||||||
</property>
|
</property>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Maximum west-east slope:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QWidget" name="widget_2" native="true"/>
|
||||||
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -46,17 +56,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="0" column="2">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Frame coloring:</string>
|
<string>South facing</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Orientation::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
@@ -66,71 +79,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Maximum west-east slope:</string>
|
<string>Frame coloring:</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="editWETL">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> º</string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>90.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>8.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QDoubleSpinBox" name="editSFTL">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buttonSymbols">
|
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> º</string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>90.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<double>2.800000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QWidget" name="widget_2" native="true"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>South facing</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QDoubleSpinBox" name="editNFTL">
|
<widget class="QDoubleSpinBox" name="editNFTL">
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="buttonSymbols">
|
<property name="buttonSymbols">
|
||||||
<enum>QAbstractSpinBox::NoButtons</enum>
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> º</string>
|
<string> º</string>
|
||||||
@@ -149,7 +111,45 @@
|
|||||||
<string>North Facing</string>
|
<string>North Facing</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QDoubleSpinBox" name="editSFTL">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> º</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>90.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>2.800000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="editWETL">
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buttonSymbols">
|
||||||
|
<enum>QAbstractSpinBox::ButtonSymbols::NoButtons</enum>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> º</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>90.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>8.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
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);
|
MyApp.onMapMove(e.latlng.lat, e.latlng.lng);
|
||||||
|
|
||||||
const bounds = map.getBounds();
|
const bounds = map.getBounds();
|
||||||
MyApp.onMapZoom(bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast());
|
MyApp.onMapZoom(bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast(), map.getZoom());
|
||||||
});
|
});
|
||||||
|
|
||||||
var DrawShapes;
|
var DrawShapes;
|
||||||
|
|||||||
@@ -224,14 +224,20 @@ def getProjected(shape, direction=FreeCAD.Vector(0, 0, 1)): # Based on Draft / s
|
|||||||
return ow
|
return ow
|
||||||
|
|
||||||
|
|
||||||
def findObjects(classtype):
|
'''def findObjects(classtype):
|
||||||
objects = FreeCAD.ActiveDocument.Objects
|
objects = FreeCAD.ActiveDocument.Objects
|
||||||
objlist = list()
|
objlist = list()
|
||||||
for object in objects:
|
for object in objects:
|
||||||
if hasattr(object, "Proxy"):
|
if hasattr(object, "Proxy"):
|
||||||
if object.Proxy.Type == classtype:
|
if object.Proxy.Type == classtype:
|
||||||
objlist.append(object)
|
objlist.append(object)
|
||||||
return objlist
|
return objlist'''
|
||||||
|
|
||||||
|
def findObjects(classtype):
|
||||||
|
return [obj for obj in FreeCAD.ActiveDocument.Objects
|
||||||
|
if hasattr(obj, "Proxy")
|
||||||
|
and hasattr(obj.Proxy, "Type")
|
||||||
|
and obj.Proxy.Type == classtype]
|
||||||
|
|
||||||
def getClosePoints(sh1, angle):
|
def getClosePoints(sh1, angle):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
# Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/
|
# Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/
|
||||||
|
|
||||||
import math
|
import math
|
||||||
# from PIL import Image
|
from PIL import Image
|
||||||
import os
|
import os
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import urllib
|
|||||||
# alternativa a PIL: Image
|
# alternativa a PIL: Image
|
||||||
# CV2
|
# CV2
|
||||||
|
|
||||||
class GoogleMapDownloader:
|
class GoogleMapDownloader1:
|
||||||
"""
|
"""
|
||||||
A class which generates high resolution google maps images given
|
A class which generates high resolution google maps images given
|
||||||
a longitude, latitude and zoom level
|
a longitude, latitude and zoom level
|
||||||
|
|||||||
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,17 +2,18 @@
|
|||||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||||
<name>PVPlant</name>
|
<name>PVPlant</name>
|
||||||
<description>FreeCAD Fotovoltaic Power Plant Toolkit</description>
|
<description>FreeCAD Fotovoltaic Power Plant Toolkit</description>
|
||||||
<version>2025.02.22</version>
|
<version>2025.11.20</version>
|
||||||
<date>2025.02.22</date>
|
<date>2025.11.20</date>
|
||||||
<maintainer email="javier.branagutierrez@gmail.com">Javier Braña</maintainer>
|
<maintainer email="javier.branagutierrez@gmail.com">Javier Braña</maintainer>
|
||||||
<license file="LICENSE">LGPL-2.1-or-later</license>
|
<license file="LICENSE">LGPL-2.1-or-later</license>
|
||||||
<url type="repository" branch="main">https://homehud.duckdns.org/javier/PVPlant</url>
|
<url type="repository" branch="developed">https://homehud.duckdns.org/javier/PVPlant</url>
|
||||||
<url type="bugtracker">https://homehud.duckdns.org/javier/PVPlant/issues</url>
|
<url type="bugtracker">https://homehud.duckdns.org/javier/PVPlant/issues</url>
|
||||||
|
<url type="readme">https://homehud.duckdns.org/javier/PVPlant/src/branch/developed/README.md</url>
|
||||||
<icon>PVPlant/Resources/Icons/PVPlantWorkbench.svg</icon>
|
<icon>PVPlant/Resources/Icons/PVPlantWorkbench.svg</icon>
|
||||||
|
|
||||||
<content>
|
<content>
|
||||||
<workbench>
|
<workbench>
|
||||||
<classname>RoadWorkbench</classname>
|
<classname>PVPlantWorkbench</classname>
|
||||||
<subdirectory>./</subdirectory>
|
<subdirectory>./</subdirectory>
|
||||||
</workbench>
|
</workbench>
|
||||||
</content>
|
</content>
|
||||||
|
|||||||
10
reload.py
10
reload.py
@@ -24,11 +24,15 @@ class _CommandReload:
|
|||||||
def Activated(self):
|
def Activated(self):
|
||||||
import PVPlantPlacement, \
|
import PVPlantPlacement, \
|
||||||
PVPlantGeoreferencing, PVPlantImportGrid, PVPlantTerrainAnalisys, \
|
PVPlantGeoreferencing, PVPlantImportGrid, PVPlantTerrainAnalisys, \
|
||||||
PVPlantSite, PVPlantRackChecking, PVPlantFence, PVPlantFencePost, PVPlantFenceGate, \
|
PVPlantSite, PVPlantRackChecking, PVPlantCreateTerrainMesh, \
|
||||||
PVPlantCreateTerrainMesh, \
|
|
||||||
PVPlantFoundation, PVPlantBuilding, PVPlantEarthWorks, PVPlantPad, \
|
PVPlantFoundation, PVPlantBuilding, PVPlantEarthWorks, PVPlantPad, \
|
||||||
PVPlantRoad, PVPlantTerrain, PVPlantStringing, PVPlantManhole, \
|
PVPlantRoad, PVPlantTerrain, PVPlantStringing, PVPlantManhole, \
|
||||||
GraphProfile
|
GraphProfile
|
||||||
|
|
||||||
|
from Civil.Fence import PVPlantFenceGate as PVPlantFenceGate
|
||||||
|
from Civil.Fence import PVPlantFence as PVPlantFence
|
||||||
|
from Civil.Fence import PVPlantFencePost as PVPlantFencePost
|
||||||
|
|
||||||
from Civil import PVPlantTrench
|
from Civil import PVPlantTrench
|
||||||
from Vegetation import PVPlantTreeGenerator
|
from Vegetation import PVPlantTreeGenerator
|
||||||
|
|
||||||
@@ -59,9 +63,11 @@ class _CommandReload:
|
|||||||
importlib.reload(PVPlantSite)
|
importlib.reload(PVPlantSite)
|
||||||
importlib.reload(PVPlantFrame)
|
importlib.reload(PVPlantFrame)
|
||||||
importlib.reload(PVPlantRackChecking)
|
importlib.reload(PVPlantRackChecking)
|
||||||
|
|
||||||
importlib.reload(PVPlantFence)
|
importlib.reload(PVPlantFence)
|
||||||
importlib.reload(PVPlantFenceGate)
|
importlib.reload(PVPlantFenceGate)
|
||||||
importlib.reload(PVPlantFencePost)
|
importlib.reload(PVPlantFencePost)
|
||||||
|
|
||||||
importlib.reload(PVPlantFoundation)
|
importlib.reload(PVPlantFoundation)
|
||||||
importlib.reload(PVPlantCreateTerrainMesh)
|
importlib.reload(PVPlantCreateTerrainMesh)
|
||||||
importlib.reload(PVPlantTreeGenerator)
|
importlib.reload(PVPlantTreeGenerator)
|
||||||
|
|||||||
@@ -9,16 +9,12 @@ setuptools~=68.2.2
|
|||||||
laspy~=2.5.3
|
laspy~=2.5.3
|
||||||
geopy~=2.4.1
|
geopy~=2.4.1
|
||||||
lxml~=4.9.3
|
lxml~=4.9.3
|
||||||
pip~=23.3.2
|
|
||||||
wheel~=0.42.0
|
|
||||||
Brotli~=1.1.0
|
|
||||||
PySocks~=1.7.1
|
|
||||||
typing_extensions~=4.9.0
|
|
||||||
docutils~=0.20.1
|
|
||||||
Pillow~=10.1.0
|
Pillow~=10.1.0
|
||||||
pyproj~=3.7.1
|
pyproj~=3.7.1
|
||||||
simplekml~=1.3.6
|
simplekml~=1.3.6
|
||||||
geojson~=3.1.0
|
geojson~=3.1.0
|
||||||
certifi~=2023.11.17
|
certifi~=2023.11.17
|
||||||
SciPy~=1.11.4
|
SciPy~=1.11.4
|
||||||
ezdxf~=1.4.1
|
pycollada~=0.7.2
|
||||||
|
shapely
|
||||||
|
rtree
|
||||||
Reference in New Issue
Block a user