Compare commits
22 Commits
9ad6fd1a4b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d009cb7695 | |||
| 5a642a4119 | |||
| 74bf60101c | |||
| 5dd8869caf | |||
| 03464ffafd | |||
| e111a985c3 | |||
| 65d57e98b7 | |||
| 86bae4f643 | |||
| e49d0694b4 | |||
| 1241ee97ba | |||
| 0e4b6e7fa4 | |||
| 092ccb75e0 | |||
| 9524e73955 | |||
| a7ac8826a0 | |||
| 1d062a087f | |||
| af559092bf | |||
| c0291198b1 | |||
| 4981b00918 | |||
| 3b38651609 | |||
| 322830c79e | |||
| 58bf3b890b | |||
| c76f541ba2 |
@@ -340,7 +340,44 @@ class Trench(ArchComponent.Component):
|
||||
p2.z = 0
|
||||
return p2.sub(p1)
|
||||
|
||||
def getsegments(wire):
|
||||
def getsegments(wire): #deepseek
|
||||
"""Divide un wire en segmentos rectos basados en cambios de dirección (sin splitWiresByCurvature)"""
|
||||
import Part
|
||||
from math import degrees
|
||||
|
||||
segments = []
|
||||
current_segment = []
|
||||
angle_threshold = 1.0 # Grados para considerar cambio de dirección
|
||||
|
||||
def get_angle(v1, v2):
|
||||
return degrees(v1.getAngle(v2))
|
||||
|
||||
edges = wire.Edges
|
||||
for i in range(len(edges)):
|
||||
if i == 0:
|
||||
current_segment.append(edges[i])
|
||||
continue
|
||||
|
||||
prev_edge = edges[i - 1]
|
||||
curr_edge = edges[i]
|
||||
|
||||
# Vectores de dirección
|
||||
v1 = prev_edge.tangentAt(prev_edge.FirstParameter)
|
||||
v2 = curr_edge.tangentAt(curr_edge.FirstParameter)
|
||||
|
||||
angle = get_angle(v1, v2)
|
||||
if angle > angle_threshold:
|
||||
segments.append(Part.Wire(current_segment))
|
||||
current_segment = [curr_edge]
|
||||
else:
|
||||
current_segment.append(curr_edge)
|
||||
|
||||
if current_segment:
|
||||
segments.append(Part.Wire(current_segment))
|
||||
|
||||
return segments
|
||||
|
||||
def getsegments_old(wire):
|
||||
import math
|
||||
segments = []
|
||||
|
||||
@@ -381,13 +418,6 @@ class Trench(ArchComponent.Component):
|
||||
pts_plane.append(tmp)
|
||||
path_plane = Part.makePolygon(pts_plane)
|
||||
|
||||
'''o1 = path_plane.makeOffset2D(d, 2, False, True, True)
|
||||
o2 = path_plane.makeOffset2D(-d, 2, False, True, True)
|
||||
points = calculateOffset(o1)
|
||||
points.insert(0, points.pop(1))
|
||||
points.reverse()
|
||||
points2 = calculateOffset(o2)'''
|
||||
|
||||
points = self.calculateOffset(path_plane, d)
|
||||
points2 = self.calculateOffset(path_plane, -d)
|
||||
|
||||
@@ -1018,7 +1048,7 @@ class semiAutomaticTrench:
|
||||
self.unregister_editing_callbacks()
|
||||
|
||||
|
||||
class CommandTrench: # V1:
|
||||
'''class CommandTrench: # V1:
|
||||
"""Gui command for the Line tool."""
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1107,5 +1137,5 @@ if FreeCAD.GuiUp:
|
||||
|
||||
FreeCADGui.addCommand('PVPlantTrench', CommandTrench())
|
||||
FreeCADGui.addCommand('PVPlantSemiAutomaticTrench', CommandSemiAutomaticTrench())
|
||||
FreeCADGui.addCommand('Trenches', CommandTrenchGroup())
|
||||
FreeCADGui.addCommand('Trenches', CommandTrenchGroup())'''
|
||||
|
||||
@@ -47,16 +47,16 @@ except AttributeError:
|
||||
import PVPlantResources
|
||||
|
||||
|
||||
def makeCable(base = None):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Cable")
|
||||
def makeCable(name="Cable"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = name
|
||||
Cable(obj)
|
||||
ViewProviderCable(obj.ViewObject)
|
||||
if base:
|
||||
obj.Base = base
|
||||
return obj
|
||||
|
||||
|
||||
class Cable(ArchComponent.Component):
|
||||
"A Base Frame Obcject - Class"
|
||||
"A Cable Obcject - Class"
|
||||
|
||||
def __init__(self, obj):
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
@@ -254,48 +254,10 @@ class Cable(ArchComponent.Component):
|
||||
return val.Placement.Base
|
||||
|
||||
def execute(self, obj):
|
||||
import Part, DraftGeomUtils
|
||||
import Draft
|
||||
|
||||
if obj.Base:
|
||||
w = obj.Base.Shape.SubShapes[1].SubShapes[0]
|
||||
w = DraftGeomUtils.filletWire(w, 150)
|
||||
else:
|
||||
return
|
||||
|
||||
"""if obj.Base:
|
||||
# Si tiene ruta, dibujar ruteado
|
||||
import PVPlantTrench as trench
|
||||
if isinstance(obj.Base, trench.Trench):
|
||||
w = obj.Base.Shape.SubShapes[0]
|
||||
else:
|
||||
w = obj.Base.Shape
|
||||
elif obj.From and obj.Name:
|
||||
'''line = Part.LineSegment()
|
||||
line.StartPoint = getPoint(obj.From)
|
||||
line.EndPoint = getPoint(obj.To)
|
||||
w = Part.Wire(line.toShape())'''
|
||||
w = Part.makePolygon([self.getPoint(obj.From), self.getPoint(obj.To)])
|
||||
else:
|
||||
return"""
|
||||
|
||||
r = obj.ExternalDiameter.Value / 2
|
||||
p = Part.Wire([Part.Circle(FreeCAD.Vector(0, 0, 0),
|
||||
FreeCAD.Vector(0, 1, 0),
|
||||
r).toShape()])
|
||||
c = obj.Offset
|
||||
c.x -= r
|
||||
c.z += r
|
||||
v1 = w.Vertexes[1].Point - w.Vertexes[0].Point
|
||||
v2 = DraftGeomUtils.getNormal(p)
|
||||
p.Placement.Base = w.Vertexes[0].Point + c
|
||||
p.Placement.Rotation = FreeCAD.Rotation(v2, v1)
|
||||
|
||||
obj.Shape = w.makePipeShell([p], True, False, 0)
|
||||
obj.Distance = w.Length
|
||||
''' No hacer nada. Es un componente sin shape'''
|
||||
|
||||
|
||||
class ViewProviderCable(ArchComponent.ViewProviderComponent):
|
||||
class ViewProviderCable(ArchComponent.ViewProviderComponent): #hace falta??
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
|
||||
@@ -311,15 +273,7 @@ class CommandCable:
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el BOQ de la")}
|
||||
|
||||
def Activated(self):
|
||||
import Draft
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
wire = None
|
||||
for obj in sel:
|
||||
if Draft.getType(obj) == "Wire":
|
||||
wire = obj
|
||||
break
|
||||
|
||||
makeCable(wire)
|
||||
makeCable()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def IsActive(self):
|
||||
|
||||
78
Export/ExporterCommands.py
Normal file
78
Export/ExporterCommands.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2017 - Amritpal Singh <amrit3701@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 *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "RebarCommands"
|
||||
__author__ = "Amritpal Singh"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import FreeCADGui, FreeCAD
|
||||
from PySide import QtGui, QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
import os
|
||||
|
||||
|
||||
class CommandExportToPVSyst:
|
||||
"Export to PVSyst"
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "PVsyst.png")),
|
||||
'Accel': "E, P",
|
||||
'MenuText': QT_TRANSLATE_NOOP("Outputs", "Export to PVSyst"),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Outputs", "Exportar a PVSyst")}
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Export import exportPVSyst
|
||||
taskd = exportPVSyst.PVSystTaskPanel()
|
||||
# taskd.show()
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
from Export import PVPlantBOQMechanical, PVPlantBOQElectrical, PVPlantBOQCivil
|
||||
from Export import exportDXF, exportKMZ
|
||||
FreeCADGui.addCommand('BOQMechanical', PVPlantBOQMechanical.CommandBOQMechanical())
|
||||
FreeCADGui.addCommand('BOQElectrical', PVPlantBOQElectrical.CommandBOQElectrical())
|
||||
FreeCADGui.addCommand('BOQCivil', PVPlantBOQCivil.CommandBOQCivil())
|
||||
FreeCADGui.addCommand('exportDXF', exportDXF.CommandExportDXF())
|
||||
FreeCADGui.addCommand('exportToPVSyst', CommandExportToPVSyst())
|
||||
FreeCADGui.addCommand('exportKMZ', exportKMZ.CommandExportKMZ())
|
||||
|
||||
Exportlist = ["BOQCivil",
|
||||
"BOQMechanical",
|
||||
"BOQElectrical",
|
||||
"Separator",
|
||||
"exportDXF",
|
||||
# "importDXF",
|
||||
"exportToPVSyst",
|
||||
"exportKMZ",
|
||||
]
|
||||
@@ -59,7 +59,7 @@ def makeBOQCivil():
|
||||
|
||||
|
||||
|
||||
class _CommandBOQCivil:
|
||||
class CommandBOQCivil:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "boqc.svg")),
|
||||
@@ -76,5 +76,5 @@ class _CommandBOQCivil:
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQCivil', _CommandBOQCivil())
|
||||
'''if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQCivil', _CommandBOQCivil())'''
|
||||
|
||||
@@ -83,7 +83,8 @@ def makeBOQElectrical():
|
||||
b = mgb.get_boundary(m)
|
||||
Part.show(b)
|
||||
|
||||
class _CommandBOQElectrical:
|
||||
|
||||
class CommandBOQElectrical:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "boqe.svg")),
|
||||
'Accel': "R, E",
|
||||
@@ -100,5 +101,5 @@ class _CommandBOQElectrical:
|
||||
return False
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQElectrical', _CommandBOQElectrical())
|
||||
'''if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQElectrical', _CommandBOQElectrical())'''
|
||||
|
||||
@@ -20,32 +20,43 @@
|
||||
# * *
|
||||
# ***********************************************************************
|
||||
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
from typing import Dict, List
|
||||
|
||||
import FreeCAD
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui, os
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
import openpyxl
|
||||
from openpyxl.styles import Alignment, Border, Side, PatternFill, Font
|
||||
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
|
||||
from openpyxl.workbook import Workbook
|
||||
from openpyxl.worksheet.worksheet import Worksheet
|
||||
|
||||
import PVPlantResources
|
||||
import PVPlantSite
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
|
||||
__title__ = "PVPlant Mechanical BOQ"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
# Constants
|
||||
SCALE = 0.001 # Millimeters to meters
|
||||
THIN_BORDER = Border(
|
||||
top=Side(border_style="thin", color="7DA4B8"),
|
||||
left=Side(border_style="thin", color="7DA4B8"),
|
||||
right=Side(border_style="thin", color="7DA4B8"),
|
||||
bottom=Side(border_style="thin", color="7DA4B8")
|
||||
)
|
||||
HEADER_FILL = PatternFill("solid", fgColor="7DA4B8")
|
||||
HEADER_FONT = Font(name='Arial', size=10, bold=True, color="FFFFFF")
|
||||
DATA_FONT = Font(name='Arial', size=10)
|
||||
CENTER_ALIGN = Alignment(horizontal="center", vertical="center")
|
||||
|
||||
|
||||
|
||||
# Estilos:
|
||||
thin = Side(border_style="thin", color="7DA4B8")
|
||||
double = Side(border_style="double", color="ff0000")
|
||||
@@ -56,6 +67,16 @@ border_fat = Border(top=thin, left=thin, right=thin, bottom=thin)
|
||||
|
||||
scale = 0.001 # milimeters to meter
|
||||
|
||||
|
||||
def open_file(path: str) -> None:
|
||||
"""Open a file or directory using the system's default handler"""
|
||||
if platform.system() == "Windows":
|
||||
os.startfile(path)
|
||||
elif platform.system() == "Darwin":
|
||||
subprocess.Popen(["open", path])
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", path])
|
||||
|
||||
def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment=None):
|
||||
"""
|
||||
Apply styles to a range of cells as if they were a single cell.
|
||||
@@ -94,124 +115,118 @@ def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment
|
||||
for c in row:
|
||||
c.fill = fill
|
||||
|
||||
def spreadsheetBOQFrames(sheet, sel):
|
||||
sheet['A1'] = 'Index'
|
||||
sheet['B1'] = 'Frame'
|
||||
sheet['C1'] = 'Frame Type'
|
||||
sheet['D1'] = 'X'
|
||||
sheet['E1'] = 'Y'
|
||||
sheet['F1'] = 'Z'
|
||||
sheet['G1'] = 'Angle N-S'
|
||||
sheet['H1'] = 'Angle L-W'
|
||||
sheet['I1'] = 'Nº Poles'
|
||||
|
||||
sheet.column_dimensions['A'].width = 8
|
||||
sheet.column_dimensions['B'].width = 30
|
||||
sheet.column_dimensions['C'].width = 20
|
||||
sheet.column_dimensions['D'].width = 20
|
||||
sheet.column_dimensions['E'].width = 20
|
||||
sheet.column_dimensions['F'].width = 20
|
||||
sheet.column_dimensions['G'].width = 15
|
||||
sheet.column_dimensions['H'].width = 15
|
||||
sheet.column_dimensions['I'].width = 15
|
||||
sheet.row_dimensions[1].height = 40
|
||||
def create_sheet_headers(sheet: Worksheet, headers: List[str], widths: Dict[str, float]) -> None:
|
||||
"""Create standardized sheet headers."""
|
||||
for col, header in enumerate(headers, start=1):
|
||||
cell = sheet.cell(row=1, column=col, value=header)
|
||||
cell.fill = HEADER_FILL
|
||||
cell.font = HEADER_FONT
|
||||
cell.alignment = CENTER_ALIGN
|
||||
|
||||
style_range(sheet, 'A1:I1',
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
fill=PatternFill("solid", fgColor="7DA4B8"),
|
||||
font=Font(name='Quicksand', size=10, b=True, color="FFFFFF"),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
for col, width in widths.items():
|
||||
sheet.column_dimensions[col].width = width
|
||||
|
||||
for ind in range(0, len(sel)):
|
||||
row = ind + 2
|
||||
sheet['A{0}'.format(row)] = ind + 1
|
||||
sheet['B{0}'.format(row)] = sel[ind].Label
|
||||
sheet['C{0}'.format(row)] = sel[ind].Setup.Label
|
||||
sheet['D{0}'.format(row)] = sel[ind].Placement.Base.x * scale
|
||||
sheet['E{0}'.format(row)] = sel[ind].Placement.Base.y * scale
|
||||
sheet['R{0}'.format(row)] = sel[ind].Placement.Base.z * scale
|
||||
sheet['G{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[0]
|
||||
sheet['H{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[1]
|
||||
sheet['I{0}'.format(row)] = sel[ind].Setup.NumberPole.Value
|
||||
style_range(sheet, 'A' + str(row) + ':I' + str(row),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=10),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
sheet.row_dimensions[1].height = 30
|
||||
style_range(sheet, f'A1:{chr(64 + len(headers))}1',
|
||||
border=THIN_BORDER,
|
||||
fill=HEADER_FILL,
|
||||
font=HEADER_FONT,
|
||||
alignment=CENTER_ALIGN)
|
||||
|
||||
def spreadsheetBOQPoles(sheet, sel):
|
||||
import MeshPart as mp
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
|
||||
# Data:
|
||||
terrain = PVPlantSite.get().Terrain.Mesh # Shape
|
||||
def spreadsheetBOQFrames(sheet: Worksheet, selection: List[FreeCAD.DocumentObject]) -> None:
|
||||
"""Generate frames information sheet."""
|
||||
headers = [
|
||||
'Index', 'Frame', 'Frame Type', 'X (m)', 'Y (m)', 'Z (m)',
|
||||
'Angle N-S', 'Angle L-W', 'Nº Poles'
|
||||
]
|
||||
widths = {'A': 8, 'B': 30, 'C': 20, 'D': 15, 'E': 15,
|
||||
'F': 15, 'G': 15, 'H': 15, 'I': 10}
|
||||
|
||||
# Headers:
|
||||
sheet['A1'] = 'Frame'
|
||||
sheet['B1'] = 'Pole'
|
||||
sheet['C1'] = 'Pole Type'
|
||||
sheet['D1'] = 'X'
|
||||
sheet['E1'] = 'Y'
|
||||
sheet['F1'] = 'Z frame attach'
|
||||
sheet['G1'] = 'Z aerial head'
|
||||
sheet['H1'] = 'Pole length'
|
||||
sheet['I1'] = 'Pole aerial length'
|
||||
sheet['J1'] = 'Pole terrain enter length'
|
||||
create_sheet_headers(sheet, headers, widths)
|
||||
|
||||
sheet.column_dimensions['A'].width = 30
|
||||
sheet.column_dimensions['B'].width = 8
|
||||
sheet.column_dimensions['C'].width = 20
|
||||
sheet.column_dimensions['D'].width = 20
|
||||
sheet.column_dimensions['E'].width = 20
|
||||
sheet.column_dimensions['F'].width = 20
|
||||
sheet.column_dimensions['G'].width = 20
|
||||
sheet.column_dimensions['H'].width = 20
|
||||
sheet.column_dimensions['I'].width = 20
|
||||
sheet.column_dimensions['J'].width = 20
|
||||
sheet.row_dimensions[1].height = 40
|
||||
style_range(sheet, 'A1:J1',
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
fill=PatternFill("solid", fgColor="7DA4B8"),
|
||||
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
sheet['A2'] = ""
|
||||
for idx, obj in enumerate(selection, start=2):
|
||||
placement = obj.Placement
|
||||
sheet[f'A{idx}'] = idx - 1
|
||||
sheet[f'B{idx}'] = obj.Label
|
||||
sheet[f'C{idx}'] = obj.Setup.Label
|
||||
sheet[f'D{idx}'] = placement.Base.x * SCALE
|
||||
sheet[f'E{idx}'] = placement.Base.y * SCALE
|
||||
sheet[f'F{idx}'] = placement.Base.z * SCALE
|
||||
sheet[f'G{idx}'] = placement.Rotation.toEuler()[0]
|
||||
sheet[f'H{idx}'] = placement.Rotation.toEuler()[1]
|
||||
sheet[f'I{idx}'] = obj.Setup.NumberPole.Value
|
||||
|
||||
style_range(sheet, f'A{idx}:I{idx}',
|
||||
border=THIN_BORDER,
|
||||
font=DATA_FONT,
|
||||
alignment=CENTER_ALIGN)
|
||||
|
||||
def spreadsheetBOQPoles(sheet: Worksheet, selection: List[FreeCAD.DocumentObject]) -> None:
|
||||
"""Generate poles information sheet."""
|
||||
headers = [
|
||||
'Frame', 'Pole', 'Pole Type', 'X (m)', 'Y (m)', 'Z Frame Attach (m)',
|
||||
'Z Aerial Head (m)', 'Pole Length (m)', 'Aerial Length (m)',
|
||||
'Terrain Enter Length (m)'
|
||||
]
|
||||
widths = {chr(65 + i): 20 for i in range(10)}
|
||||
widths['A'] = 30
|
||||
widths['B'] = 10
|
||||
|
||||
create_sheet_headers(sheet, headers, widths)
|
||||
sheet.row_dimensions[2].height = 5
|
||||
|
||||
data = {"Frame": [],
|
||||
#"FrameType": [],
|
||||
"Pole": [],
|
||||
"PoleType": [],
|
||||
"PoleLength": [],
|
||||
"Center": [],
|
||||
"Head": []}
|
||||
|
||||
cnt = 0
|
||||
for frame_ind, frame in enumerate(sel):
|
||||
poles = frame.Shape.SubShapes[1].SubShapes[0].SubShapes
|
||||
numpoles = int(frame.Setup.NumberPole.Value)
|
||||
seq = frame.Setup.PoleSequence
|
||||
if len(seq) < numpoles:
|
||||
seq = PVPlantFrame.getarray(frame.Setup.PoleSequence, numpoles)
|
||||
for pole_ind in range(numpoles):
|
||||
pole = poles[pole_ind]
|
||||
poletype = frame.Setup.PoleType[seq[pole_ind]]
|
||||
data["Frame"].append(frame.Label)
|
||||
#data["FrameType"].append(frame.Setup.Label)
|
||||
data["Pole"].append(pole_ind + 1)
|
||||
data["PoleType"].append(poletype.Label)
|
||||
data["PoleLength"].append(int(poletype.Height))
|
||||
data["Center"].append(pole.BoundBox.Center)
|
||||
data["Head"].append(pole.BoundBox.ZMax)
|
||||
cnt += 1
|
||||
import MeshPart as mp
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
# Data:
|
||||
terrain = PVPlantSite.get().Terrain.Mesh # Shape
|
||||
poles_data = []
|
||||
|
||||
pts = mp.projectPointsOnMesh(data["Center"], terrain, FreeCAD.Vector(0, 0, 1))
|
||||
#if cnt == len(pts):
|
||||
data["Soil"] = pts
|
||||
for frame in selection:
|
||||
try:
|
||||
poles = frame.Shape.SubShapes[1].SubShapes[0].SubShapes
|
||||
num_poles = int(frame.Setup.NumberPole.Value)
|
||||
sequence = frame.Setup.PoleSequence
|
||||
|
||||
if len(sequence) < num_poles:
|
||||
sequence = PVPlantFrame.getarray(frame.Setup.PoleSequence, num_poles)
|
||||
|
||||
for pole_idx in range(num_poles):
|
||||
pole = poles[pole_idx]
|
||||
pole_type = frame.Setup.PoleType[sequence[pole_idx]]
|
||||
center = pole.BoundBox.Center
|
||||
|
||||
poles_data.append({
|
||||
'frame': frame.Label,
|
||||
'number': pole_idx + 1,
|
||||
'type': pole_type.Label,
|
||||
'center': center,
|
||||
'head_z': pole.BoundBox.ZMax,
|
||||
'length': pole_type.Height.Value
|
||||
})
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error processing frame {frame.Label}: {str(e)}\n")
|
||||
|
||||
# Project points on terrain
|
||||
try:
|
||||
import MeshPart
|
||||
points = [data['center'] for data in poles_data]
|
||||
terrain_z = MeshPart.projectPointsOnMesh(points, terrain, FreeCAD.Vector(0, 0, 1))
|
||||
for data, z in zip(poles_data, terrain_z):
|
||||
data['terrain_z'] = z.z
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Terrain projection failed: {str(e)}\n")
|
||||
for data in poles_data:
|
||||
data['terrain_z'] = 0
|
||||
|
||||
# Write data to sheet
|
||||
row = 3
|
||||
group_from = row
|
||||
f = data["Frame"][0]
|
||||
for i in range(0, len(data["Frame"])):
|
||||
if f != data["Frame"][i]:
|
||||
f = poles_data[0]['frame']
|
||||
for i, data in enumerate(poles_data):
|
||||
if f != data["frame"]:
|
||||
style_range(sheet, 'A' + str(group_from) + ':F' + str(row - 1),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=11, ),
|
||||
@@ -221,27 +236,28 @@ def spreadsheetBOQPoles(sheet, sel):
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=11, ),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
#sheet['A{0}'.format(row)] = ""
|
||||
# sheet['A{0}'.format(row)] = ""
|
||||
sheet.row_dimensions[row].height = 5
|
||||
row += 1
|
||||
f = data["Frame"][i]
|
||||
f = data["frame"]
|
||||
group_from = row
|
||||
|
||||
sheet['A{0}'.format(row)] = data['Frame'][i]
|
||||
sheet['B{0}'.format(row)] = data['Pole'][i]
|
||||
sheet['C{0}'.format(row)] = data['PoleType'][i]
|
||||
sheet['D{0}'.format(row)] = round(data['Center'][i].x, 0) * scale
|
||||
sheet['A{0}'.format(row)] = data['frame']
|
||||
sheet['B{0}'.format(row)] = data['number']
|
||||
sheet['C{0}'.format(row)] = data['type']
|
||||
sheet['D{0}'.format(row)] = round(data['center'].x, 0) * scale
|
||||
sheet['D{0}'.format(row)].number_format = "0.000"
|
||||
sheet['E{0}'.format(row)] = round(data['Center'][i].y, 0) * scale
|
||||
sheet['E{0}'.format(row)] = round(data['center'].y, 0) * scale
|
||||
sheet['E{0}'.format(row)].number_format = "0.000"
|
||||
try:
|
||||
sheet['F{0}'.format(row)] = round(data['Soil'][i].z, 0) * scale
|
||||
sheet['F{0}'.format(row)] = round(data['terrain_z'].z, 0) * scale
|
||||
sheet['F{0}'.format(row)].number_format = "0.000"
|
||||
except:
|
||||
pass
|
||||
sheet['G{0}'.format(row)] = round(data['Head'][i]) * scale
|
||||
|
||||
sheet['G{0}'.format(row)] = round(data['head_z']) * scale
|
||||
sheet['G{0}'.format(row)].number_format = "0.000"
|
||||
sheet['H{0}'.format(row)] = data["PoleLength"][i] * scale
|
||||
sheet['H{0}'.format(row)] = data["length"] * scale
|
||||
sheet['H{0}'.format(row)].number_format = "0.000"
|
||||
sheet['I{0}'.format(row)] = '=G{0}-F{0}'.format(row)
|
||||
sheet['I{0}'.format(row)].number_format = "0.000"
|
||||
@@ -250,7 +266,7 @@ def spreadsheetBOQPoles(sheet, sel):
|
||||
|
||||
style_range(sheet, 'A' + str(row) + ':J' + str(row),
|
||||
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
||||
font=Font(name='Quicksand', size=11,),
|
||||
font=Font(name='Quicksand', size=11, ),
|
||||
alignment=Alignment(horizontal="center", vertical="center"))
|
||||
row += 1
|
||||
|
||||
@@ -294,7 +310,7 @@ def spreadsheetBOQPanelCollision(sheet, sel):
|
||||
sheet['G{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[1]
|
||||
sheet['H{0}'.format(ind + 2)] = sel[ind].NumberPole.Value
|
||||
|
||||
class _CommandBOQMechanical:
|
||||
class CommandBOQMechanical:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "boqm.svg")),
|
||||
'Accel': "R, M",
|
||||
@@ -302,37 +318,39 @@ class _CommandBOQMechanical:
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el BOQ de la")}
|
||||
|
||||
def Activated(self):
|
||||
# make file global:
|
||||
#sel = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
sel = []
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
'''if not hasattr(obj, "Proxy"):
|
||||
continue
|
||||
if issubclass(obj.Proxy.__class__, PVPlantRack.Frame):
|
||||
objects.append(obj)'''
|
||||
if obj.Name.startswith("Tracker") and not obj.Name.startswith("TrackerSetup"):
|
||||
sel.append(obj)
|
||||
sel = sorted(sel, key=lambda x: x.Label)
|
||||
if len(sel) > 0:
|
||||
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
|
||||
filename = os.path.join(path, "BOQMechanical.xlsx")
|
||||
mywb = openpyxl.Workbook()
|
||||
sheet = mywb.active
|
||||
sheet.title = 'Frames information'
|
||||
spreadsheetBOQFrames(sheet, sel)
|
||||
doc = FreeCAD.ActiveDocument
|
||||
|
||||
sheet = mywb.create_sheet("Poles information")
|
||||
spreadsheetBOQPoles(sheet, sel)
|
||||
mywb.save(filename)
|
||||
if not doc or not doc.FileName:
|
||||
FreeCAD.Console.PrintError("Document must be saved first\n")
|
||||
return
|
||||
|
||||
print("Se ha generado el BOQMechanical: ")
|
||||
print(filename)
|
||||
'''import sys
|
||||
path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
|
||||
sys.path.append(path)'''
|
||||
try:
|
||||
sel = [obj for obj in FreeCAD.ActiveDocument.Objects if (hasattr(obj, "Proxy") and (obj.Proxy.Type == "Tracker"))]
|
||||
|
||||
import subprocess
|
||||
subprocess.Popen('explorer ' + path)
|
||||
if not sel:
|
||||
FreeCAD.Console.PrintWarning("No valid trackers found\n")
|
||||
return
|
||||
|
||||
path = os.path.dirname(doc.FileName)
|
||||
filename = os.path.join(path, "BOQ_Mechanical.xlsx")
|
||||
|
||||
sel = sorted(sel, key=lambda x: x.Label)
|
||||
|
||||
wb = openpyxl.Workbook()
|
||||
wb.remove(wb.active) # Remove default sheet
|
||||
|
||||
frames_sheet = wb.create_sheet("Frames Information")
|
||||
spreadsheetBOQFrames(frames_sheet, sel)
|
||||
|
||||
poles_sheet = wb.create_sheet("Poles information")
|
||||
spreadsheetBOQPoles(poles_sheet , sel)
|
||||
|
||||
wb.save(filename)
|
||||
FreeCAD.Console.PrintMessage(f"Report generated: {filename}\n")
|
||||
open_file(path)
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error generating BOQ: {str(e)}\n")
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
@@ -340,5 +358,3 @@ class _CommandBOQMechanical:
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('BOQMechanical', _CommandBOQMechanical())
|
||||
|
||||
@@ -4,7 +4,7 @@ from Utils.PVPlantUtils import findObjects
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide import QtCore, QtGui, QtWidgets
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import os
|
||||
@@ -27,98 +27,6 @@ from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
field = {"name": "", "width": 0, "heigth": 0}
|
||||
|
||||
|
||||
class exportDXF:
|
||||
|
||||
def __init__(self, filename):
|
||||
''' '''
|
||||
self.doc = None
|
||||
self.msp = None
|
||||
self.filename = filename
|
||||
|
||||
'''
|
||||
doc.linetypes.add("GRENZE2",
|
||||
# linetype definition in acad.lin:
|
||||
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
|
||||
# replacing BOX by shape index 132 (got index from an AutoCAD file),
|
||||
# ezdxf can't get shape index from ltypeshp.shx
|
||||
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description="Grenze eckig ----[]-----[]----[]-----[]----[]--",
|
||||
length=1.45, # required for complex line types
|
||||
})
|
||||
|
||||
doc.linetypes.add("GRENZE2",
|
||||
# linetype definition in acad.lin:
|
||||
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
|
||||
# replacing BOX by shape index 132 (got index from an AutoCAD file),
|
||||
# ezdxf can't get shape index from ltypeshp.shx
|
||||
pattern = "A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description = "Límite1 ----0-----0----0-----0----0-----0--",
|
||||
length = 1.45, # required for complex line types
|
||||
})
|
||||
'''
|
||||
|
||||
def createFile(self, version='R2018'):
|
||||
import ezdxf
|
||||
from ezdxf.tools.standards import linetypes
|
||||
|
||||
# 1. Create a new document
|
||||
self.doc = ezdxf.new(version)
|
||||
# 2. Setup document:
|
||||
self.doc.header["$INSUNITS"] = 6
|
||||
self.doc.header['$MEASUREMENT'] = 1
|
||||
self.doc.header['$LUNITS'] = 2
|
||||
self.doc.header['$AUNITS'] = 0
|
||||
|
||||
# 3. Add new entities to the modelspace:
|
||||
self.msp = self.doc.modelspace()
|
||||
|
||||
print("linetypes: ", self.doc.linetypes)
|
||||
for name, desc, pattern in linetypes():
|
||||
if name not in self.doc.linetypes:
|
||||
self.doc.linetypes.add(name=name,
|
||||
pattern=pattern,
|
||||
description=desc,
|
||||
)
|
||||
|
||||
|
||||
self.doc.linetypes.add(name="FENCELINE1",
|
||||
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description="Límite1 ----0-----0----0-----0----0-----0--",
|
||||
length=1.45) # required for complex line types
|
||||
|
||||
def save(self):
|
||||
from os.path import exists
|
||||
file_exists = exists(self.filename)
|
||||
self.doc.saveas(self.filename)
|
||||
self.doc.save()
|
||||
|
||||
def createLayer(self, layerName="newLayer", layerColor=(0,0,0), layerLineType='CONTINUOUS'):
|
||||
layer = self.doc.layers.add(name=layerName, linetype=layerLineType)
|
||||
layer.rgb = layerColor
|
||||
return layer
|
||||
|
||||
def createBlock(self, blockName='newBlock'):
|
||||
# Create a block
|
||||
block = self.doc.blocks.new(name=blockName)
|
||||
return block
|
||||
|
||||
def insertBlock(self, name, point=(0.0, 0.0), rotation=0.0, xscale=0.0, yscale=0.0):
|
||||
if name == "":
|
||||
return None
|
||||
|
||||
return self.msp.add_blockref(name, point, dxfattribs={
|
||||
'xscale': xscale,
|
||||
'yscale': yscale,
|
||||
'rotation': rotation
|
||||
})
|
||||
|
||||
def createPolyline(self, wire):
|
||||
data = getWire(wire.Shape)
|
||||
lwp = self.msp.add_lwpolyline(data)
|
||||
return lwp
|
||||
|
||||
|
||||
def getWire(wire, nospline=False, width=.0):
|
||||
"""Return a list of DXF ready points and bulges from a wire.
|
||||
|
||||
@@ -163,7 +71,7 @@ def getWire(wire, nospline=False, width=.0):
|
||||
--------
|
||||
calcBulge
|
||||
"""
|
||||
import Part
|
||||
|
||||
import DraftGeomUtils
|
||||
import math
|
||||
|
||||
@@ -261,14 +169,419 @@ def getArcData(edge):
|
||||
math.degrees(ang1), math.degrees(ang2))
|
||||
|
||||
|
||||
# =================================================================================
|
||||
# CLASE PARA EXPORTACIÓN DXF
|
||||
# =================================================================================
|
||||
|
||||
class exportDXF:
|
||||
|
||||
def __init__(self, filename):
|
||||
|
||||
self.doc = None
|
||||
self.msp = None
|
||||
self.filename = filename
|
||||
self.paper_layouts = []
|
||||
|
||||
'''
|
||||
doc.linetypes.add("GRENZE2",
|
||||
# linetype definition in acad.lin:
|
||||
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
|
||||
# replacing BOX by shape index 132 (got index from an AutoCAD file),
|
||||
# ezdxf can't get shape index from ltypeshp.shx
|
||||
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description="Grenze eckig ----[]-----[]----[]-----[]----[]--",
|
||||
length=1.45, # required for complex line types
|
||||
})
|
||||
|
||||
doc.linetypes.add("GRENZE2",
|
||||
# linetype definition in acad.lin:
|
||||
# A,.25,-.1,[BOX,ltypeshp.shx,x=-.1,s=.1],-.1,1
|
||||
# replacing BOX by shape index 132 (got index from an AutoCAD file),
|
||||
# ezdxf can't get shape index from ltypeshp.shx
|
||||
pattern = "A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description = "Límite1 ----0-----0----0-----0----0-----0--",
|
||||
length = 1.45, # required for complex line types
|
||||
})
|
||||
'''
|
||||
|
||||
def createFile(self, version='R2018'):
|
||||
import ezdxf
|
||||
from ezdxf.tools.standards import linetypes
|
||||
|
||||
# 1. Create a new document
|
||||
self.doc = ezdxf.new(version)
|
||||
|
||||
# 2. Setup document:
|
||||
self.doc.header["$INSUNITS"] = 6
|
||||
self.doc.header['$MEASUREMENT'] = 1
|
||||
self.doc.header['$LUNITS'] = 2
|
||||
self.doc.header['$AUNITS'] = 0
|
||||
|
||||
# 3. Add new entities to the modelspace:
|
||||
self.msp = self.doc.modelspace()
|
||||
|
||||
for name, desc, pattern in linetypes():
|
||||
if name not in self.doc.linetypes:
|
||||
self.doc.linetypes.add(name=name,
|
||||
pattern=pattern,
|
||||
description=desc,
|
||||
)
|
||||
|
||||
|
||||
self.doc.linetypes.add(name="FENCELINE1",
|
||||
pattern="A,.25,-.1,[132,ltypeshp.shx,x=-.1,s=.1],-.1,1",
|
||||
description="Límite1 ----0-----0----0-----0----0-----0--",
|
||||
length=1.45) # required for complex line types
|
||||
|
||||
def save(self):
|
||||
from os.path import exists
|
||||
file_exists = exists(self.filename)
|
||||
self.doc.saveas(self.filename)
|
||||
self.doc.save()
|
||||
|
||||
def createLayer(self, layerName="newLayer", layerColor=(0,0,0), layerLineType='CONTINUOUS'):
|
||||
layer = self.doc.layers.add(name=layerName, linetype=layerLineType)
|
||||
layer.rgb = layerColor
|
||||
return layer
|
||||
|
||||
def createBlock(self, blockName='newBlock'):
|
||||
# Create a block
|
||||
block = self.doc.blocks.new(name=blockName)
|
||||
return block
|
||||
|
||||
def insertBlock(self, name, point=(0.0, 0.0), rotation=0.0, xscale=0.0, yscale=0.0):
|
||||
if name == "":
|
||||
return None
|
||||
|
||||
return self.msp.add_blockref(name, point, dxfattribs={
|
||||
'xscale': xscale,
|
||||
'yscale': yscale,
|
||||
'rotation': rotation
|
||||
})
|
||||
|
||||
def createPolyline(self, wire):
|
||||
try:
|
||||
data = getWire(wire.Shape)
|
||||
lwp = self.msp.add_lwpolyline(data)
|
||||
return lwp
|
||||
except Exception as e:
|
||||
print("Error creating polyline:", e)
|
||||
return None
|
||||
|
||||
# =================================================================================
|
||||
# INTERFAZ DE USUARIO
|
||||
# =================================================================================
|
||||
class LineTypeEditorDelegate(QtWidgets.QStyledItemDelegate):
|
||||
"""Delegado que muestra el combobox personalizado solo durante la edición"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.line_styles = {
|
||||
"CONTINUOUS": QtCore.Qt.SolidLine,
|
||||
"DASHED": QtCore.Qt.DashLine,
|
||||
"DOTTED": QtCore.Qt.DotLine,
|
||||
"DASH-DOT": QtCore.Qt.DashDotLine,
|
||||
"DASH-DOT-DOT": QtCore.Qt.DashDotDotLine,
|
||||
"FENCELINE1": QtCore.Qt.CustomDashLine,
|
||||
}
|
||||
self.custom_patterns = {
|
||||
"FENCELINE1": [4, 2, 1, 2]
|
||||
}
|
||||
|
||||
# Tamaño mínimo para mostrar la previsualización
|
||||
self.min_preview_width = 80
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
# SOLO se crea el editor cuando se inicia la edición
|
||||
return LineTypeComboBox(parent)
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
# Establecer el valor actual en el editor
|
||||
value = index.data(QtCore.Qt.DisplayRole)
|
||||
if value:
|
||||
editor.setCurrentText(value)
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
# Guardar el valor seleccionado en el modelo
|
||||
model.setData(index, editor.currentText(), QtCore.Qt.EditRole)
|
||||
|
||||
def paint0(self, painter, option, index):
|
||||
"""Pintado normal de la celda (sin editor)"""
|
||||
# Configurar el fondo según selección
|
||||
if option.state & QtWidgets.QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
else:
|
||||
painter.fillRect(option.rect, option.palette.base())
|
||||
|
||||
# Dibujar el texto del tipo de línea
|
||||
text = index.data(QtCore.Qt.DisplayRole) or ""
|
||||
painter.setPen(option.palette.text().color())
|
||||
painter.drawText(option.rect.adjusted(5, 0, -5, 0),
|
||||
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter,
|
||||
text)
|
||||
|
||||
def paint1(self, painter, option, index):
|
||||
# Fondo para selección
|
||||
if option.state & QtWidgets.QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
else:
|
||||
painter.fillRect(option.rect, option.palette.base())
|
||||
|
||||
# Obtener el tipo de línea
|
||||
line_type = index.data(QtCore.Qt.DisplayRole) or "CONTINUOUS"
|
||||
line_type = line_type.upper()
|
||||
|
||||
# Área de texto
|
||||
text_rect = option.rect.adjusted(5, 0, -100, 0)
|
||||
painter.setPen(option.palette.color(QtGui.QPalette.Text))
|
||||
painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_type)
|
||||
|
||||
# Área de previsualización
|
||||
preview_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)
|
||||
|
||||
# Configurar el estilo de línea
|
||||
pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
|
||||
|
||||
if line_type in self.line_styles:
|
||||
pen.setStyle(self.line_styles[line_type])
|
||||
|
||||
if line_type in self.custom_patterns:
|
||||
pen.setDashPattern(self.custom_patterns[line_type])
|
||||
else:
|
||||
pen.setStyle(QtCore.Qt.SolidLine)
|
||||
|
||||
painter.setPen(pen)
|
||||
|
||||
# Dibujar la línea
|
||||
center_y = preview_rect.center().y()
|
||||
painter.drawLine(preview_rect.left(), center_y, preview_rect.right(), center_y)
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
# Fondo para selección
|
||||
if option.state & QtWidgets.QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
else:
|
||||
painter.fillRect(option.rect, option.palette.base())
|
||||
|
||||
# Obtener el tipo de línea
|
||||
line_type = index.data(QtCore.Qt.DisplayRole) or "CONTINUOUS"
|
||||
line_type = line_type.upper()
|
||||
|
||||
# Calcular el espacio disponible
|
||||
total_width = option.rect.width()
|
||||
|
||||
# Área de texto (ajustable)
|
||||
text_rect = option.rect.adjusted(5, 0, -5, 0)
|
||||
|
||||
# Área de previsualización (derecha, tamaño fijo pero adaptable)
|
||||
preview_width = min(self.min_preview_width, total_width - 50) # 50px para texto mínimo
|
||||
if preview_width < 20: # No mostrar previsualización si es demasiado pequeña
|
||||
preview_width = 0
|
||||
|
||||
# Ajustar rectángulos
|
||||
if preview_width > 0:
|
||||
text_rect.setRight(option.rect.right() - preview_width - 10)
|
||||
preview_rect = option.rect.adjusted(
|
||||
option.rect.width() - preview_width,
|
||||
2,
|
||||
-5,
|
||||
-2
|
||||
)
|
||||
else:
|
||||
# Solo mostrar texto si no hay espacio para previsualización
|
||||
preview_rect = None
|
||||
|
||||
# Dibujar texto del tipo de línea
|
||||
painter.setPen(option.palette.color(QtGui.QPalette.Text))
|
||||
painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_type)
|
||||
|
||||
# Dibujar previsualización si hay espacio suficiente
|
||||
if preview_rect and preview_width > 20:
|
||||
# Configurar el estilo de línea
|
||||
pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
|
||||
if line_type in self.line_styles:
|
||||
pen.setStyle(self.line_styles[line_type])
|
||||
if line_type in self.custom_patterns:
|
||||
pen.setDashPattern(self.custom_patterns[line_type])
|
||||
else:
|
||||
pen.setStyle(QtCore.Qt.SolidLine)
|
||||
|
||||
painter.setPen(pen)
|
||||
|
||||
# Dibujar la línea centrada verticalmente
|
||||
center_y = preview_rect.center().y()
|
||||
painter.drawLine(preview_rect.left(), center_y, preview_rect.right(), center_y)
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
return QtCore.QSize(200, 25)
|
||||
|
||||
|
||||
class LineTypeDelegate(QtWidgets.QStyledItemDelegate):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.line_types = [
|
||||
{"name": "Continuous", "pen": QtCore.Qt.SolidLine},
|
||||
{"name": "Dashed", "pen": QtCore.Qt.DashLine},
|
||||
{"name": "Dotted", "pen": QtCore.Qt.DotLine},
|
||||
{"name": "Dash-Dot", "pen": QtCore.Qt.DashDotLine},
|
||||
{"name": "Dash-Dot-Dot", "pen": QtCore.Qt.DashDotDotLine}
|
||||
]
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
# Dibujar el fondo
|
||||
painter.save()
|
||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
|
||||
# Configuraciones comunes
|
||||
line_color = QtGui.QColor(0, 0, 0) # Color de la línea
|
||||
line_width = 2 # Grosor de línea
|
||||
|
||||
# Dibujar el ítem base
|
||||
super().paint(painter, option, index)
|
||||
|
||||
# Obtener el tipo de línea
|
||||
line_type = self.line_types[index.row()]
|
||||
|
||||
# Configurar el área de dibujo
|
||||
text_rect = option.rect.adjusted(5, 0, -100, 0)
|
||||
line_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)
|
||||
|
||||
# Dibujar el texto
|
||||
painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_type["name"])
|
||||
|
||||
# Dibujar la línea de muestra
|
||||
pen = QtGui.QPen(line_color, line_width, line_type["pen"])
|
||||
painter.setPen(pen)
|
||||
|
||||
start_y = line_rect.center().y()
|
||||
painter.drawLine(line_rect.left(), start_y, line_rect.right(), start_y)
|
||||
|
||||
painter.restore()
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
size = super().sizeHint(option, index)
|
||||
size.setHeight(25) # Altura aumentada para mejor visualización
|
||||
return size
|
||||
|
||||
|
||||
class LineTypePreviewDelegate(QtWidgets.QStyledItemDelegate):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.line_types = [
|
||||
{"name": "Continuous", "style": QtCore.Qt.SolidLine},
|
||||
{"name": "Dashed", "style": QtCore.Qt.DashLine},
|
||||
{"name": "Dotted", "style": QtCore.Qt.DotLine},
|
||||
{"name": "Custom 1 (--0--0--)", "style": QtCore.Qt.CustomDashLine, "pattern": [8, 4, 2, 4]}, # Patrón personalizado
|
||||
{"name": "Custom 2 (- - -)", "style": QtCore.Qt.CustomDashLine, "pattern": [6, 3]} # Otro patrón
|
||||
]
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
|
||||
# SOLUCIÓN AL PROBLEMA: Usar un enfoque más simple sin painter.save/restore
|
||||
try:
|
||||
if index.row() >= len(self.line_types):
|
||||
return
|
||||
|
||||
# Configurar el fondo para selección
|
||||
if option.state & QtGui.QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
|
||||
# Determinar el tipo de línea
|
||||
line_data = self.line_types[index.row()]
|
||||
line_name = line_data["name"]
|
||||
line_style = line_data["style"]
|
||||
|
||||
# Área de texto
|
||||
text_rect = option.rect.adjusted(5, 0, -100, 0)
|
||||
painter.setPen(option.palette.color(QtGui.QPalette.Text))
|
||||
painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_name)
|
||||
|
||||
# Área de previsualización
|
||||
preview_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)
|
||||
|
||||
# Dibujar la línea de muestra
|
||||
pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
|
||||
pen.setStyle(line_style)
|
||||
painter.setPen(pen)
|
||||
|
||||
center_y = preview_rect.center().y()
|
||||
painter.drawLine(preview_rect.left(), center_y, preview_rect.right(), center_y)
|
||||
|
||||
except Exception as e:
|
||||
print("Error painting line type:", e)
|
||||
|
||||
'''
|
||||
painter.save()
|
||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
|
||||
if index.row() >= len(self.line_types):
|
||||
painter.restore()
|
||||
return
|
||||
|
||||
line_data = self.line_types[index.row()]
|
||||
line_name = line_data["name"]
|
||||
line_style = line_data.get("style", QtCore.Qt.SolidLine)
|
||||
dash_pattern = line_data.get("pattern", [])
|
||||
|
||||
# Fondo para selección
|
||||
if option.state & QtGui.QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
|
||||
# Áreas de dibujo
|
||||
text_rect = option.rect.adjusted(5, 0, -100, 0)
|
||||
preview_rect = option.rect.adjusted(option.rect.width() - 90, 2, -5, -2)
|
||||
|
||||
# Texto
|
||||
painter.setPen(option.palette.color(QtGui.QPalette.Text))
|
||||
painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, line_name)
|
||||
|
||||
# Línea personalizada
|
||||
pen = QtGui.QPen(QtGui.QColor(0, 0, 0), 2)
|
||||
pen.setStyle(line_style)
|
||||
|
||||
if line_style == QtCore.Qt.CustomDashLine and dash_pattern:
|
||||
pen.setDashPattern(dash_pattern)
|
||||
|
||||
painter.setPen(pen)
|
||||
painter.drawLine(preview_rect.left(), preview_rect.center().y(),
|
||||
preview_rect.right(), preview_rect.center().y())
|
||||
|
||||
painter.restore()'''
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
return QtCore.QSize(200, 25)
|
||||
|
||||
|
||||
class LineTypeComboBox(QtWidgets.QComboBox):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._delegate = LineTypePreviewDelegate(self)
|
||||
self.setItemDelegate(self._delegate)
|
||||
self.addItems(["Continuous", "Dashed", "Dotted", "Dash-Dot", "Dash-Dot-Dot"])
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""Pintado simplificado sin QPainter complejo"""
|
||||
painter = QtGui.QPainter(self)
|
||||
try:
|
||||
# Dibujar fondo
|
||||
option = QtWidgets.QStyleOptionComboBox()
|
||||
self.initStyleOption(option)
|
||||
self.style().drawComplexControl(QtGui.QStyle.CC_ComboBox, option, painter, self)
|
||||
|
||||
# Dibujar texto
|
||||
text_rect = self.rect().adjusted(5, 0, -30, 0)
|
||||
painter.setPen(self.palette().color(QtGui.QPalette.Text))
|
||||
painter.drawText(text_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, self.currentText())
|
||||
finally:
|
||||
painter.end() # Asegurar que el painter se cierre correctamente
|
||||
|
||||
|
||||
class _PVPlantExportDXF(QtGui.QWidget):
|
||||
'''The editmode TaskPanel to select what you want to export'''
|
||||
|
||||
def __init__(self):
|
||||
# super(_PVPlantExportDXF, self).__init__()
|
||||
QtGui.QWidget.__init__(self)
|
||||
|
||||
import os
|
||||
QtGui.QWidget.__init__(self)
|
||||
# self.form:
|
||||
self.form = FreeCADGui.PySideUic.loadUi(
|
||||
os.path.join(os.path.dirname(os.path.realpath(__file__)), "exportDXF.ui"))
|
||||
@@ -280,53 +593,229 @@ class _PVPlantExportDXF(QtGui.QWidget):
|
||||
self.layout.addWidget(self.form)
|
||||
|
||||
self.form.buttonAcept.clicked.connect(self.onAceptClick)
|
||||
self.form.buttonCancel.clicked.connect(self.onCancelClick)
|
||||
|
||||
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "autocad")
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
self.filename = os.path.join(path, FreeCAD.ActiveDocument.Name) + ".dxf"
|
||||
self.delegate = LineTypeEditorDelegate()
|
||||
self.form.tableLayers.setItemDelegateForColumn(3, self.delegate)
|
||||
|
||||
def createLayers(self):
|
||||
''' '''
|
||||
self.exporter.createLayer("Areas_Boundary", layerColor=(0, 125, 125), layerLineType="FENCELINE1")
|
||||
self.exporter.createLayer("Areas_Exclusion", layerColor=(255, 0, 0))
|
||||
self.exporter.createLayer("Areas_Offsets", layerColor=(128, 128, 255))
|
||||
from datetime import datetime
|
||||
import os
|
||||
output_dir = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "autocad")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
self.filename = os.path.join(output_dir, f"{timestamp}-{FreeCAD.ActiveDocument.Name}.dxf")
|
||||
|
||||
self.exporter.createLayer("Internal_Roads", layerColor=(128, 128, 128))
|
||||
self.exporter.createLayer("Internal_Roads_Axis", layerColor=(255, 255, 255), layerLineType="DASHEDX2")
|
||||
# Limpiar la tabla
|
||||
for row in range(self.form.tableLayers.rowCount() - 1, -1, -1):
|
||||
self.form.tableLayers.removeRow(row)
|
||||
|
||||
# Configuración de las capas por defecto
|
||||
self.add_row("Areas_Boundary", QtGui.QColor(0, 125, 125), "FENCELINE1", "1", True)
|
||||
self.add_row("Areas_Exclusion", QtGui.QColor(255, 0, 0), "CONTINUOUS", "1", True)
|
||||
self.add_row("Internal_Roads", QtGui.QColor(128, 128, 128), "CONTINUOUS", "1", True)
|
||||
self.add_row("Frames", QtGui.QColor(0, 255, 0), "CONTINUOUS", "1", True)
|
||||
|
||||
def writeArea(self):
|
||||
for area in FreeCAD.ActiveDocument.Boundary.Group:
|
||||
pol = self.exporter.createPolyline(area)
|
||||
def save_project_settings(self):
|
||||
"""Guarda la configuración actual en el proyecto de FreeCAD"""
|
||||
try:
|
||||
# Guardar estado de la tabla
|
||||
table_data = []
|
||||
for row in range(self.form.tableLayers.rowCount()):
|
||||
# Estado del checkbox
|
||||
checked = self.form.tableLayers.cellWidget(row, 0).findChild(QtWidgets.QCheckBox).isChecked()
|
||||
|
||||
# Nombre de capa
|
||||
name = self.form.tableLayers.item(row, 1).text()
|
||||
|
||||
# Color
|
||||
color_btn = self.form.tableLayers.cellWidget(row, 2).findChild(QtWidgets.QPushButton)
|
||||
color = color_btn.color
|
||||
color_str = f"{color.red()},{color.green()},{color.blue()}"
|
||||
|
||||
# Tipo de línea
|
||||
line_type = self.form.tableLayers.item(row, 3).text()
|
||||
|
||||
# Grosor
|
||||
thickness_combo = self.form.tableLayers.cellWidget(row, 4)
|
||||
thickness = thickness_combo.currentText()
|
||||
|
||||
table_data.append({
|
||||
"name": name,
|
||||
"color": color_str,
|
||||
"line_type": line_type,
|
||||
"thickness": thickness,
|
||||
"checked": checked
|
||||
})
|
||||
|
||||
# Crear o actualizar el objeto de configuración en el proyecto
|
||||
config_obj = None
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.Name == "DXF_Export_Config":
|
||||
config_obj = obj
|
||||
break
|
||||
|
||||
if not config_obj:
|
||||
config_obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "DXF_Export_Config")
|
||||
|
||||
# Guardar como propiedad del objeto
|
||||
config_obj.addProperty("App::PropertyString", "TableSettings", "DXF Export", "Table configuration")
|
||||
config_obj.TableSettings = json.dumps(table_data)
|
||||
|
||||
print("Configuración guardada en el proyecto")
|
||||
except Exception as e:
|
||||
print("Error guardando configuración en el proyecto:", e)
|
||||
|
||||
def load_project_settings(self):
|
||||
"""Carga la configuración guardada desde el proyecto de FreeCAD"""
|
||||
try:
|
||||
# Buscar objeto de configuración en el proyecto
|
||||
config_obj = None
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if obj.Name == "DXF_Export_Config":
|
||||
config_obj = obj
|
||||
break
|
||||
|
||||
if config_obj and hasattr(config_obj, "TableSettings"):
|
||||
table_json = config_obj.TableSettings
|
||||
if table_json:
|
||||
table_data = json.loads(table_json)
|
||||
self.load_table_data(table_data)
|
||||
return
|
||||
|
||||
# Si no hay configuración guardada para este proyecto, cargar configuración por defecto
|
||||
self.add_default_rows()
|
||||
except Exception as e:
|
||||
print("Error cargando configuración del proyecto:", e)
|
||||
self.add_default_rows()
|
||||
|
||||
def add_default_rows(self):
|
||||
"""Añade filas por defecto cuando no hay configuración guardada"""
|
||||
self.add_row("Areas_Boundary", QtGui.QColor(0, 125, 125), "FENCELINE1", "1", True)
|
||||
self.add_row("Areas_Exclusion", QtGui.QColor(255, 0, 0), "DASHED", "1", True)
|
||||
self.add_row("Internal_Roads", QtGui.QColor(128, 128, 128), "CONTINUOUS", "1", True)
|
||||
self.add_row("Frames", QtGui.QColor(0, 255, 0), "CONTINUOUS", "1", True)
|
||||
|
||||
def load_table_data(self, table_data):
|
||||
"""Carga los datos en la tabla desde una estructura de datos"""
|
||||
for layer_data in table_data:
|
||||
# Parsear color
|
||||
color_parts = layer_data["color"].split(",")
|
||||
if len(color_parts) == 3:
|
||||
color = QtGui.QColor(int(color_parts[0]), int(color_parts[1]), int(color_parts[2]))
|
||||
else:
|
||||
color = QtGui.QColor(0, 0, 0) # Color por defecto si hay error
|
||||
|
||||
# Añadir fila
|
||||
self.add_row(
|
||||
layer_data["name"],
|
||||
color,
|
||||
layer_data["line_type"],
|
||||
layer_data["thickness"],
|
||||
layer_data["checked"]
|
||||
)
|
||||
|
||||
def add_row(self, name, color, line_type, thickness, checked=True):
|
||||
row = self.form.tableLayers.rowCount()
|
||||
self.form.tableLayers.insertRow(row)
|
||||
self.form.tableLayers.setRowHeight(row, 25)
|
||||
|
||||
# Checkbox
|
||||
checkbox = QtWidgets.QCheckBox()
|
||||
checkbox.setChecked(checked)
|
||||
cell_widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QHBoxLayout(cell_widget)
|
||||
layout.addWidget(checkbox)
|
||||
layout.setAlignment(QtCore.Qt.AlignCenter)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form.tableLayers.setCellWidget(row, 0, cell_widget)
|
||||
|
||||
# Nombre de capa
|
||||
item = QtWidgets.QTableWidgetItem(name)
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
|
||||
self.form.tableLayers.setItem(row, 1, item)
|
||||
|
||||
# Selector de color
|
||||
color_btn = QtWidgets.QPushButton()
|
||||
color_btn.setFixedSize(20, 20)
|
||||
color_btn.setStyleSheet(f"background-color: {color.name()}; border: 1px solid #808080; border-radius: 3px;")
|
||||
color_btn.color = color
|
||||
color_btn.clicked.connect(lambda: self.change_color(color_btn))
|
||||
cell_widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QHBoxLayout(cell_widget)
|
||||
layout.addWidget(color_btn)
|
||||
layout.setAlignment(QtCore.Qt.AlignCenter)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form.tableLayers.setCellWidget(row, 2, cell_widget)
|
||||
|
||||
# Tipo de línea
|
||||
item = QtWidgets.QTableWidgetItem(line_type)
|
||||
self.form.tableLayers.setItem(row, 3, item)
|
||||
|
||||
# Grosor de línea
|
||||
thickness_combo = QtWidgets.QComboBox()
|
||||
thickness_combo.addItems(["0.1", "0.2", "0.3", "0.5", "1.0", "2.0"])
|
||||
thickness_combo.setCurrentText(thickness)
|
||||
self.form.tableLayers.setCellWidget(row, 4, thickness_combo)
|
||||
|
||||
def change_color(self, button):
|
||||
color = QtWidgets.QColorDialog.getColor(button.color, self, "Seleccionar color")
|
||||
if color.isValid():
|
||||
button.color = color
|
||||
button.setStyleSheet(f"background-color: {color.name()}; border: 1px solid #808080; border-radius: 3px;")
|
||||
|
||||
def createLayers(self, exporter):
|
||||
for row in range(self.form.tableLayers.rowCount()):
|
||||
if self.form.tableLayers.cellWidget(row, 0).findChild(QtWidgets.QCheckBox).isChecked():
|
||||
layer_name = self.form.tableLayers.item(row, 1).text()
|
||||
color = self.form.tableLayers.cellWidget(row, 2).findChild(QtWidgets.QPushButton).color
|
||||
#line_type = self.form.tableLayers.cellWidget(row, 3).currentText()
|
||||
line_type = self.form.tableLayers.item(row, 3).text()
|
||||
|
||||
exporter.createLayer(
|
||||
layerName=layer_name,
|
||||
layerColor=(color.red(), color.green(), color.blue()),
|
||||
layerLineType=line_type.upper()
|
||||
)
|
||||
|
||||
def writeArea(self, exporter):
|
||||
exporter.createPolyline(FreeCAD.ActiveDocument.Site.Boundary, "boundary")
|
||||
|
||||
areas_types = ["Boundaries", "Exclusions", "Offsets"]
|
||||
for area_type in areas_types:
|
||||
if hasattr(FreeCAD.ActiveDocument, area_type):
|
||||
for area in FreeCAD.ActiveDocument.Boundaries.Group:
|
||||
exporter.createPolyline(area, "Areas_Boundary")
|
||||
|
||||
'''for area in FreeCAD.ActiveDocument.Boundaries.Group:
|
||||
pol = exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Boundary"
|
||||
|
||||
for area in FreeCAD.ActiveDocument.Exclusion.Group:
|
||||
pol = self.exporter.createPolyline(area)
|
||||
for area in FreeCAD.ActiveDocument.Exclusions.Group:
|
||||
pol = exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Exclusion"
|
||||
|
||||
for area in FreeCAD.ActiveDocument.Offsets.Group:
|
||||
self.exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Offsets"
|
||||
pol = exporter.createPolyline(area)
|
||||
pol.dxf.layer = "Areas_Offsets"'''
|
||||
|
||||
def writeFrameSetups(self):
|
||||
import Part
|
||||
# 1. Profiles:
|
||||
profilelist = list()
|
||||
def writeFrameSetups(self, exporter):
|
||||
if not hasattr(FreeCAD.ActiveDocument, "Site"):
|
||||
return
|
||||
|
||||
# 2. Postes
|
||||
for ts in FreeCAD.ActiveDocument.Site.Frames:
|
||||
for poletype in ts.PoleType:
|
||||
if not (poletype in profilelist):
|
||||
profilelist.append(poletype)
|
||||
block = self.exporter.createBlock(poletype.Label)
|
||||
w = poletype.Base.Shape.Wires[0]
|
||||
w.Placement.Base = FreeCAD.Vector(w.Placement.Base).sub(w.BoundBox.Center)
|
||||
block.add_lwpolyline(getWire(w))
|
||||
block = exporter.createBlock(poletype.Label)
|
||||
w = poletype.Base.Shape.Wires[0]
|
||||
center = w.BoundBox.Center
|
||||
w.Placement.Base = w.Placement.Base.sub(center)
|
||||
block.add_lwpolyline(getWire(w))
|
||||
|
||||
block.add_circle((0, 0), 0.2, dxfattribs={'color': 2})
|
||||
p = math.sin(math.radians(45)) * 0.2
|
||||
block.add_circle((0, 0), 0.2, dxfattribs={'color': 2})
|
||||
p = math.sin(math.radians(45)) * 0.2
|
||||
|
||||
block.add_line((-p, -p), (p, p), dxfattribs={"layer": "MyLines"})
|
||||
block.add_line((-p, p), (p, -p), dxfattribs={"layer": "MyLines"})
|
||||
block.add_line((-p, -p), (p, p), dxfattribs={"layer": "MyLines"})
|
||||
block.add_line((-p, p), (p, -p), dxfattribs={"layer": "MyLines"})
|
||||
|
||||
# 2. Frames
|
||||
for ts in FreeCAD.ActiveDocument.Site.Frames:
|
||||
@@ -336,10 +825,10 @@ class _PVPlantExportDXF(QtGui.QWidget):
|
||||
w = Part.makePolygon(pts)
|
||||
w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
|
||||
mblockname = "Trina_TSM-DEG21C-20-6XXWp Vertex"
|
||||
mblock = self.exporter.createBlock(mblockname)
|
||||
mblock = exporter.createBlock(mblockname)
|
||||
mblock.add_lwpolyline(getWire(w))
|
||||
|
||||
rblock = self.exporter.createBlock(ts.Label)
|
||||
rblock = exporter.createBlock(ts.Label)
|
||||
w = max(ts.Shape.SubShapes[0].SubShapes[1].SubShapes[0].Faces, key=lambda x: x.Placement.Base.z).Wires[0]
|
||||
w.Placement.Base = w.Placement.Base.sub(w.BoundBox.Center)
|
||||
rblock.add_lwpolyline(getWire(w))
|
||||
@@ -359,34 +848,38 @@ class _PVPlantExportDXF(QtGui.QWidget):
|
||||
'yscale': .0,
|
||||
'rotation': 0})
|
||||
|
||||
def writeFrames(self):
|
||||
def writeFrames(self, exporter):
|
||||
objects = findObjects('Tracker')
|
||||
for frame in objects:
|
||||
if hasattr(frame, "Setup"):
|
||||
point = frame.Placement.Base * 0.001
|
||||
point = point[:2]
|
||||
self.exporter.insertBlock(frame.Setup.Label, point=point, rotation=frame.AngleZ)
|
||||
exporter.insertBlock(frame.Setup.Label, point=point, rotation=frame.AngleZ)
|
||||
|
||||
def writeRoads(self):
|
||||
def writeRoads(self, exporter):
|
||||
objects = findObjects("Road")
|
||||
#rblock = self.exporter.createBlock("Internal_roads")
|
||||
#rblock = exporter.createBlock("Internal_roads")
|
||||
for road in objects:
|
||||
base = self.exporter.createPolyline(road.Base)
|
||||
base = exporter.createPolyline(road.Base, "Internal_Roads")
|
||||
base.dxf.const_width = road.Width.Value * 0.001
|
||||
base.dxf.layer = "Internal_Roads"
|
||||
|
||||
axis = self.exporter.createPolyline(road.Base)
|
||||
axis = exporter.createPolyline(road.Base, "Internal_Roads_Axis")
|
||||
axis.dxf.const_width = .2
|
||||
axis.dxf.layer = "Internal_Roads_Axis"
|
||||
#my_lines = doc.layers.get('MyLines')
|
||||
|
||||
def writeTrenches(self):
|
||||
if FreeCAD.ActiveDocument.Transport:
|
||||
for road in FreeCAD.ActiveDocument.Transport.Group:
|
||||
base = exporter.createPolyline(road, "External_Roads")
|
||||
base.dxf.const_width = road.Width
|
||||
|
||||
axis = exporter.createPolyline(road.Base, "External_Roads_Axis")
|
||||
axis.dxf.const_width = .2
|
||||
|
||||
def writeTrenches(self, exporter):
|
||||
objects = findObjects("Trench")
|
||||
# rblock = self.exporter.createBlock("Internal_roads")
|
||||
# rblock = exporter.createBlock("Internal_roads")
|
||||
for obj in objects:
|
||||
base = self.exporter.createPolyline(obj.Base)
|
||||
base = exporter.createPolyline(obj.Base, "Trench")
|
||||
base.dxf.const_width = obj.Width.Value * 0.001
|
||||
base.dxf.layer = "Trench"
|
||||
|
||||
def setup_layout4(self, doc):
|
||||
layout2 = doc.layouts.new("scale 1-1")
|
||||
@@ -423,25 +916,86 @@ class _PVPlantExportDXF(QtGui.QWidget):
|
||||
layout2.add_line((center.x, y1), (center.x, y2)) # vertical center line
|
||||
layout2.add_circle((0, 0), radius=5) # plot origin
|
||||
|
||||
def onAceptClick(self):
|
||||
self.exporter = exportDXF(self.filename)
|
||||
if self.exporter:
|
||||
self.exporter.createFile()
|
||||
self.createLayers()
|
||||
self.writeArea()
|
||||
self.writeFrameSetups()
|
||||
self.writeFrames()
|
||||
self.writeRoads()
|
||||
self.writeTrenches()
|
||||
self.setup_layout4(self.exporter.doc)
|
||||
def createPaperSpaces(self, exporter):
|
||||
# Datos para el cajetín
|
||||
title_block_data = {
|
||||
'width': 190,
|
||||
'height': 50,
|
||||
'fields': [
|
||||
{'name': 'Proyecto', 'value': FreeCAD.ActiveDocument.Label},
|
||||
{'name': 'Escala', 'value': '1:100'},
|
||||
{'name': 'Fecha', 'value': QtCore.QDate.currentDate().toString("dd/MM/yyyy")},
|
||||
{'name': 'Dibujado por', 'value': os.getlogin()}
|
||||
]
|
||||
}
|
||||
|
||||
self.exporter.save()
|
||||
print(self.filename)
|
||||
# Datos para la tabla de información
|
||||
table_data = {
|
||||
'Potencia pico': '50 MWp',
|
||||
'Número de trackers': str(len(findObjects('Tracker'))),
|
||||
'Superficie total': f"{FreeCAD.ActiveDocument.Site.Boundary.Shape.Area / 10000:.2f} ha"
|
||||
}
|
||||
|
||||
# Datos para el viewport
|
||||
viewport_data = {
|
||||
'center': (150, 100),
|
||||
'size': (250, 180),
|
||||
'view_center': (0, 0),
|
||||
'view_height': 200
|
||||
}
|
||||
|
||||
# Crear diferentes layouts para diferentes tamaños de papel
|
||||
exporter.createPaperSpaceLayout(
|
||||
"Layout_A4",
|
||||
(297, 210), # A4 landscape
|
||||
title_block_data,
|
||||
table_data,
|
||||
viewport_data
|
||||
)
|
||||
|
||||
exporter.createPaperSpaceLayout(
|
||||
"Layout_A3",
|
||||
(420, 297), # A3 landscape
|
||||
title_block_data,
|
||||
table_data,
|
||||
viewport_data
|
||||
)
|
||||
|
||||
def onAceptClick(self):
|
||||
exporter = exportDXF(self.filename)
|
||||
exporter.createFile()
|
||||
|
||||
# Crear capas configuradas
|
||||
self.createLayers(exporter)
|
||||
|
||||
# Exportar objetos
|
||||
self.writeArea(exporter)
|
||||
self.writeFrameSetups(exporter)
|
||||
self.writeFrames(exporter)
|
||||
self.writeRoads(exporter)
|
||||
self.writeTrenches(exporter)
|
||||
|
||||
# Crear espacios de papel
|
||||
self.setup_layout4(exporter.doc)
|
||||
self.createPaperSpaces(exporter)
|
||||
|
||||
# Guardar archivo
|
||||
exporter.save()
|
||||
|
||||
# Mostrar mensaje de éxito
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Exportación completada",
|
||||
f"Archivo DXF guardado en:\n{self.filename}"
|
||||
)
|
||||
|
||||
self.close()
|
||||
|
||||
def onCancelClick(self):
|
||||
# Cerrar el diálogo sin hacer nada
|
||||
self.close()
|
||||
|
||||
class _CommandExportDXF:
|
||||
class CommandExportDXF:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "dxf.svg")),
|
||||
'Accel': "E, A",
|
||||
@@ -462,5 +1016,5 @@ class _CommandExportDXF:
|
||||
return False
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('exportDXF', _CommandExportDXF())
|
||||
'''if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('exportDXF', _CommandExportDXF())'''
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>715</width>
|
||||
<height>520</height>
|
||||
<width>462</width>
|
||||
<height>282</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -33,19 +33,197 @@
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Tab 1</string>
|
||||
<string>Layers</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableLayers">
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Sel</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>LineType</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Lineweight</string>
|
||||
</property>
|
||||
</column>
|
||||
<item row="2" column="1">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Tab 2</string>
|
||||
<string>Objetos a exportar</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableObjects">
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>New Row</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>New Row</string>
|
||||
</property>
|
||||
</row>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Sel</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>LineType</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Lineweight</string>
|
||||
</property>
|
||||
</column>
|
||||
<item row="0" column="0">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>Planos en papel a crear</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tablePapers">
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>New Row</string>
|
||||
</property>
|
||||
</row>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>New Row</string>
|
||||
</property>
|
||||
</row>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Sel</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>LineType</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Lineweight</string>
|
||||
</property>
|
||||
</column>
|
||||
<item row="0" column="0">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonAcept">
|
||||
<property name="text">
|
||||
|
||||
278
Export/exportKMZ.py
Normal file
278
Export/exportKMZ.py
Normal file
@@ -0,0 +1,278 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
import tempfile
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
from PySide2 import QtWidgets, QtCore, QtGui
|
||||
import FreeCAD
|
||||
import Mesh
|
||||
import Part
|
||||
import Import
|
||||
import pyproj
|
||||
import simplekml
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from DraftTools import translate
|
||||
else:
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
|
||||
# Verificación de dependencias
|
||||
try:
|
||||
from pyproj import Transformer
|
||||
except ImportError:
|
||||
QtWidgets.QMessageBox.critical(None, "Error", "pyproj no está instalado.")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
import simplekml
|
||||
except ImportError:
|
||||
QtWidgets.QMessageBox.critical(None, "Error", "simplekml no está instalado.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class ExportKMZDialog(QtWidgets.QDialog):
|
||||
def __init__(self):
|
||||
super(ExportKMZDialog, self).__init__()
|
||||
self.setWindowTitle("Exportar a KMZ")
|
||||
self.layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
# Selección de archivo FCStd
|
||||
self.file_layout = QtWidgets.QHBoxLayout()
|
||||
self.fcstd_line = QtWidgets.QLineEdit()
|
||||
self.browse_fcstd_btn = QtWidgets.QPushButton("Examinar...")
|
||||
self.browse_fcstd_btn.clicked.connect(self.browse_fcstd)
|
||||
self.file_layout.addWidget(self.fcstd_line)
|
||||
self.file_layout.addWidget(self.browse_fcstd_btn)
|
||||
self.layout.addLayout(self.file_layout)
|
||||
|
||||
# Sistema de coordenadas
|
||||
self.crs_label = QtWidgets.QLabel("Sistema de coordenadas origen:")
|
||||
self.crs_display = QtWidgets.QLabel("No encontrado")
|
||||
self.layout.addWidget(self.crs_label)
|
||||
self.layout.addWidget(self.crs_display)
|
||||
|
||||
# Archivo de salida KMZ
|
||||
self.output_layout = QtWidgets.QHBoxLayout()
|
||||
self.kmz_line = QtWidgets.QLineEdit()
|
||||
self.browse_kmz_btn = QtWidgets.QPushButton("Examinar...")
|
||||
self.browse_kmz_btn.clicked.connect(self.browse_kmz)
|
||||
self.output_layout.addWidget(self.kmz_line)
|
||||
self.output_layout.addWidget(self.browse_kmz_btn)
|
||||
self.layout.addWidget(QtWidgets.QLabel("Archivo KMZ de salida:"))
|
||||
self.layout.addLayout(self.output_layout)
|
||||
|
||||
# Progreso y logs
|
||||
self.progress = QtWidgets.QProgressBar()
|
||||
self.log = QtWidgets.QTextEdit()
|
||||
self.log.setReadOnly(True)
|
||||
self.layout.addWidget(self.progress)
|
||||
self.layout.addWidget(self.log)
|
||||
|
||||
# Botones
|
||||
self.buttons = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
|
||||
self.buttons.accepted.connect(self.accept)
|
||||
self.buttons.rejected.connect(self.reject)
|
||||
self.layout.addWidget(self.buttons)
|
||||
|
||||
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "kmz")
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
name = datetime.now().strftime("%Y%m%d%H%M%S") + "-" + FreeCAD.ActiveDocument.Name
|
||||
self.filename = os.path.join(path, name) + ".kmz"
|
||||
|
||||
def browse_fcstd(self):
|
||||
path, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||
self, "Seleccionar archivo FreeCAD", "", "*.FCStd")
|
||||
if path:
|
||||
self.fcstd_line.setText(path)
|
||||
crs = self.get_crs_from_fcstd(path)
|
||||
self.crs_display.setText(crs if crs else "No encontrado")
|
||||
|
||||
def get_crs_from_fcstd(self, path):
|
||||
try:
|
||||
with zipfile.ZipFile(path, 'r') as z:
|
||||
doc_xml = z.read('Document.xml')
|
||||
root = ET.fromstring(doc_xml)
|
||||
for prop in root.findall('.//Property[@name="CoordinateSystem"]'):
|
||||
if prop.get('type') == 'App::PropertyString':
|
||||
return prop.find('String').get('value')
|
||||
except Exception as e:
|
||||
self.log.append(f"Error leyendo CRS: {str(e)}")
|
||||
return None
|
||||
|
||||
def browse_kmz(self):
|
||||
path, _ = QtWidgets.QFileDialog.getSaveFileName(
|
||||
self, "Guardar KMZ", "", "*.kmz")
|
||||
if path:
|
||||
self.kmz_line.setText(path)
|
||||
|
||||
|
||||
class ExportKMZ(QtCore.QObject):
|
||||
progress_updated = QtCore.Signal(int)
|
||||
log_message = QtCore.Signal(str)
|
||||
|
||||
def __init__(self, input_path, output_path, crs):
|
||||
super().__init__()
|
||||
self.input_path = input_path
|
||||
self.output_path = output_path
|
||||
self.crs = crs
|
||||
self.doc = None
|
||||
self.transformer = pyproj.Transformer.from_crs(crs, 'EPSG:4326', always_xy=True)
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.kml = simplekml.Kml()
|
||||
self.model_folder = self.kml.newfolder(name='Modelos 3D')
|
||||
self.drawing_folder = self.kml.newfolder(name='Dibujos 2D')
|
||||
|
||||
def process(self):
|
||||
try:
|
||||
self.doc = FreeCAD.openDocument(self.input_path)
|
||||
FreeCAD.setActiveDocument(self.doc.Name)
|
||||
self.process_objects()
|
||||
self.save_kmz()
|
||||
self.log_message.emit("Exportación completada exitosamente.")
|
||||
return True
|
||||
except Exception as e:
|
||||
self.log_message.emit(f"Error: {str(e)}")
|
||||
return False
|
||||
finally:
|
||||
if self.doc:
|
||||
FreeCAD.closeDocument(self.doc.Name)
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def process_objects(self):
|
||||
total = len(self.doc.Objects)
|
||||
for i, obj in enumerate(self.doc.Objects):
|
||||
try:
|
||||
if hasattr(obj, 'Shape') and obj.Shape.Volume > 0:
|
||||
self.process_3d(obj)
|
||||
elif obj.TypeId == 'Sketcher::SketchObject':
|
||||
self.process_2d(obj)
|
||||
except Exception as e:
|
||||
self.log_message.emit(f"Error procesando {obj.Name}: {str(e)}")
|
||||
self.progress_updated.emit(int((i + 1) / total * 100))
|
||||
|
||||
def process_3d(self, obj):
|
||||
placement = obj.getGlobalPlacement()
|
||||
x, y, z = placement.Base.x, placement.Base.y, placement.Base.z
|
||||
lon, lat = self.transformer.transform(x, y)
|
||||
|
||||
temp_doc = FreeCAD.newDocument("TempExport", hidden=True)
|
||||
temp_obj = temp_doc.addObject('Part::Feature', 'TempObj')
|
||||
temp_obj.Shape = obj.Shape.copy()
|
||||
temp_obj.Placement = FreeCAD.Placement()
|
||||
|
||||
model_path = os.path.join(self.temp_dir, f"{obj.Name}.dae")
|
||||
Import.export(temp_obj, model_path)
|
||||
FreeCAD.closeDocument(temp_doc.Name)
|
||||
|
||||
color = obj.ViewObject.ShapeColor
|
||||
kml_color = simplekml.Color.rgb(
|
||||
int(color[0] * 255), int(color[1] * 255), int(color[2] * 255))
|
||||
|
||||
model = self.model_folder.newmodel(name=obj.Name)
|
||||
model.altitudemode = simplekml.AltitudeMode.relativetoground
|
||||
model.longitude = lon
|
||||
model.latitude = lat
|
||||
model.altitude = z
|
||||
model.model = simplekml.Model()
|
||||
model.model.link.href = f"models/{obj.Name}.dae"
|
||||
model.style = simplekml.Style()
|
||||
model.style.polystyle.color = kml_color
|
||||
|
||||
def process_2d(self, obj):
|
||||
coords = []
|
||||
placement = obj.getGlobalPlacement()
|
||||
for geom in obj.Geometry:
|
||||
for point in geom.StartPoint, geom.EndPoint:
|
||||
global_point = placement.multVec(point)
|
||||
lon, lat = self.transformer.transform(global_point.x, global_point.y)
|
||||
coords.append((lon, lat, global_point.z))
|
||||
|
||||
if len(coords) < 3:
|
||||
return
|
||||
|
||||
poly = self.drawing_folder.newpolygon(name=obj.Name)
|
||||
poly.outerboundaryis = coords
|
||||
poly.altitudemode = simplekml.AltitudeMode.relativetoground
|
||||
poly.style.polystyle.color = simplekml.Color.rgb(255, 0, 0, 128)
|
||||
|
||||
def save_kmz(self):
|
||||
kml_path = os.path.join(self.temp_dir, "doc.kml")
|
||||
self.kml.save(kml_path)
|
||||
|
||||
with zipfile.ZipFile(self.output_path, 'w') as zipf:
|
||||
zipf.write(kml_path, arcname='doc.kml')
|
||||
for root, _, files in os.walk(self.temp_dir):
|
||||
for file in files:
|
||||
if file.endswith('.dae'):
|
||||
zipf.write(
|
||||
os.path.join(root, file),
|
||||
arcname=os.path.join('models', file))
|
||||
|
||||
|
||||
'''
|
||||
def main():
|
||||
app = QtWidgets.QApplication([]) if not FreeCAD.GuiUp else QtWidgets.QApplication.instance()
|
||||
dialog = ExportKMZDialog()
|
||||
if dialog.exec_() == QtWidgets.QDialog.Accepted:
|
||||
input_path = dialog.fcstd_line.text()
|
||||
output_path = dialog.kmz_line.text()
|
||||
crs = dialog.crs_display.text()
|
||||
|
||||
if not crs:
|
||||
QtWidgets.QMessageBox.critical(None, "Error", "Sistema de coordenadas no definido.")
|
||||
return
|
||||
|
||||
exporter = ExportKMZ(input_path, output_path, crs)
|
||||
progress_dialog = QtWidgets.QProgressDialog("Exportando...", "Cancelar", 0, 100)
|
||||
exporter.progress_updated.connect(progress_dialog.setValue)
|
||||
exporter.log_message.connect(lambda msg: dialog.log.append(msg))
|
||||
progress_dialog.show()
|
||||
|
||||
QtCore.QTimer.singleShot(100, exporter.process)
|
||||
app.exec_()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
'''
|
||||
|
||||
|
||||
class CommandExportKMZ:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "googleearth.svg")),
|
||||
'Accel': "E, G",
|
||||
'MenuText': "Export to KMZ",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Export choosed layers to kmz file")}
|
||||
|
||||
def Activated(self):
|
||||
taskd = ExportKMZDialog()
|
||||
taskd.setParent(FreeCADGui.getMainWindow())
|
||||
taskd.setWindowFlags(QtCore.Qt.Dialog or QtCore.Qt.Dialog)
|
||||
taskd.setWindowModality(QtCore.Qt.WindowModal)
|
||||
taskd.show()
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -186,8 +186,8 @@ def createPVPanel(name):
|
||||
typpvpanel.noct = 45.0 # SINTC (double) (Cº)
|
||||
|
||||
typpvpanel.manuf = "" # Fabricante
|
||||
typpypanel.chr_name = "" # Nombre característico
|
||||
typpypanel.appr_status = 1 # Estado: 0: No aprobado, 1: aprobado
|
||||
typpvpanel.chr_name = "" # Nombre característico
|
||||
typpvpanel.appr_status = 1 # Estado: 0: No aprobado, 1: aprobado
|
||||
|
||||
return typpvpanel
|
||||
|
||||
|
||||
@@ -44,37 +44,24 @@ except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
## @package importDAE
|
||||
# \ingroup ARCH
|
||||
# \brief DAE (Collada) file format importer and exporter
|
||||
#
|
||||
# This module provides tools to import and export Collada (.dae) files.
|
||||
|
||||
__title__ = "FreeCAD Collada importer"
|
||||
__author__ = "Yorik van Havre"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
try:
|
||||
# Python 2 forward compatibility
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
import collada
|
||||
COLLADA_AVAILABLE = True
|
||||
except ImportError:
|
||||
COLLADA_AVAILABLE = False
|
||||
|
||||
__title__ = "FreeCAD PVSyst importer"
|
||||
__author__ = "Javier Braña"
|
||||
#__url__ = "http://www.freecadweb.org"
|
||||
|
||||
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||
|
||||
|
||||
def checkCollada():
|
||||
"checks if collada if available"
|
||||
|
||||
global collada
|
||||
COLLADA = None
|
||||
try:
|
||||
import collada
|
||||
except ImportError:
|
||||
FreeCAD.Console.PrintError(translate("Arch", "pycollada not found, collada support is disabled.") + "\n")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
def check_collada():
|
||||
"""Verifica la disponibilidad de pycollada"""
|
||||
if not COLLADA_AVAILABLE:
|
||||
FreeCAD.Console.PrintError(translate("PVPlant", "pycollada no encontrado, soporte Collada desactivado.") + "\n")
|
||||
return COLLADA_AVAILABLE
|
||||
|
||||
|
||||
# from ARCH:
|
||||
@@ -84,6 +71,7 @@ def triangulate(shape):
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||
mesher = p.GetInt("ColladaMesher", 0)
|
||||
tessellation = p.GetFloat("ColladaTessellation", 1.0)
|
||||
|
||||
grading = p.GetFloat("ColladaGrading", 0.3)
|
||||
segsperedge = p.GetInt("ColladaSegsPerEdge", 1)
|
||||
segsperradius = p.GetInt("ColladaSegsPerRadius", 2)
|
||||
@@ -96,9 +84,15 @@ def triangulate(shape):
|
||||
elif mesher == 1:
|
||||
return MeshPart.meshFromShape(Shape=shape, MaxLength=tessellation).Topology
|
||||
else:
|
||||
return MeshPart.meshFromShape(Shape=shape, GrowthRate=grading, SegPerEdge=segsperedge,
|
||||
SegPerRadius=segsperradius, SecondOrder=secondorder, Optimize=optimize,
|
||||
AllowQuad=allowquads).Topology
|
||||
return MeshPart.meshFromShape(Shape=shape,
|
||||
GrowthRate=grading,
|
||||
SegPerEdge=segsperedge,
|
||||
SegPerRadius=segsperradius,
|
||||
SecondOrder=secondorder,
|
||||
Optimize=optimize,
|
||||
AllowQuad=allowquads
|
||||
).Topology
|
||||
|
||||
|
||||
def export(exportList, filename, tessellation=1, colors=None):
|
||||
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
|
||||
@@ -254,12 +248,9 @@ def export(exportList, filename, tessellation=1, colors=None):
|
||||
|
||||
FreeCAD.Console.PrintMessage(translate("Arch", "file %s successfully created.") % filename)
|
||||
|
||||
def exportToDAE(path):
|
||||
filename = path + ".dae"
|
||||
|
||||
def exportToPVC(path, exportTerrain = False):
|
||||
filename = path + ".pvc"
|
||||
scale = 0.001 # from millimeters (FreeCAD) to meters (Collada)
|
||||
filename = f"{path}.pvc"
|
||||
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
import datetime
|
||||
@@ -453,7 +444,7 @@ def exportToPVC(path, exportTerrain = False):
|
||||
array = SubElement(axis, 'float_array')
|
||||
array.set('id', 'tracker{0}AxisFloatArray1'.format(ind))
|
||||
array.set('count', '3')
|
||||
array.text = '{0:.6f} {1:.6f} {2:.6f}'.format(centers[i].x, centers[i].y, centers[i].z)
|
||||
array.text = '{0:.6f} {1:.6f} {2:.6f}'.format(centers[ind].x, centers[ind].y, centers[ind].z)
|
||||
|
||||
min_phi = SubElement(frame, 'min_phi')
|
||||
min_phi.text = '{0}'.format(int(obj.Setup.MinPhi.Value))
|
||||
@@ -529,10 +520,10 @@ def exportToPVC(path, exportTerrain = False):
|
||||
frame_setup["footprint"] = ""
|
||||
|
||||
objind = 0
|
||||
|
||||
# TODO: revisar
|
||||
for type in frameType:
|
||||
isTracker = "tracker" in type.Proxy.Type.lower()
|
||||
#TODO: Sólo para los proyectos de NAcho. Borrar
|
||||
for typ in frameType:
|
||||
isTracker = "tracker" in typ.Proxy.Type.lower()
|
||||
isTracker = False
|
||||
|
||||
objectlist = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
@@ -545,7 +536,7 @@ def exportToPVC(path, exportTerrain = False):
|
||||
objectlist = tmp.copy()
|
||||
|
||||
for obj in objectlist:
|
||||
if obj.Setup == type:
|
||||
if obj.Setup == typ:
|
||||
findex = numpy.array([])
|
||||
|
||||
modules = obj.Setup.Shape.SubShapes[0].SubShapes[0]
|
||||
@@ -726,26 +717,26 @@ def exportToH2P(path): # sólo válido para mesas
|
||||
#for obj in objects:
|
||||
grouptype.append(objects[0])
|
||||
|
||||
for type in grouptype:
|
||||
for typ in grouptype:
|
||||
st += 'TABLE\n' \
|
||||
'10\n'
|
||||
st += f3.format(type.Width.Value) + ',' + f3.format(type.Length.Value) + ',' + \
|
||||
st += f3.format(typ.Width.Value) + ',' + f3.format(typ.Length.Value) + ',' + \
|
||||
f3.format(0) + ',' + f3.format(0) + ',' + f3.format(0) + ',' + f3.format(0) + "\n"
|
||||
#'#{ f3 %pvsyst.ilb.to_mm },#{f3 %pvsyst.irb.to_mm},#{f3 %pvsyst.itb.to_mm},' \
|
||||
#'#{f3 %pvsyst.ibb.to_mm}\n'
|
||||
st += '20\n'
|
||||
st += str(int(type.ModulesCols.Value)) + ',' + str(int(type.ModulesRows.Value)) + ',' + \
|
||||
str(type.ModuleColGap.Value) + ',' + str(type.ModuleRowGap.Value) + ',' + '30\n'
|
||||
st += str(int(typ.ModulesCols.Value)) + ',' + str(int(typ.ModulesRows.Value)) + ',' + \
|
||||
str(typ.ModuleColGap.Value) + ',' + str(typ.ModuleRowGap.Value) + ',' + '30\n'
|
||||
st += '30\n'
|
||||
st += '1,' + f3.format(type.ModuleWidth.Value) + ',' + f3.format(type.ModuleHeight.Value) + ',' + \
|
||||
f3.format(type.ModuleThick.Value) + ',' + f2.format(450) + '\n' #f2.format(type.ModulePower.Value) + '\n'
|
||||
st += '1,' + f3.format(typ.ModuleWidth.Value) + ',' + f3.format(typ.ModuleHeight.Value) + ',' + \
|
||||
f3.format(typ.ModuleThick.Value) + ',' + f2.format(450) + '\n' #f2.format(typ.ModulePower.Value) + '\n'
|
||||
|
||||
# cornerdown = find_component_sizes(group.cdef)[1]
|
||||
# pvorigin = Geom::Point3d.new(cornerdown.x, cornerdown.y, 0)
|
||||
# group.instances.each{ | ins | str += pvsyst_insert(ins, pvorigin)}
|
||||
|
||||
for obj in objects:
|
||||
if obj.CloneOf == type:
|
||||
if obj.CloneOf == typ:
|
||||
st += H2PInsert(obj)
|
||||
|
||||
## TODO: Bucle para buscar objetos que den sombra y el terreno. Todos llaman a H2PMesh
|
||||
@@ -778,12 +769,12 @@ def H2PInsert(obj):
|
||||
|
||||
return st
|
||||
|
||||
def H2PMesh(mesh, type):
|
||||
def H2PMesh(mesh, typ):
|
||||
scale = 0.001 ## ver como se puede hacer para que sea general. Pasar de mm a m
|
||||
|
||||
f3 = '{:.3f}'
|
||||
st = ''
|
||||
if type:
|
||||
if typ:
|
||||
st = 'ShadowObject\nFence\n'
|
||||
else:
|
||||
st = 'DGM\n'
|
||||
@@ -799,7 +790,7 @@ def H2PMesh(mesh, type):
|
||||
|
||||
return st
|
||||
|
||||
class _PVSystTaskPanel:
|
||||
class PVSystTaskPanel:
|
||||
|
||||
def __init__(self):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(os.path.dirname(__file__) + "/exportPVSyst.ui")
|
||||
@@ -812,7 +803,7 @@ class _PVSystTaskPanel:
|
||||
def accept(self):
|
||||
import datetime
|
||||
x = datetime.datetime.now()
|
||||
date = x.strftime("%y%m%d%H%M%S")
|
||||
date = x.strftime("%Y%m%d%H%M%S")
|
||||
overwrite = True
|
||||
|
||||
path = os.path.join(os.path.dirname(FreeCAD.ActiveDocument.FileName), "outputs", "PVSyst")
|
||||
@@ -820,12 +811,10 @@ class _PVSystTaskPanel:
|
||||
os.makedirs(path)
|
||||
|
||||
name = FreeCAD.ActiveDocument.Label
|
||||
if not overwrite:
|
||||
name = date + "-" + name
|
||||
#if not overwrite:
|
||||
name = date + "-" + name
|
||||
filename = os.path.join(path, name)
|
||||
|
||||
#if self.form.cbDAE.isChecked():
|
||||
# exportToDAE(filename)
|
||||
|
||||
if self.form.cbPVC.isChecked():
|
||||
exportToPVC(filename, self.form.cbTerrain.isChecked())
|
||||
@@ -840,7 +829,7 @@ class _PVSystTaskPanel:
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
class _CommandExportToPVSyst:
|
||||
'''class _CommandExportToPVSyst:
|
||||
"Export to PVSyst"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -861,4 +850,4 @@ class _CommandExportToPVSyst:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('ExportToPVSyst', _CommandExportToPVSyst())
|
||||
FreeCADGui.addCommand('ExportToPVSyst', _CommandExportToPVSyst())'''
|
||||
|
||||
@@ -20,47 +20,32 @@
|
||||
# * *
|
||||
# ***********************************************************************
|
||||
|
||||
import FreeCAD, Draft
|
||||
import PVPlantSite
|
||||
import copy
|
||||
import FreeCAD
|
||||
import Draft
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import openpyxl
|
||||
from openpyxl.styles import Alignment, Border, Side, Font
|
||||
|
||||
from PVPlantPlacement import getCols
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
from DraftTools import translate
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import Part
|
||||
import pivy
|
||||
from pivy import coin
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "PVPlant Trench"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
from PVPlantResources import DirDocuments as DirDocuments
|
||||
|
||||
|
||||
'''import os
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
def open_file(path):
|
||||
"""Open a file or directory using the default system handler"""
|
||||
if platform.system() == "Windows":
|
||||
os.startfile(path)
|
||||
elif platform.system() == "Darwin":
|
||||
subprocess.Popen(["open", path])
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", path])'''
|
||||
subprocess.Popen(["xdg-open", path])
|
||||
|
||||
|
||||
from PVPlantPlacement import getCols
|
||||
|
||||
914
Importer/importOSM.py
Normal file
914
Importer/importOSM.py
Normal file
@@ -0,0 +1,914 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Part
|
||||
import Draft
|
||||
from xml.etree import ElementTree as ET
|
||||
import ssl
|
||||
import certifi
|
||||
import urllib.request
|
||||
import math
|
||||
import utm
|
||||
from collections import defaultdict
|
||||
import PVPlantImportGrid as ImportElevation
|
||||
|
||||
scale = 1000.0
|
||||
|
||||
|
||||
class OSMImporter:
|
||||
|
||||
def __init__(self, origin):
|
||||
self.Origin = origin if origin else FreeCAD.Vector(0, 0, 0)
|
||||
self.overpass_url = "https://overpass-api.de/api/interpreter"
|
||||
self.nodes = {}
|
||||
self.ways_data = defaultdict(dict)
|
||||
self.feature_colors = {
|
||||
'building': (0.8, 0.8, 0.6),
|
||||
'highway': {
|
||||
'motorway': (1.0, 0.4, 0.4),
|
||||
'trunk': (1.0, 0.6, 0.4),
|
||||
'primary': (1.0, 0.8, 0.4),
|
||||
'secondary': (1.0, 1.0, 0.4),
|
||||
'tertiary': (0.8, 1.0, 0.4),
|
||||
'residential': (0.6, 0.6, 0.6)
|
||||
},
|
||||
'railway': (0.4, 0.4, 0.4),
|
||||
'power': {
|
||||
'line': (0.0, 0.0, 0.0),
|
||||
'tower': (0.3, 0.3, 0.3),
|
||||
'substation': (0.8, 0.0, 0.0)
|
||||
},
|
||||
'vegetation': (0.4, 0.8, 0.4),
|
||||
'water': (0.4, 0.6, 1.0)
|
||||
}
|
||||
self.ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
|
||||
def transform_from_latlon(self, lat, lon):
|
||||
point = ImportElevation.getElevationFromOE([[lat, lon], ])
|
||||
return FreeCAD.Vector(point[0].x, point[0].y, point[0].z) * scale - self.Origin
|
||||
'''x, y, _, _ = utm.from_latlon(lat, lon)
|
||||
return FreeCAD.Vector(x, y, .0) * scale - self.Origin'''
|
||||
|
||||
def get_osm_data(self, bbox):
|
||||
query = f"""
|
||||
[out:xml][bbox:{bbox}];
|
||||
(
|
||||
way["building"];
|
||||
way["highway"];
|
||||
way["railway"];
|
||||
way["power"="line"];
|
||||
way["power"="substation"];
|
||||
way["natural"="water"];
|
||||
way["landuse"="forest"];
|
||||
node["natural"="tree"];
|
||||
);
|
||||
(._;>;);
|
||||
out body;
|
||||
"""
|
||||
req = urllib.request.Request(
|
||||
self.overpass_url,
|
||||
data=query.encode('utf-8'),
|
||||
headers={'User-Agent': 'FreeCAD-OSM-Importer/1.0'},
|
||||
method='POST'
|
||||
)
|
||||
return urllib.request.urlopen(req, context=self.ssl_context, timeout=30).read()
|
||||
|
||||
def create_layer(self, name):
|
||||
if not FreeCAD.ActiveDocument.getObject(name):
|
||||
return FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", name)
|
||||
return FreeCAD.ActiveDocument.getObject(name)
|
||||
|
||||
def process_osm_data(self, osm_data):
|
||||
root = ET.fromstring(osm_data)
|
||||
|
||||
# Almacenar nodos transformados
|
||||
for node in root.findall('node'):
|
||||
self.nodes[node.attrib['id']] = self.transform_from_latlon(
|
||||
float(node.attrib['lat']),
|
||||
float(node.attrib['lon'])
|
||||
)
|
||||
|
||||
# Procesar ways
|
||||
for way in root.findall('way'):
|
||||
way_id = way.attrib['id']
|
||||
self.ways_data[way_id] = {
|
||||
'tags': {tag.attrib['k']: tag.attrib['v'] for tag in way.findall('tag')},
|
||||
'nodes': [nd.attrib['ref'] for nd in way.findall('nd')]
|
||||
}
|
||||
|
||||
print("1. Create Transportations")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_transportation()
|
||||
|
||||
print("2. Create Buildings")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_buildings()
|
||||
|
||||
print("3. Create Power Infrastructure")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_power_infrastructure()
|
||||
|
||||
print("4. Create Vegetation")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_vegetation()
|
||||
|
||||
print("5. Create Water Bodies")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_water_bodies()
|
||||
|
||||
def create_transportation(self):
|
||||
transport_layer = self.create_layer("Transport")
|
||||
|
||||
for way_id, data in self.ways_data.items():
|
||||
tags = data['tags']
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
|
||||
if len(nodes) < 2:
|
||||
continue
|
||||
|
||||
# Carreteras
|
||||
if 'highway' in tags:
|
||||
highway_type = tags['highway']
|
||||
width = {
|
||||
'motorway': 10.0,
|
||||
'trunk': 8.0,
|
||||
'primary': 6.0,
|
||||
'secondary': 5.0,
|
||||
'tertiary': 4.0
|
||||
}.get(highway_type, 3.0)
|
||||
self.create_road(nodes, width, highway_type, transport_layer, tags['name'] if 'name' in tags else "")
|
||||
|
||||
# Vías férreas
|
||||
if 'railway' in tags:
|
||||
self.create_railway(nodes, transport_layer, tags['name'] if 'name' in tags else "")
|
||||
|
||||
def create_road(self, nodes, width, road_type, layer, name=""):
|
||||
points = [n for n in nodes]
|
||||
polyline = Draft.make_wire(points, closed=False, face=False)
|
||||
polyline.Label = f"Road_{name if (name != '') else road_type}"
|
||||
polyline.ViewObject.LineWidth = 2.0
|
||||
polyline.ViewObject.ShapeColor = self.feature_colors['highway'].get(road_type, (0.5, 0.5, 0.5))
|
||||
polyline.addProperty("App::PropertyString", "OSMType", "Metadata", "Tipo de vía").OSMType = road_type
|
||||
polyline.addProperty("App::PropertyFloat", "Width", "Metadata", "Ancho de la vía").Width = width
|
||||
layer.addObject(polyline)
|
||||
|
||||
def create_railway(self, nodes, layer, name=""):
|
||||
points = [n for n in nodes]
|
||||
rail_line = Draft.make_wire(points, closed=False, face=False)
|
||||
rail_line.Label = f"{name if (name != '') else 'Railway'}"
|
||||
rail_line.ViewObject.LineWidth = 1.5
|
||||
rail_line.ViewObject.ShapeColor = self.feature_colors['railway']
|
||||
layer.addObject(rail_line)
|
||||
|
||||
def create_buildings(self):
|
||||
building_layer = self.create_layer("Buildings")
|
||||
for way_id, data in self.ways_data.items():
|
||||
if 'building' not in data['tags']:
|
||||
continue
|
||||
|
||||
print(data)
|
||||
|
||||
tags = data['tags']
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
|
||||
if len(nodes) < 3:
|
||||
continue
|
||||
|
||||
# Calcular altura
|
||||
height = self.get_building_height(tags)
|
||||
|
||||
# Crear polígono base
|
||||
polygon_points = [n for n in nodes]
|
||||
if polygon_points[0] != polygon_points[-1]:
|
||||
polygon_points.append(polygon_points[0])
|
||||
|
||||
try:
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
extruded = face.extrude(FreeCAD.Vector(0, 0, height))# * scale - self.Origin )
|
||||
|
||||
building = building_layer.addObject("Part::Feature", f"Building_{way_id}")
|
||||
building.Shape = extruded
|
||||
building.Label = f"Building ({height}m)"
|
||||
building.ViewObject.ShapeColor = self.feature_colors['building']
|
||||
|
||||
# Metadatos
|
||||
building.addProperty("App::PropertyFloat", "Height", "Metadata", "Altura del edificio").Height = height
|
||||
if 'building:levels' in tags:
|
||||
building.addProperty("App::PropertyInteger", "Levels", "Metadata",
|
||||
"Niveles del edificio").Levels = int(tags['building:levels'])
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error en edificio {way_id}: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def get_building_height(tags):
|
||||
# Lógica de cálculo de altura
|
||||
if 'height' in tags:
|
||||
try:
|
||||
return float(tags['height'].split()[0])
|
||||
except:
|
||||
pass
|
||||
if 'building:levels' in tags:
|
||||
try:
|
||||
return float(tags['building:levels']) * 3.0
|
||||
except:
|
||||
pass
|
||||
return 5.0 # Altura por defecto
|
||||
|
||||
def create_power_infrastructure(self):
|
||||
power_layer = self.create_layer("Power_Infrastructure")
|
||||
|
||||
for way_id, data in self.ways_data.items():
|
||||
tags = data['tags']
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
|
||||
if 'power' in tags:
|
||||
print("\n\n")
|
||||
print(tags)
|
||||
feature_type = tags['power']
|
||||
if feature_type == 'line':
|
||||
print("3.1. Create Power Lines")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_power_line(
|
||||
nodes=nodes,
|
||||
tags=tags,
|
||||
layer=power_layer
|
||||
)
|
||||
|
||||
elif feature_type == 'substation':
|
||||
print("3.1. Create substations")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_substation(
|
||||
way_id=way_id,
|
||||
tags=tags,
|
||||
nodes=nodes,
|
||||
layer=power_layer
|
||||
)
|
||||
|
||||
elif feature_type == 'tower':
|
||||
print("3.1. Create power towers")
|
||||
FreeCADGui.updateGui()
|
||||
self.create_power_tower(
|
||||
position=nodes[0] if nodes else None,
|
||||
tags=tags,
|
||||
layer=power_layer
|
||||
)
|
||||
'''self.create_power_tower_1(
|
||||
way=way_id,
|
||||
layer=power_layer
|
||||
)'''
|
||||
|
||||
def create_power_line(self, nodes, tags, layer):
|
||||
"""Crea líneas de transmisión eléctrica con propiedades técnicas"""
|
||||
try:
|
||||
# Configuración basada en tags
|
||||
line_type = tags.get('line', 'overhead')
|
||||
voltage = self.parse_voltage(tags.get('voltage', '0'))
|
||||
cables = int(tags.get('cables', '3'))
|
||||
material = tags.get('material', 'aluminum')
|
||||
|
||||
# Crear geometría
|
||||
points = [FreeCAD.Vector(*n) for n in nodes]
|
||||
if len(points) < 2:
|
||||
return
|
||||
|
||||
wire = Draft.make_wire(points, closed=False, face=False)
|
||||
wire.Label = f"Power_Line_{int(voltage/1000000)}kV"
|
||||
|
||||
# Propiedades visuales
|
||||
wire.ViewObject.LineWidth = 6 #'''1 + (voltage / 100000)'''
|
||||
color = self.feature_colors['power']['line']
|
||||
wire.ViewObject.ShapeColor = color
|
||||
|
||||
# Propiedades técnicas
|
||||
wire.addProperty("App::PropertyFloat", "Voltage", "PowerLine", "Voltage in volts").Voltage = voltage
|
||||
wire.addProperty("App::PropertyInteger", "Cables", "PowerLine", "Number of conductors").Cables = cables
|
||||
wire.addProperty("App::PropertyString", "Material", "PowerLine", "Conductor material").Material = material
|
||||
wire.addProperty("App::PropertyString", "Type", "PowerLine", "Line type").Type = line_type
|
||||
|
||||
layer.addObject(wire)
|
||||
|
||||
# Añadir torres si es overhead
|
||||
'''if line_type == 'overhead':
|
||||
distance_between_towers = 150 # metros por defecto
|
||||
if 'distance_between_towers' in tags:
|
||||
try:
|
||||
distance_between_towers = float(tags['distance_between_towers'])
|
||||
except:
|
||||
pass
|
||||
|
||||
self.create_power_towers_along_line(
|
||||
points=points,
|
||||
voltage=voltage,
|
||||
distance=distance_between_towers,
|
||||
layer=layer
|
||||
)'''
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creating power line: {str(e)}\n")
|
||||
|
||||
def create_power_towers_along_line(self, points, voltage, distance, layer):
|
||||
"""Crea torres de transmisión a lo largo de la línea"""
|
||||
total_length = 0
|
||||
previous_point = None
|
||||
|
||||
for point in points:
|
||||
if previous_point is not None:
|
||||
segment_length = (point - previous_point).Length
|
||||
num_towers = int(segment_length / distance)
|
||||
|
||||
if num_towers > 0:
|
||||
step = segment_length / num_towers
|
||||
direction = (point - previous_point).normalize()
|
||||
|
||||
for i in range(num_towers):
|
||||
tower_pos = previous_point + (direction * (i * step))
|
||||
self.create_power_tower(
|
||||
position=tower_pos,
|
||||
tags={'voltage': str(voltage)},
|
||||
layer=layer
|
||||
)
|
||||
|
||||
previous_point = point
|
||||
|
||||
def create_power_tower(self, position, tags, layer):
|
||||
"""Crea una torre de transmisión individual"""
|
||||
try:
|
||||
voltage = self.parse_voltage(tags.get('voltage', '0'))
|
||||
|
||||
# Dimensiones basadas en voltaje
|
||||
base_size = 2.0 + (voltage / 100000)
|
||||
height = 25.0 + (voltage / 10000)
|
||||
|
||||
# Geometría de la torre
|
||||
base = Part.makeBox(base_size, base_size, 3.0,
|
||||
FreeCAD.Vector(position.x - base_size / 2, position.y - base_size / 2, 0))
|
||||
mast = Part.makeCylinder(0.5, height, FreeCAD.Vector(position.x, position.y, 3.0))
|
||||
|
||||
# Unir componentes
|
||||
tower = base.fuse(mast)
|
||||
tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Power_Tower")
|
||||
layer.addObject(tower_obj)
|
||||
tower_obj.Shape = tower
|
||||
tower_obj.ViewObject.ShapeColor = self.feature_colors['power']['tower']
|
||||
|
||||
# Añadir propiedades
|
||||
tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Design voltage").Voltage = voltage
|
||||
tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Tower height").Height = height
|
||||
|
||||
# Añadir crossarms (crucetas)
|
||||
for i in range(3):
|
||||
crossarm_height = 3.0 + (i * 5.0)
|
||||
crossarm = Part.makeBox(
|
||||
4.0, 0.2, 0.2,
|
||||
FreeCAD.Vector(position.x - 2.0, position.y - 0.1, crossarm_height)
|
||||
)
|
||||
tower_obj.Shape = tower_obj.Shape.fuse(crossarm)
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creating power tower: {str(e)}\n")
|
||||
|
||||
def create_power_line_simple(self, nodes, layer):
|
||||
# Torres de alta tensión
|
||||
for node in nodes:
|
||||
cylinder = Part.makeCylinder(1.0, 20.0, FreeCAD.Vector(node[0], node[1], 0))# * scale - self.Origin )
|
||||
pole = FreeCAD.ActiveDocument.addObject("Part::Feature", "PowerPole")
|
||||
layer.addObject(pole)
|
||||
pole.Shape = cylinder
|
||||
pole.ViewObject.ShapeColor = self.feature_colors['power']['tower']
|
||||
|
||||
# Líneas eléctricas
|
||||
points = [n for n in nodes]
|
||||
cable = Draft.make_wire(points, closed=False, face=False)
|
||||
cable.ViewObject.LineWidth = 3.0
|
||||
cable.ViewObject.ShapeColor = self.feature_colors['power']['line']
|
||||
layer.addObject(cable)
|
||||
|
||||
def create_power_tower_1(self, way, layer):
|
||||
"""Crea una torre eléctrica según especificaciones OSM"""
|
||||
tags = way['tags']
|
||||
nodes = [self.nodes[ref] for ref in way['nodes'] if ref in self.nodes]
|
||||
|
||||
if not nodes:
|
||||
return
|
||||
|
||||
try:
|
||||
# Obtener parámetros principales
|
||||
structure_type = tags.get('structure', 'lattice')
|
||||
material = tags.get('material', 'steel')
|
||||
height = float(tags.get('height', 30.0)) # Altura en metros
|
||||
voltage = self.parse_voltage(tags.get('voltage', '132000')) # 132kV por defecto
|
||||
lines = int(tags.get('lines', '3'))
|
||||
operator = tags.get('operator', '')
|
||||
tower_name = tags.get('name', f"Tower_{way['id']}")
|
||||
|
||||
# Calcular posición (usar primer nodo)
|
||||
position = FreeCAD.Vector(*nodes[0])
|
||||
|
||||
# Configurar dimensiones basadas en parámetros
|
||||
base_size = self.calculate_base_size(structure_type, height)
|
||||
crossarm_length = self.calculate_crossarm_length(voltage, lines)
|
||||
color = self.get_material_color(material)
|
||||
|
||||
# Crear geometría según tipo de estructura
|
||||
if structure_type == 'lattice':
|
||||
tower = self.create_lattice_tower(height, base_size, crossarm_length)
|
||||
elif structure_type == 'tubular':
|
||||
tower = self.create_tubular_tower(height, base_size, crossarm_length)
|
||||
elif structure_type == 'portal':
|
||||
tower = self.create_portal_tower(height, base_size, crossarm_length)
|
||||
else:
|
||||
tower = self.create_default_tower(height, base_size, crossarm_length)
|
||||
|
||||
# Crear objeto en FreeCAD
|
||||
tower_obj = layer.addObject("Part::Feature", "Power_Tower")
|
||||
tower_obj.Shape = tower
|
||||
tower_obj.ViewObject.ShapeColor = color
|
||||
tower_obj.Placement.Base = position
|
||||
|
||||
# Añadir propiedades técnicas
|
||||
tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical", "Voltaje nominal (V)").Voltage = voltage
|
||||
tower_obj.addProperty("App::PropertyFloat", "Height", "Technical", "Altura total (m)").Height = height
|
||||
tower_obj.addProperty("App::PropertyString", "StructureType", "Technical",
|
||||
"Tipo de estructura").StructureType = structure_type
|
||||
tower_obj.addProperty("App::PropertyString", "Material", "Technical",
|
||||
"Material de construcción").Material = material
|
||||
tower_obj.addProperty("App::PropertyInteger", "Lines", "Technical", "Número de circuitos").Lines = lines
|
||||
if operator:
|
||||
tower_obj.addProperty("App::PropertyString", "Operator", "General", "Operador").Operator = operator
|
||||
|
||||
# Añadir cables si existen nodos de conexión
|
||||
if len(nodes) >= 2:
|
||||
connection_points = [FreeCAD.Vector(*n) for n in nodes]
|
||||
self.create_power_lines_between_towers(connection_points, voltage, layer)
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creando torre {way['id']}: {str(e)}\n")
|
||||
|
||||
def create_lattice_tower(self, height, base_size, crossarm_length):
|
||||
"""Crea torre de celosía tipo armazón"""
|
||||
# Base
|
||||
base = Part.makeBox(base_size, base_size, 3.0)
|
||||
|
||||
# Patas principales
|
||||
leg_profile = Part.makeBox(0.5, 0.5, height)
|
||||
legs = []
|
||||
for x in [-base_size / 2, base_size / 2]:
|
||||
for y in [-base_size / 2, base_size / 2]:
|
||||
leg = leg_profile.copy()
|
||||
leg.translate(FreeCAD.Vector(x, y, 3.0))
|
||||
legs.append(leg)
|
||||
|
||||
# Travesaños horizontales
|
||||
horizontal_bars = []
|
||||
for z in [10.0, height / 2, height - 5.0]:
|
||||
bar = Part.makeBox(base_size + 1.0, 0.3, 0.3, FreeCAD.Vector(-(base_size + 1) / 2, -0.15, z))
|
||||
horizontal_bars.append(bar)
|
||||
|
||||
# Crucetas
|
||||
crossarms = self.create_crossarms(height, crossarm_length)
|
||||
|
||||
# Unir todas las partes
|
||||
tower = base.multiFuse(legs + horizontal_bars + crossarms)
|
||||
return tower.removeSplitter()
|
||||
|
||||
def create_crossarms(self, height, length):
|
||||
"""Crea crucetas para líneas eléctricas"""
|
||||
crossarms = []
|
||||
positions = [
|
||||
(height - 5.0, 0), # Superior
|
||||
(height - 15.0, 22.5), # Media con ángulo
|
||||
(height - 25.0, -22.5) # Inferior con ángulo
|
||||
]
|
||||
|
||||
for z, angle in positions:
|
||||
crossarm = Part.makeBox(length, 0.3, 0.3)
|
||||
crossarm.rotate(FreeCAD.Vector(0, 0, z), FreeCAD.Vector(0, 0, 1), angle)
|
||||
crossarm.translate(FreeCAD.Vector(-length / 2, -0.15, z))
|
||||
crossarms.append(crossarm)
|
||||
|
||||
return crossarms
|
||||
|
||||
def calculate_base_size(self, structure_type, height):
|
||||
"""Calcula tamaño de base según tipo de estructura y altura"""
|
||||
base_sizes = {
|
||||
'lattice': 4.0 + (height * 0.1),
|
||||
'tubular': 3.0 + (height * 0.05),
|
||||
'portal': 6.0,
|
||||
'default': 3.0
|
||||
}
|
||||
return base_sizes.get(structure_type, base_sizes['default'])
|
||||
|
||||
def calculate_crossarm_length(self, voltage, lines):
|
||||
"""Calcula longitud de crucetas según voltaje y número de circuitos"""
|
||||
return (voltage / 100000) * 8.0 + (lines * 2.0)
|
||||
|
||||
def get_material_color(self, material):
|
||||
"""Devuelve color según material de construcción"""
|
||||
colors = {
|
||||
'steel': (0.65, 0.65, 0.7),
|
||||
'concrete': (0.8, 0.8, 0.8),
|
||||
'wood': (0.5, 0.3, 0.2),
|
||||
'aluminum': (0.9, 0.9, 0.9),
|
||||
'default': (0.5, 0.5, 0.5)
|
||||
}
|
||||
return colors.get(material.lower(), colors['default'])
|
||||
|
||||
def create_power_lines_between_towers(self, points, voltage, layer):
|
||||
"""Crea cables entre torres"""
|
||||
cable_thickness = 0.1 + (voltage / 500000)
|
||||
|
||||
for i in range(len(points) - 1):
|
||||
start = points[i]
|
||||
end = points[i + 1]
|
||||
|
||||
# Crear cable curvo (catenaria)
|
||||
cable = self.create_catenary(start, end, sag=5.0)
|
||||
cable_obj = layer.addObject("Part::Feature", f"Power_Line_{i}")
|
||||
cable_obj.Shape = cable
|
||||
cable_obj.ViewObject.LineWidth = cable_thickness * 1000 # Convertir a mm
|
||||
cable_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)
|
||||
|
||||
def create_catenary(self, start, end, sag=5.0):
|
||||
"""Crea curva de catenaria para cables eléctricos"""
|
||||
mid_point = (start + end) / 2
|
||||
mid_point.z -= sag
|
||||
|
||||
arc = Part.Arc(
|
||||
start,
|
||||
mid_point,
|
||||
end
|
||||
)
|
||||
|
||||
return Part.Edge(arc.toShape())
|
||||
|
||||
def create_substation(self, way_id, tags, nodes, layer):
|
||||
"""Crea subestaciones con todos los componentes detallados"""
|
||||
try:
|
||||
# 1. Parámetros base
|
||||
voltage = self.parse_voltage(tags.get('voltage', '0'))
|
||||
substation_type = tags.get('substation', 'distribution')
|
||||
name = tags.get('name', f"Substation_{way_id}")
|
||||
|
||||
if len(nodes) < 3:
|
||||
FreeCAD.Console.PrintWarning(f"Subestación {way_id} ignorada: polígono inválido\n")
|
||||
return
|
||||
|
||||
# 2. Geometría base
|
||||
polygon_points = [n for n in nodes if isinstance(n, FreeCAD.Vector)]
|
||||
if polygon_points[0] != polygon_points[-1]:
|
||||
polygon_points.append(polygon_points[0])
|
||||
|
||||
# 3. Base del terreno
|
||||
base_height = 0.3
|
||||
try:
|
||||
base_shape = Part.makePolygon(polygon_points)
|
||||
base_face = Part.Face(base_shape)
|
||||
base_extrude = base_face.extrude(FreeCAD.Vector(0, 0, base_height))
|
||||
base_obj = layer.addObject("Part::Feature", f"{name}_Base")
|
||||
base_obj.Shape = base_extrude
|
||||
base_obj.ViewObject.ShapeColor = (0.2, 0.2, 0.2)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error base {way_id}: {str(e)}\n")
|
||||
|
||||
# 4. Cercado perimetral
|
||||
if tags.get('fence', 'no') == 'yes':
|
||||
try:
|
||||
fence_offset = -0.8 # metros hacia adentro
|
||||
fence_points = self.offset_polygon(polygon_points, fence_offset)
|
||||
if len(fence_points) > 2:
|
||||
fence_shape = Part.makePolygon(fence_points)
|
||||
fence_face = Part.Face(fence_shape)
|
||||
fence_extrude = fence_face.extrude(FreeCAD.Vector(0, 0, 2.8))
|
||||
fence_obj = layer.addObject("Part::Feature", f"{name}_Fence")
|
||||
fence_obj.Shape = fence_extrude
|
||||
fence_obj.ViewObject.ShapeColor = (0.4, 0.4, 0.4)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Error cerca {way_id}: {str(e)}\n")
|
||||
|
||||
# 5. Edificio principal
|
||||
if tags.get('building', 'no') == 'yes':
|
||||
try:
|
||||
building_offset = -2.0 # metros hacia adentro
|
||||
building_height = 4.5 + (voltage / 100000)
|
||||
building_points = self.offset_polygon(polygon_points, building_offset)
|
||||
if len(building_points) > 2:
|
||||
building_shape = Part.makePolygon(building_points)
|
||||
building_face = Part.Face(building_shape)
|
||||
building_extrude = building_face.extrude(FreeCAD.Vector(0, 0, building_height))
|
||||
building_obj = layer.addObject("Part::Feature", f"{name}_Building")
|
||||
building_obj.Shape = building_extrude
|
||||
building_obj.ViewObject.ShapeColor = (0.7, 0.7, 0.7)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Error edificio {way_id}: {str(e)}\n")
|
||||
|
||||
# 6. Transformadores
|
||||
try:
|
||||
num_transformers = int(tags.get('transformers', 1))
|
||||
for i in range(num_transformers):
|
||||
transformer_pos = self.calculate_equipment_position(
|
||||
polygon_points,
|
||||
index=i,
|
||||
total=num_transformers,
|
||||
offset=3.0
|
||||
)
|
||||
transformer = self.create_transformer(
|
||||
position=transformer_pos,
|
||||
voltage=voltage,
|
||||
tech_type=tags.get('substation:type', 'outdoor')
|
||||
)
|
||||
layer.addObject(transformer)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Error transformadores {way_id}: {str(e)}\n")
|
||||
|
||||
# 7. Torre de seccionamiento para alta tensión
|
||||
if substation_type == 'transmission' and voltage >= 110000:
|
||||
try:
|
||||
tower_pos = self.calculate_tower_position(polygon_points)
|
||||
tower = self.create_circuit_breaker_tower(
|
||||
position=tower_pos,
|
||||
voltage=voltage
|
||||
)
|
||||
layer.addObject(tower)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Error torre {way_id}: {str(e)}\n")
|
||||
|
||||
# 8. Propiedades técnicas
|
||||
substation_data = layer.addObject("App::FeaturePython", f"{name}_Data")
|
||||
props = {
|
||||
"Voltage": voltage,
|
||||
"Type": substation_type,
|
||||
"Components": ['Base'] + (['Fence'] if 'fence' in tags else []) +
|
||||
(['Building'] if 'building' in tags else []) +
|
||||
[f'Transformer_{n + 1}' for n in range(num_transformers)]
|
||||
}
|
||||
for prop, value in props.items():
|
||||
if isinstance(value, list):
|
||||
substation_data.addProperty("App::PropertyStringList", prop, "Technical").Components = value
|
||||
else:
|
||||
substation_data.addProperty(
|
||||
"App::PropertyFloat" if isinstance(value, float) else "App::PropertyString",
|
||||
prop, "Technical").setValue(value)
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error crítico en subestación {way_id}: {str(e)}\n")
|
||||
|
||||
def add_substation_fence(self, parent_obj, polygon_points):
|
||||
"""Añade cerca perimetral"""
|
||||
try:
|
||||
offset_points = self.offset_polygon(polygon_points, -0.5)
|
||||
fence = Part.makePolygon(offset_points).extrude(FreeCAD.Vector(0, 0, 2.5))
|
||||
fence_obj = parent_obj.Document.addObject("Part::Feature", "Fence")
|
||||
fence_obj.Shape = fence
|
||||
fence_obj.ViewObject.ShapeColor = (0.4, 0.4, 0.4)
|
||||
fence_obj.Placement.Base = parent_obj.Placement.Base
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Error en cerca: {str(e)}\n")
|
||||
|
||||
def parse_voltage(self, voltage_str):
|
||||
# Convertir valores comunes de voltaje a numéricos
|
||||
voltage_map = {
|
||||
'low': 400,
|
||||
'medium': 20000,
|
||||
'high': 110000,
|
||||
'extra high': 380000,
|
||||
'ultra high': 765000
|
||||
}
|
||||
try:
|
||||
return float(''.join(filter(str.isdigit, voltage_str.split()[0]))) * 1000
|
||||
except:
|
||||
return 20000 # Valor por defecto
|
||||
|
||||
def offset_polygon(self, points, offset):
|
||||
"""Versión corregida sin error de sintaxis"""
|
||||
if not points:
|
||||
return []
|
||||
|
||||
sum_x = sum(p.x for p in points)
|
||||
sum_y = sum(p.y for p in points)
|
||||
centroid = FreeCAD.Vector(sum_x / len(points), sum_y / len(points), 0)
|
||||
|
||||
return [p + (centroid - p).normalize() * offset for p in points]
|
||||
|
||||
def create_transformer(self, position, voltage, technology):
|
||||
# Crear transformador básico según características
|
||||
height = 2.0 + (voltage / 100000)
|
||||
radius = 0.5 + (voltage / 500000)
|
||||
|
||||
transformer = Part.makeCylinder(radius, height, FreeCAD.Vector(position))
|
||||
transformer_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Transformer")
|
||||
transformer_obj.Shape = transformer
|
||||
transformer_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.5) if 'oil' in technology.lower() else (0.5, 0.5, 0.5)
|
||||
return transformer_obj
|
||||
|
||||
def calculate_equipment_position(self, polygon_points, index, total, offset=0.0):
|
||||
"""Calcula posición equidistante alrededor del perímetro con offset"""
|
||||
perimeter = sum((p2 - p1).Length for p1, p2 in zip(polygon_points, polygon_points[1:]))
|
||||
target_dist = (perimeter / total) * index
|
||||
|
||||
accumulated = 0.0
|
||||
for i in range(len(polygon_points) - 1):
|
||||
p1 = polygon_points[i]
|
||||
p2 = polygon_points[i + 1]
|
||||
segment_length = (p2 - p1).Length
|
||||
if accumulated + segment_length >= target_dist:
|
||||
direction = (p2 - p1).normalize()
|
||||
return p1 + direction * (target_dist - accumulated) + direction.cross(FreeCAD.Vector(0, 0, 1)) * offset
|
||||
accumulated += segment_length
|
||||
return polygon_points[0]
|
||||
|
||||
def create_circuit_breaker_tower(self, position, voltage):
|
||||
"""Crea torre de seccionamiento especializada"""
|
||||
tower = Part.makeCompound([
|
||||
Part.makeCylinder(0.8, 12, position), # Poste principal
|
||||
Part.makeBox(3, 0.3, 0.3, position + FreeCAD.Vector(-1.5, -0.15, 12)), # Cruceta superior
|
||||
Part.makeBox(2.5, 0.3, 0.3, position + FreeCAD.Vector(-1.25, -0.15, 9)) # Cruceta media
|
||||
])
|
||||
tower_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "CircuitBreakerTower")
|
||||
tower_obj.Shape = tower
|
||||
tower_obj.ViewObject.ShapeColor = (0.1, 0.1, 0.1)
|
||||
tower_obj.addProperty("App::PropertyFloat", "Voltage", "Technical").Voltage = voltage
|
||||
return tower_obj
|
||||
|
||||
def calculate_tower_position(self, polygon_points):
|
||||
# Colocar torre en el punto medio del lado más largo
|
||||
# (Implementación compleja que requeriría cálculo geométrico)
|
||||
return FreeCAD.Vector(polygon_points[0].x, polygon_points[0].y, 0)
|
||||
|
||||
|
||||
def create_vegetation(self):
|
||||
vegetation_layer = self.create_layer("Vegetation")
|
||||
|
||||
# Procesar nodos de vegetación individual
|
||||
for way_id, tags in self.ways_data.items():
|
||||
coords = self.nodes.get(way_id)
|
||||
if not coords:
|
||||
continue
|
||||
|
||||
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():
|
||||
tags = data['tags']
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
|
||||
if not nodes or len(nodes) < 3:
|
||||
continue
|
||||
|
||||
if tags.get('natural') == 'wood' or tags.get('landuse') == 'forest':
|
||||
self.create_forest(nodes, tags, vegetation_layer)
|
||||
elif tags.get('natural') == 'grassland':
|
||||
self.create_grassland(nodes, vegetation_layer)
|
||||
elif tags.get('natural') == 'heath':
|
||||
self.create_heathland(nodes, vegetation_layer)
|
||||
elif tags.get('natural') == 'scrub':
|
||||
self.create_scrub_area(nodes, vegetation_layer)
|
||||
|
||||
def create_tree(self, position, tags, layer):
|
||||
"""Crea un árbol individual con propiedades basadas en etiquetas OSM"""
|
||||
height = float(tags.get('height', 10.0))
|
||||
trunk_radius = float(tags.get('circumference', 1.0)) / (2 * math.pi)
|
||||
canopy_radius = float(tags.get('diameter_crown', 4.0)) / 2
|
||||
|
||||
# Crear tronco
|
||||
trunk = Part.makeCylinder(trunk_radius, height, position)
|
||||
|
||||
# Crear copa (forma cónica)
|
||||
canopy_center = position + FreeCAD.Vector(0, 0, height)
|
||||
canopy = Part.makeCone(canopy_radius, canopy_radius * 0.7, canopy_radius * 1.5, canopy_center)
|
||||
|
||||
tree = trunk.fuse(canopy)
|
||||
tree_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree")
|
||||
layer.addObject(tree_obj)
|
||||
tree_obj.Shape = tree
|
||||
tree_obj.ViewObject.ShapeColor = (0.3, 0.6, 0.2) # Verde bosque
|
||||
|
||||
# Añadir metadatos
|
||||
for prop in ['genus', 'species', 'leaf_type', 'height']:
|
||||
if prop in tags:
|
||||
tree_obj.addProperty("App::PropertyString", prop.capitalize(), "Botany",
|
||||
"Botanical property").__setattr__(prop.capitalize(), tags[prop])
|
||||
|
||||
def create_forest(self, nodes, tags, layer):
|
||||
"""Crea un área boscosa con densidad variable"""
|
||||
polygon_points = [FreeCAD.Vector(*n) for n in nodes]
|
||||
if polygon_points[0] != polygon_points[-1]:
|
||||
polygon_points.append(polygon_points[0])
|
||||
|
||||
# Crear base del bosque
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
forest_base = FreeCAD.ActiveDocument.addObject("Part::Feature", "Forest_Base")
|
||||
layer.addObject(forest_base)
|
||||
forest_base.Shape = face
|
||||
forest_base.ViewObject.ShapeColor = (0.15, 0.4, 0.1) # Verde oscuro
|
||||
|
||||
# Generar árboles aleatorios dentro del polígono
|
||||
density = float(tags.get('density', 0.5)) # Árboles por m²
|
||||
area = face.Area
|
||||
num_trees = int(area * density)
|
||||
|
||||
for _ in range(num_trees):
|
||||
rand_point = self.random_point_in_polygon(polygon_points)
|
||||
self.create_tree(rand_point, {}, layer)
|
||||
|
||||
def create_grassland(self, nodes, layer):
|
||||
"""Crea un área de pastizales"""
|
||||
polygon_points = [FreeCAD.Vector(*n) for n in nodes]
|
||||
if polygon_points[0] != polygon_points[-1]:
|
||||
polygon_points.append(polygon_points[0])
|
||||
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
grassland = FreeCAD.ActiveDocument.addObject("Part::Feature", "Grassland")
|
||||
layer.addObject(grassland)
|
||||
grassland.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))
|
||||
grassland.ViewObject.ShapeColor = (0.5, 0.7, 0.3) # Verde pasto
|
||||
|
||||
def create_heathland(self, nodes, layer):
|
||||
"""Crea un área de brezales con vegetación baja"""
|
||||
polygon_points = [FreeCAD.Vector(*n) for n in nodes]
|
||||
if polygon_points[0] != polygon_points[-1]:
|
||||
polygon_points.append(polygon_points[0])
|
||||
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
heath = FreeCAD.ActiveDocument.addObject("Part::Feature", "Heathland")
|
||||
layer.addObject(heath)
|
||||
heath.Shape = face
|
||||
heath.ViewObject.ShapeColor = (0.6, 0.5, 0.4) # Color terroso
|
||||
|
||||
# Añadir arbustos dispersos
|
||||
for _ in range(int(face.Area * 0.1)): # 1 arbusto cada 10m²
|
||||
rand_point = self.random_point_in_polygon(polygon_points)
|
||||
self.create_shrub(rand_point, {}, layer)
|
||||
|
||||
def create_shrub(self, position, tags, layer):
|
||||
"""Crea un arbusto individual"""
|
||||
height = float(tags.get('height', 1.5))
|
||||
radius = float(tags.get('diameter_crown', 1.0)) / 2
|
||||
|
||||
# Crear forma de arbusto (cono invertido)
|
||||
base_center = position + FreeCAD.Vector(0, 0, height / 2)
|
||||
shrub = Part.makeCone(radius, radius * 1.5, height, base_center)
|
||||
|
||||
shrub_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Shrub")
|
||||
layer.addObject(shrub_obj)
|
||||
shrub_obj.Shape = shrub
|
||||
shrub_obj.ViewObject.ShapeColor = (0.4, 0.5, 0.3) # Verde arbusto
|
||||
|
||||
# Añadir metadatos si existen
|
||||
if 'genus' in tags:
|
||||
shrub_obj.addProperty("App::PropertyString", "Genus", "Botany", "Plant genus").Genus = tags['genus']
|
||||
|
||||
def create_tree_stump(self, position, layer):
|
||||
"""Crea un tocón de árbol"""
|
||||
height = 0.4
|
||||
radius = 0.5
|
||||
|
||||
stump = Part.makeCylinder(radius, height, position)
|
||||
stump_obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Tree_Stump")
|
||||
layer.addObject(stump_obj)
|
||||
stump_obj.Shape = stump
|
||||
stump_obj.ViewObject.ShapeColor = (0.3, 0.2, 0.1) # Marrón madera
|
||||
|
||||
def random_point_in_polygon(self, polygon_points):
|
||||
"""Genera un punto aleatorio dentro de un polígono"""
|
||||
min_x = min(p.x for p in polygon_points)
|
||||
max_x = max(p.x for p in polygon_points)
|
||||
min_y = min(p.y for p in polygon_points)
|
||||
max_y = max(p.y for p in polygon_points)
|
||||
|
||||
while True:
|
||||
rand_x = random.uniform(min_x, max_x)
|
||||
rand_y = random.uniform(min_y, max_y)
|
||||
rand_point = FreeCAD.Vector(rand_x, rand_y, 0)
|
||||
|
||||
# Verificar si el punto está dentro del polígono
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
if face.isInside(rand_point, 0.1, True):
|
||||
return rand_point
|
||||
|
||||
def create_water_bodies(self):
|
||||
water_layer = self.create_layer("Water")
|
||||
|
||||
for way_id, data in self.ways_data.items():
|
||||
if 'natural' in data['tags'] and data['tags']['natural'] == 'water':
|
||||
nodes = [self.nodes[ref] for ref in data['nodes'] if ref in self.nodes]
|
||||
if len(nodes) > 2:
|
||||
polygon_points = [n for n in nodes]
|
||||
polygon = Part.makePolygon(polygon_points)
|
||||
face = Part.Face(polygon)
|
||||
water = FreeCAD.ActiveDocument.addObject("Part::Feature", "WaterBody")
|
||||
water_layer.addObject(water)
|
||||
water.Shape = face.extrude(FreeCAD.Vector(0, 0, 0.1))# * scale - self.Origin )
|
||||
water.ViewObject.ShapeColor = self.feature_colors['water']
|
||||
|
||||
111
Importer/module_inserter.py
Normal file
111
Importer/module_inserter.py
Normal file
@@ -0,0 +1,111 @@
|
||||
import os
|
||||
import csv
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
|
||||
class SelectorDialog(QtGui.QDialog):
|
||||
def __init__(self, csv_path, title, parent=None):
|
||||
super(SelectorDialog, self).__init__(parent)
|
||||
self.setWindowTitle(title)
|
||||
self.csv_path = csv_path
|
||||
self.data = []
|
||||
self.brand_filter = ""
|
||||
self.model_filter = ""
|
||||
|
||||
# Cargar datos del CSV
|
||||
self.load_csv_data()
|
||||
|
||||
# Crear widgets
|
||||
self.create_widgets()
|
||||
self.create_layout()
|
||||
self.create_connections()
|
||||
|
||||
def load_csv_data(self):
|
||||
"""Carga los datos desde el archivo CSV"""
|
||||
if os.path.exists(self.csv_path):
|
||||
with open(self.csv_path, 'r') as f:
|
||||
reader = csv.DictReader(f, delimiter=';')
|
||||
self.data = [row for row in reader]
|
||||
|
||||
def get_unique_brands(self):
|
||||
"""Obtiene marcas únicas"""
|
||||
return list(set(row['Marca'] for row in self.data))
|
||||
|
||||
def get_models_by_brand(self, brand):
|
||||
"""Filtra modelos por marca"""
|
||||
return [row['Modelo'] for row in self.data if row['Marca'] == brand]
|
||||
|
||||
def create_widgets(self):
|
||||
self.lbl_brand = QtGui.QLabel("Marca:")
|
||||
self.cb_brand = QtGui.QComboBox()
|
||||
self.cb_brand.addItems(self.get_unique_brands())
|
||||
|
||||
self.lbl_model = QtGui.QLabel("Modelo:")
|
||||
self.cb_model = QtGui.QComboBox()
|
||||
self.update_model_combo()
|
||||
|
||||
self.btn_accept = QtGui.QPushButton("Aceptar")
|
||||
self.btn_cancel = QtGui.QPushButton("Cancelar")
|
||||
|
||||
def create_layout(self):
|
||||
layout = QtGui.QVBoxLayout()
|
||||
form_layout = QtGui.QFormLayout()
|
||||
form_layout.addRow(self.lbl_brand, self.cb_brand)
|
||||
form_layout.addRow(self.lbl_model, self.cb_model)
|
||||
|
||||
button_layout = QtGui.QHBoxLayout()
|
||||
button_layout.addWidget(self.btn_accept)
|
||||
button_layout.addWidget(self.btn_cancel)
|
||||
|
||||
layout.addLayout(form_layout)
|
||||
layout.addLayout(button_layout)
|
||||
self.setLayout(layout)
|
||||
|
||||
def create_connections(self):
|
||||
self.cb_brand.currentIndexChanged.connect(self.update_model_combo)
|
||||
self.btn_accept.clicked.connect(self.accept)
|
||||
self.btn_cancel.clicked.connect(self.reject)
|
||||
|
||||
def update_model_combo(self):
|
||||
brand = self.cb_brand.currentText()
|
||||
models = self.get_models_by_brand(brand)
|
||||
self.cb_model.clear()
|
||||
self.cb_model.addItems(models)
|
||||
|
||||
def get_selected_item(self):
|
||||
brand = self.cb_brand.currentText()
|
||||
model = self.cb_model.currentText()
|
||||
for row in self.data:
|
||||
if row['Marca'] == brand and row['Modelo'] == model:
|
||||
return row
|
||||
return None
|
||||
|
||||
|
||||
def select_modulo():
|
||||
csv_path = "/ruta/a/tu/databases/modulos.csv" # Ajusta esta ruta
|
||||
dialog = SelectorDialog(csv_path, "Seleccionar Módulo")
|
||||
if dialog.exec_():
|
||||
selected = dialog.get_selected_item()
|
||||
print("Módulo seleccionado:", selected) # Aquí puedes agregar la lógica de importación
|
||||
|
||||
|
||||
def select_inversor():
|
||||
csv_path = "/ruta/a/tu/databases/inversores.csv" # Ajusta esta ruta
|
||||
dialog = SelectorDialog(csv_path, "Seleccionar Inversor")
|
||||
if dialog.exec_():
|
||||
selected = dialog.get_selected_item()
|
||||
print("Inversor seleccionado:", selected) # Aquí puedes agregar la lógica de importación
|
||||
|
||||
|
||||
# Crear una barra de herramientas para acceder fácilmente
|
||||
toolbar = QtGui.QToolBar()
|
||||
select_modulo_action = QtGui.QAction("Seleccionar Módulo", toolbar)
|
||||
select_modulo_action.triggered.connect(select_modulo)
|
||||
toolbar.addAction(select_modulo_action)
|
||||
|
||||
select_inversor_action = QtGui.QAction("Seleccionar Inversor", toolbar)
|
||||
select_inversor_action.triggered.connect(select_inversor)
|
||||
toolbar.addAction(select_inversor_action)
|
||||
|
||||
# Agregar la barra de herramientas a FreeCAD
|
||||
Gui.addToolBar(toolbar)
|
||||
172
InitGui.py
172
InitGui.py
@@ -20,93 +20,45 @@
|
||||
# * *
|
||||
# ***********************************************************************
|
||||
|
||||
__title__="FreeCAD Fotovoltaic Power Plant Toolkit"
|
||||
__title__ = "FreeCAD Fotovoltaic Power Plant Toolkit"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "sn"
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import os
|
||||
import FreeCADGui
|
||||
import FreeCAD
|
||||
|
||||
FreeCADGui.updateLocale()
|
||||
|
||||
|
||||
class PVPlantWorkbench (Workbench):
|
||||
import os
|
||||
class PVPlantWorkbench(Workbench):
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
MenuText = "PVPlant"
|
||||
ToolTip = "Workbench for PV design"
|
||||
Icon = str(os.path.join(DirIcons, "icon.svg"))
|
||||
|
||||
def Initialize(self):
|
||||
import sys
|
||||
sys.path.append(r"C:\Users\javie\AppData\Roaming\FreeCAD\Mod")
|
||||
|
||||
# Mias
|
||||
import PVPlantGeoreferencing, PVPlantPlacement, \
|
||||
PVPlantTerrainAnalisys, PVPlantSite, PVPlantImportGrid, PVPlantFence,\
|
||||
PVPlantFoundation, PVPlantCreateTerrainMesh, \
|
||||
PVPlantTreeGenerator, PVPlantBuilding, PVPlantTrench, PVPlantEarthWorks, \
|
||||
PVPlantStringing, \
|
||||
PVPlantPad, PVPlantRoad, PVPlantTerrain, PVPlantManhole, \
|
||||
GraphProfile, Utils.PVPlantTrace,\
|
||||
reload
|
||||
import PVPlantRackChecking
|
||||
#sys.path.append(r"C:\Users\javie\AppData\Roaming\FreeCAD\Mod")
|
||||
sys.path.append(os.path.join(FreeCAD.getUserAppDataDir(), 'Mod'))
|
||||
import PVPlantTools, reload
|
||||
|
||||
from Project.Area import PVPlantArea, PVPlantAreaUtils
|
||||
from Project import ProjectSetup
|
||||
from Export import exportPVSyst, PVPlantBOQMechanical, PVPlantBOQElectrical, PVPlantBOQCivil,\
|
||||
exportDXF
|
||||
from Importer import importDXF
|
||||
self.projectlist = PVPlantTools.projectlist
|
||||
self.projectlist.insert(0, 'Reload')
|
||||
self.projectlist.insert(1, 'Separator')
|
||||
self.framelist = PVPlantTools.pv_mechanical
|
||||
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
|
||||
from Electrical.Cable import PVPlantCable, PVPlantElectricalLine
|
||||
from Electrical.CombinerBox import PVPlantStringBox
|
||||
from Electrical.Inverter import PVPlantInverter
|
||||
|
||||
# A list of command names created in the line above
|
||||
self.projectlist = ["Reload",
|
||||
"PVPlantSite",
|
||||
"PVPlantGeoreferencing",
|
||||
"ProjectSetup",
|
||||
#"ImportGrid",
|
||||
"Terrain",
|
||||
"PointsGroup",
|
||||
"PVPlantCreateTerrainMesh",
|
||||
"PVPlantAreas",
|
||||
"SplitArea",
|
||||
"TerrainAnalisys",
|
||||
"Trenches",
|
||||
"PVPlantEarthworks",
|
||||
"PVPlantPad",
|
||||
"PVPlantRoad",
|
||||
"PVPlantManhole",
|
||||
#"PVPlantFoundation"
|
||||
"GraphTerrainProfile",
|
||||
"Trace",
|
||||
]
|
||||
self.framelist = [
|
||||
"RackType",
|
||||
"PVPlantRackCheck",
|
||||
"Separator",
|
||||
"PVPlantPlacement",
|
||||
"PVPlantAdjustToTerrain",
|
||||
"PVPlantConvertTo",
|
||||
"PVArea"
|
||||
]
|
||||
|
||||
self.objectlist = [
|
||||
"PVPlantTree",
|
||||
"PVPlantBuilding",
|
||||
"PVPlantFenceGroup",
|
||||
]
|
||||
|
||||
self.inportExportlist = ["BOQCivil",
|
||||
"BOQMechanical",
|
||||
"BOQElectrical",
|
||||
"Separator",
|
||||
"exportDXF",
|
||||
#"importDXF",
|
||||
"ExportToPVSyst",
|
||||
]
|
||||
from Export import ExporterCommands
|
||||
self.inportExportlist = ExporterCommands.Exportlist
|
||||
|
||||
self.objectlist = PVPlantTools.objectlist
|
||||
''' [
|
||||
"PVPlantTree",
|
||||
"PVPlantBuilding",
|
||||
"PVPlantFenceGroup",
|
||||
]'''
|
||||
self.electricalList = ["PVPlantStringBox",
|
||||
"PVPlantCable",
|
||||
"PVPlanElectricalLine",
|
||||
@@ -114,60 +66,61 @@ class PVPlantWorkbench (Workbench):
|
||||
"Stringing",
|
||||
"Separator",
|
||||
"StringInverter",
|
||||
]
|
||||
]
|
||||
|
||||
self.roads = ["PVPlantRoad",
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
self.pads = ["PVPlantPad",
|
||||
"Separator"
|
||||
self.pads = ["PVPlantPad",
|
||||
"Separator"
|
||||
]
|
||||
|
||||
# Toolbar
|
||||
self.appendToolbar("Civil", self.projectlist) # creates a new toolbar with your commands
|
||||
self.appendToolbar("PVPlant", self.framelist) # creates a new toolbar with your commands
|
||||
self.appendToolbar("Mechanical", self.framelist) # creates a new toolbar with your commands
|
||||
self.appendToolbar("Shadow", self.objectlist) # creates a new toolbar with your commands
|
||||
self.appendToolbar("Outputs", self.inportExportlist) # creates a new toolbar with your commands
|
||||
self.appendToolbar("Electrical", self.electricalList) # creates a new toolbar with your commands
|
||||
|
||||
# Menu
|
||||
self.appendMenu("&Civil", self.projectlist) # creates a new menu
|
||||
self.appendMenu("&PVPlant", self.framelist) # creates a new menu
|
||||
self.appendMenu("&Mechanical", self.framelist) # creates a new menu
|
||||
self.appendMenu("&Shadow", self.objectlist) # creates a new menu
|
||||
self.appendMenu("&Outputs", self.inportExportlist) # creates a new menu
|
||||
self.appendMenu("&Electrical", self.electricalList) # creates a new menu
|
||||
|
||||
# Draft tools
|
||||
from DraftTools import translate
|
||||
self.drafttools = ["Draft_Line","Draft_Wire","Draft_Circle","Draft_Arc","Draft_Ellipse",
|
||||
"Draft_Polygon","Draft_Rectangle", "Draft_Text",
|
||||
"Draft_Dimension", "Draft_BSpline","Draft_Point",
|
||||
"Draft_Facebinder","Draft_BezCurve","Draft_Label"]
|
||||
self.draftmodtools = ["Draft_Move","Draft_Rotate","Draft_Offset",
|
||||
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
|
||||
"Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
|
||||
"Draft_Clone"]
|
||||
self.draftextratools = ["Draft_WireToBSpline","Draft_ShapeString",
|
||||
"Draft_PathArray","Draft_Mirror","Draft_Stretch"]
|
||||
self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup","Draft_AutoGroup",
|
||||
"Draft_SelectGroup","Draft_SelectPlane",
|
||||
"Draft_ShowSnapBar","Draft_ToggleGrid",]
|
||||
self.draftutils = ["Draft_Heal","Draft_FlipDimension",
|
||||
"Draft_ToggleConstructionMode","Draft_ToggleContinueMode","Draft_Edit",
|
||||
"Draft_Slope","Draft_AddConstruction"]
|
||||
self.snapList = ['Draft_Snap_Lock','Draft_Snap_Midpoint','Draft_Snap_Perpendicular',
|
||||
'Draft_Snap_Grid','Draft_Snap_Intersection','Draft_Snap_Parallel',
|
||||
'Draft_Snap_Endpoint','Draft_Snap_Angle','Draft_Snap_Center',
|
||||
'Draft_Snap_Extension','Draft_Snap_Near','Draft_Snap_Ortho','Draft_Snap_Special',
|
||||
'Draft_Snap_Dimensions','Draft_Snap_WorkingPlane']
|
||||
|
||||
self.drafttools = ["Draft_Line", "Draft_Wire", "Draft_Circle", "Draft_Arc", "Draft_Ellipse",
|
||||
"Draft_Polygon", "Draft_Rectangle", "Draft_Text",
|
||||
"Draft_Dimension", "Draft_BSpline", "Draft_Point",
|
||||
"Draft_Facebinder", "Draft_BezCurve", "Draft_Label"]
|
||||
self.draftmodtools = ["Draft_Move", "Draft_Rotate", "Draft_Offset",
|
||||
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
|
||||
"Draft_Shape2DView", "Draft_Draft2Sketch", "Draft_Array",
|
||||
"Draft_Clone"]
|
||||
self.draftextratools = ["Draft_WireToBSpline", "Draft_ShapeString",
|
||||
"Draft_PathArray", "Draft_Mirror", "Draft_Stretch"]
|
||||
self.draftcontexttools = ["Draft_ApplyStyle", "Draft_ToggleDisplayMode", "Draft_AddToGroup", "Draft_AutoGroup",
|
||||
"Draft_SelectGroup", "Draft_SelectPlane",
|
||||
"Draft_ShowSnapBar", "Draft_ToggleGrid", ]
|
||||
self.draftutils = ["Draft_Heal", "Draft_FlipDimension",
|
||||
"Draft_ToggleConstructionMode", "Draft_ToggleContinueMode", "Draft_Edit",
|
||||
"Draft_Slope", "Draft_AddConstruction"]
|
||||
self.snapList = ['Draft_Snap_Lock', 'Draft_Snap_Midpoint', 'Draft_Snap_Perpendicular',
|
||||
'Draft_Snap_Grid', 'Draft_Snap_Intersection', 'Draft_Snap_Parallel',
|
||||
'Draft_Snap_Endpoint', 'Draft_Snap_Angle', 'Draft_Snap_Center',
|
||||
'Draft_Snap_Extension', 'Draft_Snap_Near', 'Draft_Snap_Ortho', 'Draft_Snap_Special',
|
||||
'Draft_Snap_Dimensions', 'Draft_Snap_WorkingPlane']
|
||||
|
||||
def QT_TRANSLATE_NOOP(scope, text): return text
|
||||
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft tools"), self.drafttools)
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Draft mod tools"), self.draftmodtools)
|
||||
self.appendMenu(QT_TRANSLATE_NOOP("arch", "&Draft"), self.drafttools + self.draftmodtools + self.draftextratools)
|
||||
self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Utilities")], self.draftutils + self.draftcontexttools)
|
||||
self.appendMenu(QT_TRANSLATE_NOOP("arch", "&Draft"),
|
||||
self.drafttools + self.draftmodtools + self.draftextratools)
|
||||
self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Utilities")],
|
||||
self.draftutils + self.draftcontexttools)
|
||||
self.appendMenu([QT_TRANSLATE_NOOP("arch", "&Draft"), QT_TRANSLATE_NOOP("arch", "Snapping")], self.snapList)
|
||||
|
||||
import Part
|
||||
@@ -192,23 +145,24 @@ class PVPlantWorkbench (Workbench):
|
||||
import SelectionObserver
|
||||
import FreeCADGui
|
||||
|
||||
self.observer = SelectionObserver.SelObserver()
|
||||
FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
|
||||
#self.observer = SelectionObserver.SelObserver()
|
||||
#FreeCADGui.Selection.addObserver(self.observer) # installe la fonction en mode resident
|
||||
return
|
||||
|
||||
def Deactivated(self):
|
||||
"This function is executed when the workbench is deactivated"
|
||||
FreeCADGui.Selection.removeObserver(self.observer)
|
||||
|
||||
#FreeCADGui.Selection.removeObserver(self.observer)
|
||||
return
|
||||
|
||||
def ContextMenu(self, recipient):
|
||||
"This is executed whenever the user right-clicks on screen"
|
||||
# "recipient" will be either "view" or "tree"
|
||||
|
||||
#if FreeCAD.activeDraftCommand is None:
|
||||
# if FreeCAD.activeDraftCommand is None:
|
||||
if recipient.lower() == "view":
|
||||
print("Menus en la 'View'")
|
||||
#if FreeCAD.activeDraftCommand is None:
|
||||
# if FreeCAD.activeDraftCommand is None:
|
||||
presel = FreeCADGui.Selection.getPreselection()
|
||||
print(presel.SubElementNames, " - ", presel.PickedPoints)
|
||||
if not presel is None:
|
||||
@@ -239,7 +193,7 @@ class PVPlantWorkbench (Workbench):
|
||||
self.contextMenu.show()
|
||||
'''
|
||||
|
||||
def GetClassName(self):
|
||||
def GetClassName(self):
|
||||
# this function is mandatory if this is a full python workbench
|
||||
return "Gui::PythonWorkbench"
|
||||
|
||||
|
||||
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>
|
||||
@@ -45,24 +45,11 @@ __title__ = "PVPlant Frames"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
|
||||
import os
|
||||
import PVPlantResources
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
def selectFrames():
|
||||
objectlist = list()
|
||||
#FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if hasattr(obj, "Proxy") and obj.Proxy.isDerivedFrom("Frame"):
|
||||
objectlist.append(obj)
|
||||
return objectlist if len(objectlist) > 0 else None
|
||||
|
||||
def makeFrameSetup(name="FrameSetup"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
FrameSetup(obj)
|
||||
ViewProviderFrameSetup(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
class FrameSetup:
|
||||
"A Base Frame Setup Class"
|
||||
def __init__(self, obj):
|
||||
@@ -72,6 +59,7 @@ class FrameSetup:
|
||||
def setProperties(self, obj):
|
||||
''' Definición de Propiedades: '''
|
||||
pl = obj.PropertiesList
|
||||
|
||||
# Modulo: ------------------------------------------------------------------------------------------------------
|
||||
if not "ModuleThick" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
@@ -175,14 +163,15 @@ class FrameSetup:
|
||||
"The facemaker type to use to build the profile of this object")
|
||||
)
|
||||
|
||||
# Frame --------------------------------------------------------------------------------------------------------
|
||||
'''if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
# Poles --------------------------------------------------------------------------------------------------------
|
||||
if not "PoleType" in pl:
|
||||
obj.addProperty("App::PropertyLinkList",
|
||||
"PoleType",
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).Tilt = 30'''
|
||||
)
|
||||
|
||||
# Frame --------------------------------------------------------------------------------------------------------
|
||||
if not "MaxLengthwiseTilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"MaxLengthwiseTilt",
|
||||
@@ -217,268 +206,99 @@ class FrameSetup:
|
||||
)
|
||||
obj.setEditorMode("TotalAreaShape", 1)
|
||||
|
||||
|
||||
class Frame(ArchComponent.Component):
|
||||
"A Base Frame Obcject - Class"
|
||||
|
||||
def __init__(self, obj):
|
||||
# Definición de Variables:
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
self.obj = obj
|
||||
self.setProperties(obj)
|
||||
|
||||
# Does a IfcType exist?
|
||||
obj.IfcType = "Structural Item"
|
||||
obj.setEditorMode("IfcType", 1)
|
||||
|
||||
self.totalAreaShape = None
|
||||
self.changed = True
|
||||
|
||||
def setProperties(self, obj):
|
||||
# Definicion de Propiedades:
|
||||
print("Frame - setProperties")
|
||||
ArchComponent.Component.setProperties(self, obj)
|
||||
|
||||
pl = obj.PropertiesList
|
||||
# Modulo: ------------------------------------------------------------------------------------------------------
|
||||
if not "ModuleThick" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"ModuleThick",
|
||||
"Module",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleThick = 40
|
||||
if not "ModuleWidth" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"ModuleWidth",
|
||||
"Module",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
|
||||
).ModuleWidth = 992
|
||||
if not "ModuleHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"ModuleHeight",
|
||||
"Module",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
|
||||
).ModuleHeight = 1996
|
||||
if not "PoleCableLength" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"PoleCableLength",
|
||||
"Module",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
|
||||
).PoleCableLength = 1200
|
||||
if not "ModulePower" in pl:
|
||||
obj.addProperty("App::PropertyQuantity",
|
||||
"ModulePower",
|
||||
"Module",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The Length of this object")
|
||||
).ModulePower = 400
|
||||
|
||||
# Array de modulos: -------------------------------------------------------------------------------------------
|
||||
if not "ModuleCols" in pl:
|
||||
obj.addProperty("App::PropertyQuantity",
|
||||
"ModuleCols",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleCols = 15
|
||||
|
||||
if not "ModuleRows" in pl:
|
||||
obj.addProperty("App::PropertyQuantity",
|
||||
"ModuleRows",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleRows = 2
|
||||
|
||||
if not "ModuleColGap" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"ModuleColGap",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleColGap = 20
|
||||
|
||||
if not "ModuleRowGap" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"ModuleRowGap",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleRowGap = 20
|
||||
|
||||
if not "ModuleOffsetX" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"ModuleOffsetX",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleOffsetX = 0
|
||||
|
||||
if not "ModuleOffsetY" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"ModuleOffsetY",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleOffsetY = 0
|
||||
|
||||
if not "ModuleOrientation" in pl:
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"ModuleOrientation",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property",
|
||||
"The facemaker type to use to build the profile of this object")
|
||||
).ModuleOrientation = ["Portrait", "Landscape"]
|
||||
|
||||
if not "ModuleViews" in pl:
|
||||
obj.addProperty("App::PropertyBool",
|
||||
"ModuleViews",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property",
|
||||
"The facemaker type to use to build the profile of this object")
|
||||
).ModuleViews = True
|
||||
'''
|
||||
if not "Modules" in pl:
|
||||
obj.addProperty("App::PropertyLinkSubListChild",
|
||||
"Modules",
|
||||
"Posicion de modulos",
|
||||
QT_TRANSLATE_NOOP("App::Property",
|
||||
"The facemaker type to use to build the profile of this object")
|
||||
).ModuleViews = True
|
||||
'''
|
||||
|
||||
# Frame --------------------------------------------------------------------------------------------------------
|
||||
if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).Tilt = 30
|
||||
|
||||
if not "MaxLengthwiseTilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"MaxLengthwiseTilt",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Máxima inclinación longitudinal")
|
||||
).MaxLengthwiseTilt = 15
|
||||
|
||||
# Frame outputs -----------------------------
|
||||
if not "Width" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"Width",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property",
|
||||
"Ancho de la estructura")
|
||||
)
|
||||
obj.setEditorMode("Width", 1)
|
||||
|
||||
if not "Length" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"Length",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property",
|
||||
"Largo de la estructura")
|
||||
)
|
||||
obj.setEditorMode("Length", 1)
|
||||
|
||||
if not "TotalArea" in pl:
|
||||
obj.addProperty("App::PropertyArea",
|
||||
"TotalArea",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Area total de los Paneles")
|
||||
)
|
||||
obj.setEditorMode("TotalArea", 1)
|
||||
|
||||
|
||||
# Neighbours:
|
||||
|
||||
if not "North" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"North",
|
||||
"Neighbours",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Area total de los Paneles")
|
||||
)
|
||||
|
||||
if not "South" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"South",
|
||||
"Neighbours",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Area total de los Paneles")
|
||||
)
|
||||
|
||||
if not "East" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"East",
|
||||
"Neighbours",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Area total de los Paneles")
|
||||
)
|
||||
|
||||
if not "West" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"West",
|
||||
"Neighbours",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Area total de los Paneles")
|
||||
)
|
||||
|
||||
|
||||
|
||||
'''# Placement
|
||||
if not "Route" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"Route",
|
||||
"Placement",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Total Area de los Paneles")
|
||||
)
|
||||
obj.setEditorMode("Route", 1)
|
||||
|
||||
if not "RouteSection" in pl:
|
||||
obj.addProperty("App::PropertyIntegerConstraint",
|
||||
"RouteSection",
|
||||
"Placement",
|
||||
QT_TRANSLATE_NOOP("Part::PropertyPartShape",
|
||||
"Total Area de los Paneles")
|
||||
)
|
||||
obj.setEditorMode("RouteSection", 1)'''
|
||||
|
||||
self.Type = "Frame"
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
if prop == "North":
|
||||
if obj.getPropertyByName(prop):
|
||||
obj.getPropertyByName("Route").South = obj
|
||||
|
||||
if (prop == "Route"):
|
||||
if obj.getPropertyByName(prop):
|
||||
obj.RouteSection = (1, 1, len(obj.getPropertyByName("Route").Shape.Edges) + 1, 1)
|
||||
|
||||
def getTotalAreaShape(self):
|
||||
return self.totalAreaShape
|
||||
|
||||
|
||||
''' ------------------------------------------- Fixed Structure --------------------------------------------------- '''
|
||||
|
||||
|
||||
def makeRack(name="Rack"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
_FixedRack(obj)
|
||||
_ViewProviderFixedRack(obj.ViewObject)
|
||||
# FreeCAD.ActiveDocument.recompute()
|
||||
obj.Label = name
|
||||
FixedRack(obj)
|
||||
ViewProviderFixedRack(obj.ViewObject)
|
||||
return obj
|
||||
|
||||
|
||||
class _FixedRack(Frame):
|
||||
class FixedRack(FrameSetup):
|
||||
"A Fixed Rack Obcject"
|
||||
|
||||
def __init__(self, obj):
|
||||
# Definición de Variables:
|
||||
Frame.__init__(self, obj)
|
||||
Frame.setProperties(self, obj)
|
||||
#FrameSetup.__init__(self, obj)
|
||||
super(FixedRack, self).__init__(obj)
|
||||
self.setProperties(obj)
|
||||
obj.ModuleColumns = 6
|
||||
obj.ModuleRows = 2
|
||||
obj.ModuleColGap = 20
|
||||
obj.ModuleRowGap = 20
|
||||
#obj.Tilt = 30
|
||||
# Does a IfcType exist?
|
||||
# obj.IfcType = "Fence"
|
||||
# obj.MoveWithHost = False
|
||||
|
||||
def setProperties(self, obj):
|
||||
FrameSetup.setProperties(self, obj)
|
||||
pl = obj.PropertiesList
|
||||
if not "ModuleElevation" in pl:
|
||||
obj.addProperty("App::PropertyDistance",
|
||||
"ModuleElevation",
|
||||
"ModuleArray",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).ModuleElevation = 500
|
||||
|
||||
# Frame: ------------------------------------------------------------------------------------------------------
|
||||
if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).Tilt = 0
|
||||
|
||||
if not "FrameType" in pl:
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"FrameType",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property",
|
||||
"The facemaker type to use to build the profile of this object")
|
||||
).FrameType = ["Simple", "West-Lest"]
|
||||
|
||||
|
||||
# Pole: ------------------------------------------------------------------------------------------------------
|
||||
if not "BackPostWidth" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"BackPostWidth",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).BackPostWidth = 80
|
||||
|
||||
if not "BackPostHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"BackPostHeight",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).BackPostHeight = 160
|
||||
|
||||
if not "BackPostLength" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"BackPostLength",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).BackPostLength = 3200
|
||||
|
||||
if not "FrontPostWidth" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"FrontPostWidth",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).FrontPostWidth = 40
|
||||
|
||||
if not "FrontPostHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"FrontPostHeight",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).FrontPostHeight = 80
|
||||
|
||||
if not "FrontPostLength" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"FrontPostLength",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).FrontPostLength = 2300
|
||||
|
||||
# Array of Posts: ------------------------------------------------------------------------------------------------------
|
||||
if not "NumberPostsX" in pl:
|
||||
@@ -486,7 +306,7 @@ class _FixedRack(Frame):
|
||||
"NumberPostsX",
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).NumberPostsX = 5
|
||||
).NumberPostsX = 3
|
||||
|
||||
if not "DistancePostsX" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
@@ -515,6 +335,7 @@ class _FixedRack(Frame):
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).RammingDeep = 1500
|
||||
|
||||
# Correas: ----------------------------------------------------------------------------------------------------
|
||||
if not "BeamHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
@@ -545,9 +366,11 @@ class _FixedRack(Frame):
|
||||
).BeamSpacing = 1000
|
||||
|
||||
self.Type = "Fixed Rack"
|
||||
obj.Proxy = self
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
#ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
super(FixedRack, self).onDocumentRestored(obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
@@ -557,153 +380,64 @@ class _FixedRack(Frame):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
'''
|
||||
def addObject(self, child):
|
||||
print("addObject")
|
||||
|
||||
def removeObject(self, child):
|
||||
print("removeObject")
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
|
||||
'''
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
'''Do something when a property has changed'''
|
||||
# FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n")
|
||||
|
||||
def CalculateModuleArray(self, obj, totalh, totalw, moduleh, modulew):
|
||||
# ModuleThick
|
||||
# ModuleWidth
|
||||
# ModuleThinness
|
||||
# ModuleColor
|
||||
# ModuleCols
|
||||
# ModuleRows
|
||||
# ModuleColGap
|
||||
# ModuleRowGap
|
||||
|
||||
# BeamHeight
|
||||
# BeamWidth
|
||||
# BeamOffset
|
||||
|
||||
import Part
|
||||
|
||||
# pl = obj.Placement
|
||||
module = Part.makeBox(modulew, moduleh, obj.ModuleThick.Value)
|
||||
correa = Part.makeBox(totalw + obj.BeamOffset.Value * 2, obj.BeamWidth.Value, obj.BeamHeight.Value)
|
||||
|
||||
# Longitud poste - Produndidad de hincado + correas
|
||||
correaoffsetz = obj.BackPostLength.Value
|
||||
offsetz = correaoffsetz + obj.BeamHeight.Value
|
||||
|
||||
p1 = FreeCAD.Vector(0, 0, 0)
|
||||
p2 = FreeCAD.Vector(totalw, 0, 0)
|
||||
p3 = FreeCAD.Vector(totalw, totalh, 0)
|
||||
p4 = FreeCAD.Vector(0, totalh, 0)
|
||||
totalArea = Part.Face(Part.makePolygon([p1, p2, p3, p4, p1]))
|
||||
|
||||
totalArea = Part.makePlane(totalw, totalh)
|
||||
totalArea.Placement.Base.x = -totalw / 2
|
||||
totalArea.Placement.Base.y = -totalh / 2
|
||||
totalArea.Placement.Base.z = obj.BeamHeight.Value
|
||||
|
||||
self.ModuleArea = totalArea
|
||||
# if ShowtotalArea:
|
||||
self.ListModules.append(totalArea)
|
||||
|
||||
correaoffsety = (moduleh - obj.BeamSpacing.Value - obj.BeamWidth.Value) / 2
|
||||
def generate_modules(self, obj, h, w):
|
||||
modulos=[]
|
||||
for y in range(int(obj.ModuleRows.Value)):
|
||||
for x in range(int(obj.ModuleCols.Value)):
|
||||
xx = totalArea.Placement.Base.x + (modulew + obj.ModuleColGap.Value) * x
|
||||
yy = totalArea.Placement.Base.y + (moduleh + obj.ModuleRowGap.Value) * y
|
||||
zz = obj.BeamHeight.Value
|
||||
moduleCopy = module.copy()
|
||||
moduleCopy.Placement.Base.x = xx
|
||||
moduleCopy.Placement.Base.y = yy
|
||||
moduleCopy.Placement.Base.z = zz
|
||||
self.ListModules.append(moduleCopy)
|
||||
for x in range(int(obj.ModuleColumns.Value)):
|
||||
mod = Part.makeBox(w, h, obj.ModuleThick.Value)
|
||||
mod.Placement.Base = FreeCAD.Vector(
|
||||
x * (w + obj.ModuleColGap.Value),
|
||||
y * (h + obj.ModuleRowGap.Value),
|
||||
0
|
||||
)
|
||||
modulos.append(mod)
|
||||
|
||||
compound = Part.makeCompound(self.ListModules)
|
||||
compound.Placement.Base.z = correaoffsetz - obj.RammingDeep.Value
|
||||
compound = Part.makeCompound(modulos)
|
||||
'''center = FreeCAD.Vector(compound.BoundBox.Center)
|
||||
for mod in modulos:
|
||||
mod.Placement.Base -= FreeCAD.Vector(center.x, center.y, 0)
|
||||
compound = Part.makeCompound(modulos)'''
|
||||
return compound
|
||||
|
||||
if obj.FrontPost:
|
||||
base = FreeCAD.Vector(0, (-obj.BackPostHeight.Value + obj.DistancePostsY.Value) / 2,
|
||||
correaoffsetz - obj.RammingDeep.Value)
|
||||
else:
|
||||
base = FreeCAD.Vector(0, -obj.BackPostHeight.Value / 2 + totalh / 3,
|
||||
correaoffsetz - obj.RammingDeep.Value)
|
||||
|
||||
a = compound.rotate(base, FreeCAD.Vector(1, 0, 0), obj.Tilt)
|
||||
|
||||
del correa
|
||||
del module
|
||||
del compound
|
||||
del self.ListModules[:]
|
||||
|
||||
self.ListModules.append(a)
|
||||
|
||||
def CalculatePosts(self, obj, totalh, totalw):
|
||||
'''
|
||||
# NumberPostsX
|
||||
# NumberPostsY
|
||||
# PostHeight
|
||||
# PostWidth
|
||||
# PostLength
|
||||
# DistancePostsX
|
||||
# DistancePostsY
|
||||
'''
|
||||
|
||||
def generate_posts(self, obj):
|
||||
postBack = Part.makeBox(obj.BackPostWidth.Value, obj.BackPostHeight.Value, obj.BackPostLength.Value)
|
||||
postFront = Part.makeBox(obj.FrontPostWidth.Value, obj.FrontPostHeight.Value, obj.FrontPostLength.Value)
|
||||
|
||||
angle = obj.Placement.Rotation.toEuler()[1]
|
||||
offsetX = (-obj.DistancePostsX.Value * (obj.NumberPostsX.Value - 1)) / 2
|
||||
|
||||
# offsetY = (-obj.PostHeight.Value - obj.DistancePostsY * (obj.NumberPostsY - 1)) / 2
|
||||
if obj.FrontPost:
|
||||
offsetY = (-obj.BackPostHeight.Value + obj.DistancePostsY.Value) / 2
|
||||
else:
|
||||
# TODO: cambiar el totalh / 3 por el valor adecuado
|
||||
offsetY = -obj.BackPostHeight.Value / 2 + totalh / 3
|
||||
|
||||
offsetZ = -obj.RammingDeep.Value
|
||||
|
||||
post_back = []
|
||||
post_front = []
|
||||
for x in range(int(obj.NumberPostsX.Value)):
|
||||
xx = offsetX + (
|
||||
obj.NumberPostsX.Value + obj.DistancePostsX.Value) * x # * math.cos(obj.Placement.Rotation.toEuler()[1])
|
||||
yy = offsetY
|
||||
zz = offsetZ # * math.sin(obj.Placement.Rotation.toEuler()[1])
|
||||
|
||||
postCopy = postBack.copy()
|
||||
postCopy.Placement.Base.x = xx
|
||||
postCopy.Placement.Base.y = yy
|
||||
postCopy.Placement.Base.z = zz
|
||||
base = FreeCAD.Vector(xx, yy, obj.BackPostHeight.Value - offsetZ)
|
||||
postCopy = postCopy.rotate(base, FreeCAD.Vector(0, 1, 0), -angle)
|
||||
self.ListPosts.append(postCopy)
|
||||
postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, obj.DistancePostsY.Value, 0)
|
||||
post_back.append(postCopy)
|
||||
|
||||
if obj.FrontPost:
|
||||
postCopy = postFront.copy()
|
||||
postCopy.Placement.Base.x = xx
|
||||
postCopy.Placement.Base.y = -yy
|
||||
postCopy.Placement.Base.z = zz
|
||||
base = FreeCAD.Vector(xx, yy, obj.FrontPostHeight.Value - offsetZ)
|
||||
postCopy = postCopy.rotate(base, FreeCAD.Vector(0, 1, 0), -angle)
|
||||
self.ListPosts.append(postCopy)
|
||||
postCopy.Placement.Base = FreeCAD.Vector(x * obj.DistancePostsX.Value, 0, 0)
|
||||
post_front.append(postCopy)
|
||||
|
||||
del postBack
|
||||
del postFront
|
||||
return Part.makeCompound([Part.makeCompound(post_back), Part.makeCompound(post_front)])
|
||||
|
||||
def correct_placement(self, obj):
|
||||
center = FreeCAD.Vector(obj.BoundBox.Center)
|
||||
obj.Placement.Base -= FreeCAD.Vector(center.x, center.y, 0)
|
||||
|
||||
def execute(self, obj):
|
||||
# obj.Shape: compound
|
||||
# |- Modules and Beams: compound
|
||||
# |-- Modules array: compound
|
||||
# |--- Modules: solid
|
||||
# |-- Beams: compound
|
||||
# |--- MainBeam: solid
|
||||
# |--- Secundary Beams: solid (if exist)
|
||||
# |- Poles array: compound
|
||||
# |-- Poles: solid
|
||||
|
||||
pl = obj.Placement
|
||||
|
||||
del self.ListModules[:]
|
||||
del self.ListPosts[:]
|
||||
|
||||
self.ListModules = []
|
||||
self.ListPosts = []
|
||||
|
||||
if obj.ModuleOrientation == "Portrait":
|
||||
w = obj.ModuleWidth.Value
|
||||
h = obj.ModuleHeight.Value
|
||||
@@ -712,63 +446,38 @@ class _FixedRack(Frame):
|
||||
w = obj.ModuleHeight.Value
|
||||
|
||||
totalh = h * obj.ModuleRows + obj.ModuleRowGap.Value * (obj.ModuleRows - 1)
|
||||
totalw = w * obj.ModuleCols + obj.ModuleColGap.Value * (obj.ModuleCols - 1)
|
||||
obj.Width = totalw
|
||||
obj.Length = totalh
|
||||
totalw = w * obj.ModuleColumns + obj.ModuleColGap.Value * (obj.ModuleColumns - 1)
|
||||
|
||||
# hacer el shape:
|
||||
self.CalculateModuleArray(obj, totalh, totalw, h, w)
|
||||
self.CalculatePosts(obj, totalh, totalw)
|
||||
modules = self.generate_modules(obj, h, w)
|
||||
modules.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), obj.Tilt.Value)
|
||||
self.correct_placement(modules)
|
||||
modules.Placement.Base += FreeCAD.Vector(0, 0, obj.ModuleElevation.Value)
|
||||
posts = self.generate_posts(obj)
|
||||
self.correct_placement(posts)
|
||||
posts.Placement.Base -= FreeCAD.Vector(0, 0, obj.RammingDeep.Value)
|
||||
|
||||
allShapes = []
|
||||
allShapes.extend(self.ListModules)
|
||||
allShapes.extend(self.ListPosts)
|
||||
compound = Part.makeCompound(allShapes)
|
||||
compound = Part.makeCompound([modules, posts])
|
||||
obj.Shape = compound
|
||||
obj.Placement = pl
|
||||
|
||||
angle = obj.Placement.Rotation.toEuler()[1]
|
||||
if angle > obj.MaxLengthwiseTilt:
|
||||
obj.ViewObject.ShapeColor = (1.0, 0.0, 0.0)
|
||||
print("fin")
|
||||
|
||||
obj.Width = totalw
|
||||
obj.Length = totalh
|
||||
|
||||
class _ViewProviderFixedRack(ArchComponent.ViewProviderComponent):
|
||||
class ViewProviderFixedRack:
|
||||
"A View Provider for the Pipe object"
|
||||
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
'''
|
||||
ArchSite._ViewProviderSite.__init__(self, vobj)
|
||||
vobj.Proxy = self
|
||||
vobj.addExtension("Gui::ViewProviderGroupExtensionPython", self)
|
||||
self.setProperties(vobj)
|
||||
'''
|
||||
|
||||
def getIcon(self):
|
||||
""" Return the path to the appropriate icon. """
|
||||
return str(os.path.join(DirIcons, "solar-fixed.svg"))
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
"""Method called when the document requests the object to enter edit mode.
|
||||
|
||||
Edit mode is entered when a user double clicks on an object in the tree
|
||||
view, or when they use the menu option [Edit -> Toggle Edit Mode].
|
||||
|
||||
Just display the standard Arch component task panel.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mode: int or str
|
||||
The edit mode the document has requested. Set to 0 when requested via
|
||||
a double click or [Edit -> Toggle Edit Mode].
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
If edit mode was entered.
|
||||
"""
|
||||
|
||||
if (mode == 0) and hasattr(self, "Object"):
|
||||
taskd = _FixedRackTaskPanel(self.Object)
|
||||
taskd.obj = self.Object
|
||||
@@ -783,15 +492,7 @@ class _FixedRackTaskPanel:
|
||||
# -------------------------------------------------------------------------------------------------------------
|
||||
# Module widget form
|
||||
# -------------------------------------------------------------------------------------------------------------
|
||||
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.ui")
|
||||
self.formRack.widgetTracker.setVisible(False)
|
||||
self.formRack.comboFrameType.currentIndexChanged.connect(self.selectionchange)
|
||||
|
||||
self.formPiling = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantRackFixedPiling.ui")
|
||||
self.formPiling.editBreadthwaysNumOfPost.valueChanged.connect(self.editBreadthwaysNumOfPostChange)
|
||||
self.formPiling.editAlongNumOfPost.valueChanged.connect(self.editAlongNumOfPostChange)
|
||||
|
||||
self.form = [self.formRack, self.formPiling]
|
||||
self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "Mechanical/Frame/PVPlantFixedFrame.ui")
|
||||
|
||||
def selectionchange(self, i):
|
||||
vis = False
|
||||
@@ -839,7 +540,6 @@ def makeTrackerSetup(name="TrackerSetup"):
|
||||
pass
|
||||
return obj
|
||||
|
||||
|
||||
def getarray(array, numberofpoles):
|
||||
if len(array) == 0:
|
||||
newarray = [0] * numberofpoles
|
||||
@@ -899,36 +599,15 @@ class TrackerSetup(FrameSetup):
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).UseGroupsOfModules = False
|
||||
|
||||
# Poles: ------------------------------------------------------------------------------------------------------
|
||||
#TODO: cambiar todo esto por una lista de objetos??
|
||||
'''if not "PoleHeight" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"PoleHeight",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).PoleHeight = 160
|
||||
|
||||
if not "PoleWidth" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"PoleWidth",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The width of this object")
|
||||
).PoleWidth = 80
|
||||
|
||||
if not "PoleLength" in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"PoleLength",
|
||||
"Pole",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).PoleLength = 3000'''
|
||||
|
||||
# Array of Posts: ------------------------------------------------------------------------------------------------------
|
||||
'''movido a la clase madre
|
||||
if not "PoleType" in pl:
|
||||
obj.addProperty("App::PropertyLinkList",
|
||||
"PoleType",
|
||||
"Poles",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
)
|
||||
)'''
|
||||
|
||||
if not "PoleSequence" in pl:
|
||||
obj.addProperty("App::PropertyIntegerList",
|
||||
@@ -1272,6 +951,7 @@ class TrackerSetup(FrameSetup):
|
||||
modules = self.CalculateModuleArray(obj, totalh, totalw, h, w)
|
||||
beams = self.calculateBeams(obj, totalh, totalw, h, w)
|
||||
poles, poleaxis = self.CalculatePosts(obj, totalh, totalw)
|
||||
|
||||
compound = Part.makeCompound([modules, beams])
|
||||
compound.Placement.Base.z = obj.MainBeamAxisPosition.Value - (obj.MainBeamHeight.Value / 2)
|
||||
obj.Shape = Part.makeCompound([compound, Part.makeCompound([poles, poleaxis])])
|
||||
@@ -1279,6 +959,7 @@ class TrackerSetup(FrameSetup):
|
||||
obj.Length = max(obj.Shape.BoundBox.XLength, obj.Shape.BoundBox.YLength)
|
||||
obj.TotalPower = obj.ModulePower.Value * obj.ModuleRows * obj.ModuleColumns
|
||||
|
||||
|
||||
class ViewProviderTrackerSetup:
|
||||
"A View Provider for the TrackerSetup object"
|
||||
|
||||
@@ -1297,6 +978,7 @@ class ViewProviderTrackerSetup:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def makeTracker(name = "Tracker", setup = None):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tracker")
|
||||
obj.Label = name
|
||||
@@ -1306,6 +988,7 @@ def makeTracker(name = "Tracker", setup = None):
|
||||
obj.Setup = setup
|
||||
return obj
|
||||
|
||||
|
||||
class Tracker(ArchComponent.Component):
|
||||
"A 1 Axis single row Tracker Obcject"
|
||||
|
||||
@@ -1461,95 +1144,7 @@ class ViewProviderTracker(ArchComponent.ViewProviderComponent):
|
||||
|
||||
return False
|
||||
|
||||
class dualRowTracker(ArchComponent.Component):
|
||||
"A 1 Axis Dual Row Tracker Obcject"
|
||||
|
||||
def __init__(self, obj):
|
||||
# Definición de Variables:
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
self.setProperties(obj)
|
||||
self.Type = None
|
||||
self.oldTilt = 0
|
||||
self.oldRotation = None
|
||||
|
||||
def setProperties(self, obj):
|
||||
pl = obj.PropertiesList
|
||||
|
||||
if not "MotorPosition" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"MotorPosition",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
)
|
||||
|
||||
if not "MotorRow" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"MotorRow",
|
||||
"Frame",
|
||||
"The height of this object"
|
||||
)
|
||||
|
||||
if not "DrivenRow" in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"DrivenRow",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
)
|
||||
|
||||
if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).Tilt = 0
|
||||
|
||||
if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
)
|
||||
|
||||
if not "Tilt" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"Tilt",
|
||||
"Frame",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
)
|
||||
|
||||
if not "AngleX" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"AngleX",
|
||||
"Outputs",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).AngleX = 0
|
||||
|
||||
if not "AngleY" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"AngleY",
|
||||
"Outputs",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).AngleX = 0
|
||||
|
||||
if not "AngleZ" in pl:
|
||||
obj.addProperty("App::PropertyAngle",
|
||||
"AngleZ",
|
||||
"Outputs",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of this object")
|
||||
).AngleX = 0
|
||||
|
||||
self.Type = "Tracker"
|
||||
# obj.Type = self.Type
|
||||
obj.Proxy = self
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def execute(self, obj):
|
||||
''' '''
|
||||
|
||||
|
||||
class _TrackerTaskPanel:
|
||||
def __init__(self, obj=None):
|
||||
|
||||
@@ -1657,7 +1252,7 @@ class _FrameTaskPanel:
|
||||
return True
|
||||
|
||||
|
||||
class _CommandFixedRack:
|
||||
class CommandFixedRack:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1672,11 +1267,11 @@ class _CommandFixedRack:
|
||||
|
||||
def Activated(self):
|
||||
obj = makeRack()
|
||||
self.TaskPanel = _FixedRackTaskPanel(obj)
|
||||
FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
#self.TaskPanel = _FixedRackTaskPanel(obj)
|
||||
#FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
return
|
||||
|
||||
class _CommandTrackerSetup:
|
||||
class CommandTrackerSetup:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1687,6 +1282,7 @@ class _CommandTrackerSetup:
|
||||
"Creates a TrackerSetup object from setup dialog.")}
|
||||
|
||||
def IsActive(self):
|
||||
return True
|
||||
return (not FreeCAD.ActiveDocument is None and
|
||||
not FreeCAD.ActiveDocument.getObject("Site") is None)
|
||||
|
||||
@@ -1696,7 +1292,7 @@ class _CommandTrackerSetup:
|
||||
FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
return
|
||||
|
||||
class _CommandTracker:
|
||||
class CommandTracker:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1720,51 +1316,3 @@ class _CommandTracker:
|
||||
break
|
||||
makeTracker(setup=setupobj)
|
||||
return
|
||||
|
||||
class _CommandMultiRowTracker:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "solar-tracker.svg")),
|
||||
'MenuText': "Multi-row Tracker",
|
||||
'Accel': "R, M",
|
||||
'ToolTip': "Creates a multi-row Tracker object from trackers."}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
if FreeCADGui.Selection.getCompleteSelection():
|
||||
for ob in FreeCAD.ActiveDocument.Objects:
|
||||
if ob.Name[:4] == "Site":
|
||||
return True
|
||||
|
||||
def Activated(self):
|
||||
self.TaskPanel = _FixedRackTaskPanel()
|
||||
FreeCADGui.Control.showDialog(self.TaskPanel)
|
||||
return
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
class CommandRackGroup:
|
||||
|
||||
def GetCommands(self):
|
||||
return tuple(['PVPlantFixedRack',
|
||||
'PVPlantTrackerSetup',
|
||||
'PVPlantTracker'
|
||||
])
|
||||
|
||||
def GetResources(self):
|
||||
return {'MenuText': QT_TRANSLATE_NOOP("", 'Rack Types'),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("", 'Rack Types')
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
|
||||
FreeCADGui.addCommand('PVPlantFixedRack', _CommandFixedRack())
|
||||
FreeCADGui.addCommand('PVPlantTrackerSetup', _CommandTrackerSetup())
|
||||
FreeCADGui.addCommand('PVPlantTracker', _CommandTracker())
|
||||
FreeCADGui.addCommand('Multirow', _CommandMultiRowTracker())
|
||||
FreeCADGui.addCommand('RackType', CommandRackGroup())
|
||||
|
||||
@@ -33,7 +33,7 @@ except AttributeError:
|
||||
import os, math
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
class _TaskPanel:
|
||||
class TaskPanel:
|
||||
def __init__(self, obj = None):
|
||||
self.obj = None
|
||||
self.select = 0
|
||||
@@ -251,7 +251,7 @@ def Open3DTriangle(point_cloud):
|
||||
#p_mesh_crop = mesh.crop(bbox)
|
||||
return mesh
|
||||
|
||||
class _PVPlantCreateTerrainMesh:
|
||||
'''class _PVPlantCreateTerrainMesh:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "surface.svg")),
|
||||
@@ -268,4 +268,4 @@ class _PVPlantCreateTerrainMesh:
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantCreateTerrainMesh', _PVPlantCreateTerrainMesh())
|
||||
FreeCADGui.addCommand('PVPlantCreateTerrainMesh', _PVPlantCreateTerrainMesh())'''
|
||||
|
||||
@@ -5,6 +5,7 @@ import Part
|
||||
import ArchComponent
|
||||
from pivy import coin
|
||||
import numpy as np
|
||||
import DraftGeomUtils
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui, os
|
||||
@@ -488,7 +489,7 @@ class ViewProviderEarthWorksVolume:
|
||||
return None
|
||||
|
||||
|
||||
class _EarthWorksTaskPanel:
|
||||
class EarthWorksTaskPanel:
|
||||
def __init__(self):
|
||||
self.To = None
|
||||
|
||||
@@ -516,6 +517,7 @@ class _EarthWorksTaskPanel:
|
||||
return False
|
||||
|
||||
FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras")
|
||||
|
||||
def calculateEarthWorks(line, extreme=False):
|
||||
pts = []
|
||||
pts1 = []
|
||||
@@ -576,33 +578,6 @@ class _EarthWorksTaskPanel:
|
||||
elif ver == 1:
|
||||
from PVPlantPlacement import getCols
|
||||
columns = getCols(frames)
|
||||
|
||||
'''colelements = set()
|
||||
rowelements = set()
|
||||
for groups in columns:
|
||||
for group in groups:
|
||||
for frame in group:
|
||||
colelements.add(frame.Placement.Base.x)
|
||||
rowelements.add(frame.Placement.Base.y)
|
||||
colelements = sorted(colelements)
|
||||
rowelements = sorted(rowelements, reverse=True)
|
||||
print("Cols: ", len(colelements), " - ", colelements)
|
||||
print("Rows: ", len(rowelements), " - ", rowelements)
|
||||
|
||||
a = []
|
||||
colnum = len(colelements)
|
||||
for r in range(len(rowelements)):
|
||||
a.append([None] * colnum)
|
||||
mat = np.array(a, dtype=object)
|
||||
for groups in columns:
|
||||
for group in groups:
|
||||
for frame in group:
|
||||
colidx = colelements.index(frame.Placement.Base.x)
|
||||
rowidx = rowelements.index(frame.Placement.Base.y)
|
||||
mat[rowidx][colidx] = frame
|
||||
print(mat)
|
||||
return'''
|
||||
|
||||
for groups in columns:
|
||||
for group in groups:
|
||||
first = group[0]
|
||||
@@ -709,44 +684,46 @@ class _EarthWorksTaskPanel:
|
||||
import Mesh
|
||||
pro = utils.getProjected(sh)
|
||||
pro = utils.simplifyWire(pro)
|
||||
#pro = pro.makeOffset2D(20000, 2, False, False, True)
|
||||
Part.show(sh, "loft")
|
||||
Part.show(pro, "pro")
|
||||
pts = [ver.Point for ver in pro.Vertexes]
|
||||
'''if pts[0] != pts[-1]:
|
||||
pts.append(pts[0])'''
|
||||
land.trim(pts, 1)
|
||||
|
||||
tmp = []
|
||||
shp = Part.Shape()
|
||||
for face in sh.Faces:
|
||||
wire = face.Wires[0].copy()
|
||||
pl = wire.Placement.Base
|
||||
wire.Placement.Base = wire.Placement.Base - pl
|
||||
wire = wire.scale(2)
|
||||
wire.Placement.Base = wire.Placement.Base + pl
|
||||
#wire = wire.makeOffset2D(10000, 0, False, False, True)
|
||||
wire.Placement.Base.z = wire.Placement.Base.z - 10000
|
||||
face1 = Part.makeLoft([face.Wires[0], wire], True, True, False)
|
||||
|
||||
if DraftGeomUtils.isPlanar(wire):
|
||||
# Caso simple
|
||||
wire = wire.makeOffset2D(10000, 0, False, False, True)
|
||||
wire.Placement.Base.z = wire.Placement.Base.z - 10000
|
||||
top = wire.makeOffset2D(1, 0, False, False, True)
|
||||
loft = Part.makeLoft([top, wire], True, True, False)
|
||||
tmp.append(loft)
|
||||
shp = shp.fuse(loft)
|
||||
else:
|
||||
# Caso complejo:
|
||||
vertices = face.Vertexes
|
||||
# Dividir rectángulo en 2 triángulos
|
||||
triangles = [
|
||||
[vertices[0], vertices[1], vertices[2]],
|
||||
[vertices[2], vertices[3], vertices[0]]
|
||||
]
|
||||
|
||||
Part.show(face1, "tool")
|
||||
#tmp.append(face.extrude(FreeCAD.Vector(0, 0, -10000)))
|
||||
#Part.show(tmp[-1], "face-extrude")
|
||||
sh = sh.extrude(FreeCAD.Vector(0, 0, -10000))
|
||||
sh = Part.Solid(sh)
|
||||
Part.show(sh)
|
||||
import MeshPart as mp
|
||||
msh = mp.meshFromShape(Shape=sh) # , MaxLength=1)
|
||||
# msh = msh.smooth("Laplace", 3)
|
||||
Mesh.show(msh, "tool")
|
||||
Mesh.show(land, "trim")
|
||||
'''inner = msh.inner(land)
|
||||
Mesh.show(inner)
|
||||
outer = msh.inner(land)
|
||||
Mesh.show(outer)'''
|
||||
'''intersec = land.section(msh, MinDist=0.01)
|
||||
import Draft
|
||||
for sec in intersec:
|
||||
Draft.makeWire(sec)'''
|
||||
for tri in triangles:
|
||||
# Crear wire triangular
|
||||
wire = Part.makePolygon([v.Point for v in tri] + [tri[0].Point])
|
||||
wire = wire.makeOffset2D(10000, 0, False, False, True)
|
||||
wire.Placement.Base.z = wire.Placement.Base.z - 10000
|
||||
top = wire.makeOffset2D(1, 0, False, False, True)
|
||||
loft = Part.makeLoft([top, wire], True, True, False)
|
||||
tmp.append(loft)
|
||||
shp = shp.fuse(loft)
|
||||
|
||||
final_tool = Part.makeCompound(tmp)
|
||||
Part.show(final_tool, "tool")
|
||||
Part.show(shp)
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
self.closeForm()
|
||||
@@ -827,7 +804,7 @@ def searchTool(obj, tools):
|
||||
return tool
|
||||
|
||||
|
||||
class _CommandCalculateEarthworks:
|
||||
'''class _CommandCalculateEarthworks:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "pico.svg")),
|
||||
@@ -846,7 +823,7 @@ class _CommandCalculateEarthworks:
|
||||
return active
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantEarthworks', _CommandCalculateEarthworks())
|
||||
FreeCADGui.addCommand('PVPlantEarthworks', _CommandCalculateEarthworks())'''
|
||||
|
||||
|
||||
def accept():
|
||||
@@ -964,4 +941,5 @@ def accept():
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
self.closeForm()
|
||||
return True
|
||||
return True
|
||||
|
||||
|
||||
@@ -845,7 +845,7 @@ class _FenceTaskPanel:
|
||||
|
||||
|
||||
# Commands ---------------------------------------------------------------------------------
|
||||
class _CommandPVPlantFence:
|
||||
class CommandPVPlantFence:
|
||||
"the PVPlant Fence command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -882,14 +882,7 @@ if FreeCAD.GuiUp:
|
||||
not (FreeCAD.ActiveDocument.getObject("Terrain") is None))
|
||||
|
||||
import PVPlantFenceGate
|
||||
FreeCADGui.addCommand('PVPlantFence', _CommandPVPlantFence())
|
||||
FreeCADGui.addCommand('PVPlantFence', CommandPVPlantFence())
|
||||
FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate._CommandPVPlantGate())
|
||||
FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost._CommandFencePost())
|
||||
FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
|
||||
|
||||
def movep(obj):
|
||||
pl = obj.Shape.BoundBox.Center
|
||||
points = []
|
||||
for ind in range(len(obj.Shape.Vertexes)):
|
||||
points.append(obj.Shape.Vertexes[ind].Point - pl)
|
||||
Draft.makeWire(points)
|
||||
#FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())
|
||||
|
||||
@@ -27,8 +27,7 @@ if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from PySide2.QtWebEngineWidgets import QWebEngineView
|
||||
from PySide2.QtWebChannel import QWebChannel
|
||||
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
@@ -47,13 +46,21 @@ class MapWindow(QtGui.QWidget):
|
||||
def __init__(self, WinTitle="MapWindow"):
|
||||
super(MapWindow, self).__init__()
|
||||
self.raise_()
|
||||
self.lat = 0
|
||||
self.lon = 0
|
||||
self.lat = None
|
||||
self.lon = None
|
||||
self.minLat = None
|
||||
self.maxLat = None
|
||||
self.minLon = None
|
||||
self.maxLon = None
|
||||
self.zoom = None
|
||||
self.WinTitle = WinTitle
|
||||
|
||||
self.georeference_coordinates = {'lat': None, 'lon': None}
|
||||
self.setupUi()
|
||||
|
||||
def setupUi(self):
|
||||
from PySide2.QtWebEngineWidgets import QWebEngineView
|
||||
from PySide2.QtWebChannel import QWebChannel
|
||||
|
||||
self.ui = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantGeoreferencing.ui", self)
|
||||
|
||||
self.resize(1200, 800)
|
||||
@@ -144,6 +151,12 @@ class MapWindow(QtGui.QWidget):
|
||||
RightLayout.addWidget(self.groupbox)
|
||||
# ------------------------
|
||||
|
||||
self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
|
||||
RightLayout.addWidget(self.checkboxImportGis)
|
||||
|
||||
self.checkboxImportSatelitalImagen = QtGui.QCheckBox("Importar Imagen Satelital")
|
||||
RightLayout.addWidget(self.checkboxImportSatelitalImagen)
|
||||
|
||||
verticalSpacer = QtGui.QSpacerItem(20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
|
||||
RightLayout.addItem(verticalSpacer)
|
||||
|
||||
@@ -169,7 +182,6 @@ class MapWindow(QtGui.QWidget):
|
||||
|
||||
geolocator = Nominatim(user_agent="http")
|
||||
location = geolocator.geocode(self.valueSearch.text())
|
||||
print(location.raw)
|
||||
self.valueSearch.setText(location.address)
|
||||
self.panMap(location.longitude, location.latitude, location.raw['boundingbox'])
|
||||
|
||||
@@ -185,6 +197,7 @@ class MapWindow(QtGui.QWidget):
|
||||
"var data = drawnItems.toGeoJSON();"
|
||||
"MyApp.shapes(JSON.stringify(data));"
|
||||
)
|
||||
|
||||
self.close()
|
||||
|
||||
@QtCore.Slot(float, float)
|
||||
@@ -196,10 +209,22 @@ class MapWindow(QtGui.QWidget):
|
||||
' | UTM: ' + str(zone_number) + zone_letter +
|
||||
', {:.5f}m E, {:.5f}m N'.format(x, y))
|
||||
|
||||
@QtCore.Slot(float, float, float, float, int)
|
||||
def onMapZoom(self, minLat, minLon, maxLat, maxLon, zoom):
|
||||
self.minLat = min([minLat, maxLat])
|
||||
self.maxLat = max([minLat, maxLat])
|
||||
self.minLon = min([minLon, maxLon])
|
||||
self.maxLon = max([minLon, maxLon])
|
||||
self.zoom = zoom
|
||||
|
||||
@QtCore.Slot(float, float)
|
||||
def georeference(self, lat, lng):
|
||||
import PVPlantSite
|
||||
from geopy.geocoders import Nominatim
|
||||
|
||||
self.georeference_coordinates['lat'] = lat
|
||||
self.georeference_coordinates['lon'] = lng
|
||||
|
||||
Site = PVPlantSite.get(create=True)
|
||||
Site.Proxy.setLatLon(lat, lng)
|
||||
|
||||
@@ -228,13 +253,20 @@ class MapWindow(QtGui.QWidget):
|
||||
import geojson
|
||||
import PVPlantImportGrid as ImportElevation
|
||||
import Draft
|
||||
import PVPlantSite
|
||||
Site = PVPlantSite.get()
|
||||
|
||||
offset = FreeCAD.Vector(0, 0, 0)
|
||||
if not (self.lat is None or self.lon is None):
|
||||
offset = FreeCAD.Vector(Site.Origin)
|
||||
offset.z = 0
|
||||
|
||||
items = geojson.loads(drawnItems)
|
||||
for item in items['features']:
|
||||
if item['geometry']['type'] == "Point": # 1. if the feature is a Point or Circle:
|
||||
coord = item['geometry']['coordinates']
|
||||
point = ImportElevation.getElevationFromOE([[coord[0], coord[1]],])
|
||||
c = FreeCAD.Vector(point[0][0], point[0][1], point[0][2])
|
||||
c = FreeCAD.Vector(point[0][0], point[0][1], point[0][2]).sub(offset)
|
||||
if item['properties'].get('radius'):
|
||||
r = round(item['properties']['radius'] * 1000, 0)
|
||||
p = FreeCAD.Placement()
|
||||
@@ -252,34 +284,231 @@ class MapWindow(QtGui.QWidget):
|
||||
name = "Area"
|
||||
lp = item['geometry']['coordinates'][0]
|
||||
|
||||
pts = []
|
||||
for cords in lp:
|
||||
pts.append([cords[1], cords[0]])
|
||||
pts = [[cords[1], cords[0]] for cords in lp]
|
||||
tmp = ImportElevation.getElevationFromOE(pts)
|
||||
pts = []
|
||||
for p in tmp:
|
||||
pts.append(p.sub(FreeCAD.ActiveDocument.Site.Origin))
|
||||
pts = [p.sub(offset) for p in tmp]
|
||||
|
||||
obj = Draft.makeWire(pts, closed=cw, face=False)
|
||||
obj.Placement.Base = FreeCAD.ActiveDocument.Site.Origin
|
||||
#obj.Placement.Base = Site.Origin
|
||||
obj.Label = name
|
||||
Draft.autogroup(obj)
|
||||
|
||||
if item['properties'].get('name'):
|
||||
obj.Label = item['properties']['name']
|
||||
|
||||
FreeCAD.activeDocument().recompute()
|
||||
FreeCADGui.updateGui()
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
if self.checkboxImportGis.isChecked():
|
||||
self.getDataFromOSM(self.minLat, self.minLon, self.maxLat, self.maxLon)
|
||||
|
||||
def panMap(self, lng, lat, geometry=""):
|
||||
if self.checkboxImportSatelitalImagen.isChecked():
|
||||
# Usar los límites reales del terreno (rectangular)
|
||||
'''s_lat = self.minLat
|
||||
s_lon = self.minLon
|
||||
n_lat = self.maxLat
|
||||
n_lon = self.maxLon
|
||||
|
||||
# Obtener puntos UTM para las esquinas
|
||||
corners = ImportElevation.getElevationFromOE([
|
||||
[s_lat, s_lon], # Esquina suroeste
|
||||
[n_lat, s_lon], # Esquina sureste
|
||||
[n_lat, n_lon], # Esquina noreste
|
||||
[s_lat, n_lon] # Esquina noroeste
|
||||
])
|
||||
|
||||
if not corners or len(corners) < 4:
|
||||
FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas\n")
|
||||
return
|
||||
|
||||
# Descargar imagen satelital
|
||||
from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
|
||||
downloader = GoogleMapDownloader(
|
||||
zoom= 18, #self.zoom,
|
||||
layer='raw_satellite'
|
||||
)
|
||||
img = downloader.generateImage(
|
||||
sw_lat=s_lat,
|
||||
sw_lng=s_lon,
|
||||
ne_lat=n_lat,
|
||||
ne_lng=n_lon
|
||||
)
|
||||
|
||||
# Guardar imagen en el directorio del documento
|
||||
doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
|
||||
if not doc_path:
|
||||
doc_path = FreeCAD.ConfigGet("UserAppData")
|
||||
|
||||
filename = os.path.join(doc_path, "background.jpeg")
|
||||
img.save(filename)
|
||||
|
||||
ancho, alto = img.size
|
||||
|
||||
# Crear objeto de imagen en FreeCAD
|
||||
doc = FreeCAD.ActiveDocument
|
||||
img_obj = doc.addObject('Image::ImagePlane', 'Background')
|
||||
img_obj.ImageFile = filename
|
||||
img_obj.Label = 'Background'
|
||||
|
||||
# Calcular dimensiones en metros usando las coordenadas UTM
|
||||
# Extraer las coordenadas de las esquinas
|
||||
sw = corners[0] # Suroeste
|
||||
se = corners[1] # Sureste
|
||||
ne = corners[2] # Noreste
|
||||
nw = corners[3] # Noroeste
|
||||
|
||||
# Calcular ancho (promedio de los lados superior e inferior)
|
||||
width_bottom = se.x - sw.x
|
||||
width_top = ne.x - nw.x
|
||||
width_m = (width_bottom + width_top) / 2
|
||||
|
||||
# Calcular alto (promedio de los lados izquierdo y derecho)
|
||||
height_left = nw.y - sw.y
|
||||
height_right = ne.y - se.y
|
||||
height_m = (height_left + height_right) / 2
|
||||
|
||||
img_obj.XSize = width_m
|
||||
img_obj.YSize = height_m
|
||||
|
||||
# Posicionar el centro de la imagen en (0,0,0)
|
||||
img_obj.Placement.Base = FreeCAD.Vector(-width_m / 2, -height_m / 2, 0)'''
|
||||
|
||||
# Definir área rectangular
|
||||
s_lat = self.minLat
|
||||
s_lon = self.minLon
|
||||
n_lat = self.maxLat
|
||||
n_lon = self.maxLon
|
||||
|
||||
# Obtener puntos UTM para las esquinas y el punto de referencia
|
||||
points = [
|
||||
[s_lat, s_lon], # Suroeste
|
||||
[n_lat, n_lon], # Noreste
|
||||
[self.georeference_coordinates['lat'], self.georeference_coordinates['lon']] # Punto de referencia
|
||||
]
|
||||
utm_points = ImportElevation.getElevationFromOE(points)
|
||||
|
||||
if not utm_points or len(utm_points) < 3:
|
||||
FreeCAD.Console.PrintError("Error obteniendo elevaciones para las esquinas y referencia\n")
|
||||
return
|
||||
|
||||
sw_utm, ne_utm, ref_utm = utm_points
|
||||
|
||||
# Descargar imagen satelital
|
||||
from lib.GoogleSatelitalImageDownload import GoogleMapDownloader
|
||||
downloader = GoogleMapDownloader(
|
||||
zoom=self.zoom,
|
||||
layer='raw_satellite'
|
||||
)
|
||||
img = downloader.generateImage(
|
||||
sw_lat=s_lat,
|
||||
sw_lng=s_lon,
|
||||
ne_lat=n_lat,
|
||||
ne_lng=n_lon
|
||||
)
|
||||
|
||||
# Guardar imagen
|
||||
doc_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) if FreeCAD.ActiveDocument.FileName else ""
|
||||
if not doc_path:
|
||||
doc_path = FreeCAD.ConfigGet("UserAppData")
|
||||
|
||||
filename = os.path.join(doc_path, "background.jpeg")
|
||||
img.save(filename)
|
||||
|
||||
# Calcular dimensiones reales en metros
|
||||
width_m = ne_utm.x - sw_utm.x # Ancho en metros (este-oeste)
|
||||
height_m = ne_utm.y - sw_utm.y # Alto en metros (norte-sur)
|
||||
|
||||
# Calcular posición relativa del punto de referencia dentro de la imagen
|
||||
rel_x = (ref_utm.x - sw_utm.x) / width_m if width_m != 0 else 0.5
|
||||
rel_y = (ref_utm.y - sw_utm.y) / height_m if height_m != 0 else 0.5
|
||||
|
||||
# Crear objeto de imagen en FreeCAD
|
||||
doc = FreeCAD.ActiveDocument
|
||||
img_obj = doc.addObject('Image::ImagePlane', 'Background')
|
||||
img_obj.ImageFile = filename
|
||||
img_obj.Label = 'Background'
|
||||
|
||||
# Convertir dimensiones a milímetros (FreeCAD trabaja en mm)
|
||||
img_obj.XSize = width_m * 1000
|
||||
img_obj.YSize = height_m * 1000
|
||||
|
||||
# Posicionar para que el punto de referencia esté en (0,0,0)
|
||||
# La esquina inferior izquierda debe estar en:
|
||||
# x = -rel_x * ancho_total
|
||||
# y = -rel_y * alto_total
|
||||
img_obj.Placement.Base = FreeCAD.Vector(
|
||||
-rel_x * width_m * 1000,
|
||||
-rel_y * height_m * 1000,
|
||||
0
|
||||
)
|
||||
|
||||
# Refrescar el documento
|
||||
doc.recompute()
|
||||
|
||||
def calculate_texture_transform(self, mesh_obj, width_m, height_m):
|
||||
"""Calcula la transformación precisa para la textura"""
|
||||
try:
|
||||
# Obtener coordenadas reales de las esquinas
|
||||
import utm
|
||||
sw = utm.from_latlon(self.minLat, self.minLon)
|
||||
ne = utm.from_latlon(self.maxLat, self.maxLon)
|
||||
|
||||
# Crear matriz de transformación
|
||||
scale_x = (ne[0] - sw[0]) / width_m
|
||||
scale_y = (ne[1] - sw[1]) / height_m
|
||||
|
||||
# Aplicar transformación (solo si se usa textura avanzada)
|
||||
if hasattr(mesh_obj.ViewObject, "TextureMapping"):
|
||||
mesh_obj.ViewObject.TextureMapping = "PLANE"
|
||||
mesh_obj.ViewObject.TextureScale = (scale_x, scale_y)
|
||||
mesh_obj.ViewObject.TextureOffset = (sw[0], sw[1])
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"No se pudo calcular transformación: {str(e)}\n")
|
||||
|
||||
def getDataFromOSM(self, min_lat, min_lon, max_lat, max_lon):
|
||||
import Importer.importOSM as importOSM
|
||||
import PVPlantSite
|
||||
site = PVPlantSite.get()
|
||||
|
||||
offset = FreeCAD.Vector(0, 0, 0)
|
||||
if not (self.lat is None or self.lon is None):
|
||||
offset = FreeCAD.Vector(site.Origin)
|
||||
offset.z = 0
|
||||
importer = importOSM.OSMImporter(offset)
|
||||
osm_data = importer.get_osm_data(f"{min_lat},{min_lon},{max_lat},{max_lon}")
|
||||
importer.process_osm_data(osm_data)
|
||||
|
||||
'''FreeCAD.activeDocument().recompute()
|
||||
FreeCADGui.updateGui()
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")'''
|
||||
|
||||
def panMap_old(self, lng, lat, geometry=""):
|
||||
frame = self.view.page()
|
||||
bbox = "[{0}, {1}], [{2}, {3}]".format(float(geometry[0]), float(geometry[2]),
|
||||
float(geometry[1]), float(geometry[3]))
|
||||
command = 'map.panTo(L.latLng({lt}, {lg}));'.format(lt=lat, lg=lng)
|
||||
command = 'map.panTo(L.latLng({lt}, {lg}));'.format(lt=lat, lg=lng)
|
||||
command += 'map.fitBounds([{box}]);'.format(box=bbox)
|
||||
frame.runJavaScript(command)
|
||||
|
||||
# deepseek
|
||||
def panMap(self, lng, lat, geometry=None):
|
||||
frame = self.view.page()
|
||||
|
||||
# 1. Validación del parámetro geometry
|
||||
if not geometry or len(geometry) < 4:
|
||||
# Pan básico sin ajuste de bounds
|
||||
command = f'map.panTo(L.latLng({lat}, {lng}));'
|
||||
else:
|
||||
try:
|
||||
# 2. Mejor manejo de coordenadas (Leaflet usa [lat, lng])
|
||||
# Asumiendo que geometry es [min_lng, min_lat, max_lng, max_lat]
|
||||
southwest = f"{float(geometry[1])}, {float(geometry[0])}" # min_lat, min_lng
|
||||
northeast = f"{float(geometry[3])}, {float(geometry[2])}" # max_lat, max_lng
|
||||
command = f'map.panTo(L.latLng({lat}, {lng}));'
|
||||
command += f'map.fitBounds(L.latLngBounds([{southwest}], [{northeast}]));'
|
||||
except (IndexError, ValueError, TypeError) as e:
|
||||
print(f"Error en geometry: {str(e)}")
|
||||
command = f'map.panTo(L.latLng({lat}, {lng}));'
|
||||
frame.runJavaScript(command)
|
||||
|
||||
def importKML(self):
|
||||
file = QtGui.QFileDialog.getOpenFileName(None, "FileDialog", "", "Google Earth (*.kml *.kmz)")[0]
|
||||
|
||||
@@ -287,11 +516,11 @@ class MapWindow(QtGui.QWidget):
|
||||
layers = kmz_convert(file, "", )
|
||||
frame = self.view.page()
|
||||
for layer in layers:
|
||||
command = "drawnItems.addLayer(L.geoJSON({0}));".format(layer)
|
||||
command = "var geoJsonLayer = L.geoJSON({0}); drawnItems.addLayer(geoJsonLayer); map.fitBounds(geoJsonLayer.getBounds());".format( layer)
|
||||
frame.runJavaScript(command)
|
||||
|
||||
|
||||
class _CommandPVPlantGeoreferencing:
|
||||
class CommandPVPlantGeoreferencing:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "Location.svg")),
|
||||
@@ -309,6 +538,6 @@ class _CommandPVPlantGeoreferencing:
|
||||
else:
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
'''if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantGeoreferencing',_CommandPVPlantGeoreferencing())
|
||||
|
||||
'''
|
||||
|
||||
@@ -57,14 +57,14 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit">
|
||||
<widget class="QLineEdit" name="search_bar">
|
||||
<property name="placeholderText">
|
||||
<string>Search...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<widget class="QPushButton" name="search_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -80,10 +80,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWebEngineView" name="widget_4" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="coordinates_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -91,7 +88,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
<string>coordenadas:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -111,7 +108,7 @@
|
||||
<widget class="QWidget" name="widget_5" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="pushButton_4">
|
||||
<widget class="QPushButton" name="kmz_button">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
@@ -124,20 +121,27 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBox">
|
||||
<property name="text">
|
||||
<string>Georeferenciar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxGeoreference">
|
||||
<property name="text">
|
||||
<string>Georeferenciar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBoxImportGis">
|
||||
<property name="text">
|
||||
<string>CheckBox</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@@ -164,7 +168,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_3">
|
||||
<widget class="QPushButton" name="accept_button">
|
||||
<property name="text">
|
||||
<string>Aceptar</string>
|
||||
</property>
|
||||
@@ -178,14 +182,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QWebEngineView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qwebengineview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Georeferencing</class>
|
||||
<widget class="QDialog" name="Georeferencing">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>574</width>
|
||||
<height>350</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create Surface</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="widgetLeft" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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="QWidget" name="widgetSearch" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<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="QLineEdit" name="lineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Search...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWebEngineView" name="widget_4" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widgetRight" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Configuraciones:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox">
|
||||
<property name="text">
|
||||
<string>Georeferenciar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Cargar un archivo KMZ/KML:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_4">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listWidget">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>New Item</string>
|
||||
</property>
|
||||
<property name="checkState">
|
||||
<enum>Unchecked</enum>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
<widget class="QWidget" name="widget_6" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_2">
|
||||
<property name="text">
|
||||
<string>Cancelar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_3">
|
||||
<property name="text">
|
||||
<string>Aceptar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QWebEngineView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qwebengineview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -40,21 +40,95 @@ from PVPlantResources import DirIcons as DirIcons
|
||||
import PVPlantSite
|
||||
|
||||
|
||||
def get_elevation_from_oe(coordinates): # v1 deepseek
|
||||
"""Obtiene elevaciones de Open-Elevation API y devuelve vectores FreeCAD en coordenadas UTM.
|
||||
Args:
|
||||
coordinates (list): Lista de tuplas con coordenadas (latitud, longitud)
|
||||
Returns:
|
||||
list: Lista de vectores FreeCAD con coordenadas UTM y elevación (en milímetros)
|
||||
o lista vacía en caso de error.
|
||||
"""
|
||||
if not coordinates:
|
||||
return []
|
||||
|
||||
import requests
|
||||
import utm
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
# Construcción más eficiente de parámetros
|
||||
locations = "|".join([f"{lat:.6f},{lon:.6f}" for lat, lon in coordinates])
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
url="https://api.open-elevation.com/api/v1/lookup",
|
||||
params={'locations': locations},
|
||||
timeout=20,
|
||||
verify=True
|
||||
)
|
||||
response.raise_for_status() # Lanza excepción para códigos 4xx/5xx
|
||||
|
||||
except RequestException as e:
|
||||
print(f"Error en la solicitud: {str(e)}")
|
||||
return []
|
||||
|
||||
try:
|
||||
data = response.json()
|
||||
except ValueError:
|
||||
print("Respuesta JSON inválida")
|
||||
return []
|
||||
|
||||
if "results" not in data or len(data["results"]) != len(coordinates):
|
||||
print("Formato de respuesta inesperado")
|
||||
return []
|
||||
|
||||
points = []
|
||||
for result in data["results"]:
|
||||
try:
|
||||
# Conversión UTM con manejo de errores
|
||||
easting, northing, _, _ = utm.from_latlon(
|
||||
result["latitude"],
|
||||
result["longitude"]
|
||||
)
|
||||
|
||||
points.append(FreeCAD.Vector(round(easting), # Convertir metros a milímetros
|
||||
round(northing),
|
||||
round(result["elevation"])) * 1000)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error procesando coordenadas: {str(e)}")
|
||||
continue
|
||||
|
||||
return points
|
||||
|
||||
|
||||
def getElevationFromOE(coordinates):
|
||||
"""Obtiene elevaciones de Open-Elevation API y devuelve vectores FreeCAD en coordenadas UTM."""
|
||||
|
||||
import certifi
|
||||
from requests.exceptions import RequestException
|
||||
if len(coordinates) == 0:
|
||||
return None
|
||||
|
||||
from requests import get
|
||||
import utm
|
||||
|
||||
str=""
|
||||
locations_str=""
|
||||
total = len(coordinates) - 1
|
||||
for i, point in enumerate(coordinates):
|
||||
str += '{:.6f},{:.6f}'.format(point[0], point[1])
|
||||
locations_str += '{:.6f},{:.6f}'.format(point[0], point[1])
|
||||
if i != total:
|
||||
str += '|'
|
||||
query = 'https://api.open-elevation.com/api/v1/lookup?locations=' + str
|
||||
r = get(query, timeout=20)
|
||||
locations_str += '|'
|
||||
query = 'https://api.open-elevation.com/api/v1/lookup?locations=' + locations_str
|
||||
try:
|
||||
r = get(query, timeout=20, verify=certifi.where()) # <-- Corrección aquí
|
||||
except RequestException as e:
|
||||
points = []
|
||||
for i, point in enumerate(coordinates):
|
||||
c = utm.from_latlon(point[0], point[1])
|
||||
points.append(FreeCAD.Vector(round(c[0], 0),
|
||||
round(c[1], 0),
|
||||
0) * 1000)
|
||||
return points
|
||||
|
||||
# Only get the json response in case of 200 or 201
|
||||
points = []
|
||||
@@ -62,14 +136,16 @@ def getElevationFromOE(coordinates):
|
||||
results = r.json()
|
||||
for point in results["results"]:
|
||||
c = utm.from_latlon(point["latitude"], point["longitude"])
|
||||
v = FreeCAD.Vector(round(c[0] * 1000, 0),
|
||||
round(c[1] * 1000, 0),
|
||||
round(point["elevation"] * 1000, 0))
|
||||
v = FreeCAD.Vector(round(c[0], 0),
|
||||
round(c[1], 0),
|
||||
round(point["elevation"], 0)) * 1000
|
||||
points.append(v)
|
||||
return points
|
||||
|
||||
def getSinglePointElevationFromBing(lat, lng):
|
||||
#http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey}
|
||||
import utm
|
||||
|
||||
source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points="
|
||||
source += str(lat) + "," + str(lng)
|
||||
source += "&heights=sealevel"
|
||||
@@ -79,11 +155,9 @@ def getSinglePointElevationFromBing(lat, lng):
|
||||
response = requests.get(source)
|
||||
ans = response.text
|
||||
|
||||
# +# to do: error handling - wait and try again
|
||||
s = json.loads(ans)
|
||||
print(s)
|
||||
res = s['resourceSets'][0]['resources'][0]['elevations']
|
||||
|
||||
import utm
|
||||
for elevation in res:
|
||||
c = utm.from_latlon(lat, lng)
|
||||
v = FreeCAD.Vector(
|
||||
@@ -250,7 +324,6 @@ def getSinglePointElevationUtm(lat, lon):
|
||||
print (v)
|
||||
return v
|
||||
|
||||
|
||||
def getElevationUTM(polygon, lat, lng, resolution = 10000):
|
||||
|
||||
import utm
|
||||
@@ -374,47 +447,6 @@ def getElevation(lat, lon, b=50.35, le=11.17, size=40):
|
||||
FreeCADGui.updateGui()
|
||||
return FreeCAD.activeDocument().ActiveObject
|
||||
|
||||
|
||||
'''
|
||||
# original::
|
||||
def getElevation(lat, lon, b=50.35, le=11.17, size=40):
|
||||
tm.lat = lat
|
||||
tm.lon = lon
|
||||
baseheight = 0 #getheight(tm.lat, tm.lon)
|
||||
center = tm.fromGeographic(tm.lat, tm.lon)
|
||||
|
||||
#https://maps.googleapis.com/maps/api/elevation/json?path=36.578581,-118.291994|36.23998,-116.83171&samples=3&key=YOUR_API_KEY
|
||||
#https://maps.googleapis.com/maps/api/elevation/json?locations=39.7391536,-104.9847034&key=YOUR_API_KEY
|
||||
|
||||
source = "https://maps.googleapis.com/maps/api/elevation/json?path="
|
||||
source += str(b-size*0.001) + "," + str(le) + "|" + str(b+size*0.001) + "," + str(le)
|
||||
source += "&samples=" + str(100)
|
||||
source += "&key=AIzaSyB07X6lowYJ-iqyPmaFJvr-6zp1J63db8U"
|
||||
|
||||
response = urllib.request.urlopen(source)
|
||||
ans = response.read()
|
||||
|
||||
# +# to do: error handling - wait and try again
|
||||
s = json.loads(ans)
|
||||
res = s['results']
|
||||
|
||||
points = []
|
||||
for r in res:
|
||||
c = tm.fromGeographic(r['location']['lat'], r['location']['lng'])
|
||||
v = FreeCAD.Vector(
|
||||
round(c[0], 2),
|
||||
round(c[1], 2),
|
||||
round(r['elevation'] * 1000, 2) - baseheight
|
||||
)
|
||||
points.append(v)
|
||||
|
||||
line = Draft.makeWire(points, closed=False, face=False, support=None)
|
||||
line.ViewObject.Visibility = False
|
||||
#FreeCAD.activeDocument().recompute()
|
||||
FreeCADGui.updateGui()
|
||||
return FreeCAD.activeDocument().ActiveObject
|
||||
'''
|
||||
|
||||
class _ImportPointsTaskPanel:
|
||||
|
||||
def __init__(self, obj = None):
|
||||
|
||||
@@ -138,8 +138,6 @@ class _Manhole(ArchComponent.Component):
|
||||
obj.Shape = ext_sol.cut([ins_sol, ], 0.0)
|
||||
|
||||
|
||||
|
||||
|
||||
class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
|
||||
"A View Provider for the Pipe object"
|
||||
|
||||
@@ -180,7 +178,7 @@ class _ViewProviderManhole(ArchComponent.ViewProviderComponent):
|
||||
|
||||
import draftguitools.gui_tool_utils as gui_tool_utils
|
||||
|
||||
class _ManholeTaskPanel:
|
||||
class ManholeTaskPanel:
|
||||
def __init__(self, obj=None):
|
||||
self.new = False
|
||||
if obj is None:
|
||||
@@ -249,7 +247,7 @@ class _ManholeTaskPanel:
|
||||
self.view.removeEventCallback("SoEvent", self.call)
|
||||
|
||||
|
||||
class _CommandManhole:
|
||||
'''class _CommandManhole:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -274,5 +272,5 @@ class _CommandManhole:
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantManhole', _CommandManhole())
|
||||
FreeCADGui.addCommand('PVPlantManhole', _CommandManhole())'''
|
||||
|
||||
|
||||
@@ -323,7 +323,7 @@ class _PadTaskPanel:
|
||||
self.new = False
|
||||
self.obj = obj
|
||||
|
||||
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantTrench.ui"))
|
||||
self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "Civil/PVPlantTrench.ui"))
|
||||
|
||||
def accept(self):
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Pad")
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import numpy as np
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui, os
|
||||
@@ -47,14 +46,13 @@ except AttributeError:
|
||||
import PVPlantResources
|
||||
import PVPlantSite
|
||||
|
||||
version = "0.1.0"
|
||||
|
||||
|
||||
def selectionFilter(sel, objtype):
|
||||
print("type: ", objtype)
|
||||
fil = []
|
||||
for obj in sel:
|
||||
if hasattr(obj, "Proxy"):
|
||||
print("objeto:", obj.Proxy.__class__)
|
||||
print(obj.Proxy.__class__ is objtype)
|
||||
if obj.Proxy.__class__ is objtype:
|
||||
fil.append(obj)
|
||||
return fil
|
||||
@@ -119,8 +117,8 @@ class _PVPlantPlacementTaskPanel:
|
||||
for idx in range(len(placements)):
|
||||
newrack = PVPlantFrame.makeTracker(setup=types[idx])
|
||||
newrack.Label = "Tracker"
|
||||
newrack.Visibility = False
|
||||
newrack.Placement = placements[idx]
|
||||
newrack.Visibility = True
|
||||
MechanicalGroup.addObject(newrack)
|
||||
frames.append(newrack)
|
||||
if self.PVArea.Name.startswith("FrameArea"):
|
||||
@@ -136,11 +134,13 @@ class _PVPlantPlacementTaskPanel:
|
||||
else:
|
||||
from Utils import PVPlantUtils as utils
|
||||
wire = utils.simplifyWire(utils.getProjected(shape))
|
||||
if wire.isClosed():
|
||||
wire = wire.removeSplitter()
|
||||
return Part.Face(wire)
|
||||
|
||||
def calculateWorkingArea(self):
|
||||
self.Area = self.getProjected(self.PVArea.Shape)
|
||||
tmp = FreeCAD.ActiveDocument.findObjects(Name="ProhibitedArea")
|
||||
tmp = FreeCAD.ActiveDocument.findObjects(Name="ExclusionArea")
|
||||
if len(tmp):
|
||||
ProhibitedAreas = list()
|
||||
for obj in tmp:
|
||||
@@ -180,13 +180,12 @@ class _PVPlantPlacementTaskPanel:
|
||||
starty = int(refh.BoundBox.YMin + self.offsetY + self.gap_row * steps)
|
||||
# todo end ----------------------------------------------------------------------------------
|
||||
|
||||
return np.arange(startx, self.Area.BoundBox.XMax, self.gap_col, dtype=int), \
|
||||
np.arange(starty, self.Area.BoundBox.YMin, -self.gap_row, dtype=int)
|
||||
return np.arange(startx, self.Area.BoundBox.XMax, self.gap_col, dtype=np.int64), \
|
||||
np.arange(starty, self.Area.BoundBox.YMin, -self.gap_row, dtype=np.int64)
|
||||
|
||||
def adjustToTerrain(self, coordinates):
|
||||
mode = 1
|
||||
terrain = self.Terrain.Mesh
|
||||
type = 0
|
||||
|
||||
def placeRegion(df): # TODO: new
|
||||
import MeshPart as mp
|
||||
@@ -207,11 +206,10 @@ class _PVPlantPlacementTaskPanel:
|
||||
pbot = FreeCAD.Vector(base)
|
||||
pbot.y -= yl
|
||||
line = Part.LineSegment(ptop, pbot).toShape()
|
||||
if type == 0: # Mesh:
|
||||
profilepoints = mp.projectShapeOnMesh(line, terrain, FreeCAD.Vector(0, 0, 1))[0]
|
||||
else: # Shape:
|
||||
profilepoints = mp.projectShapeOnMesh(line, terrain, FreeCAD.Vector(0, 0, 1))[0]
|
||||
'''else: # Shape: sumamente lento por lo que quedaría eliminado si no se encuetra otro modo.
|
||||
tmp = terrain.makeParallelProjection(line, FreeCAD.Vector(0, 0, 1))
|
||||
profilepoints = [ver.Point for ver in tmp.Vertexes]
|
||||
profilepoints = [ver.Point for ver in tmp.Vertexes]'''
|
||||
|
||||
xx = list()
|
||||
yy = list()
|
||||
@@ -241,7 +239,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
df["regression"] = linregression
|
||||
|
||||
# 01. Grouping:
|
||||
from scipy.ndimage import sclabel
|
||||
from scipy.ndimage import label as sclabel
|
||||
import pandas as pd
|
||||
tmp = []
|
||||
for c, col in enumerate(coordinates):
|
||||
@@ -282,7 +280,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
placeRegion(df)
|
||||
return df
|
||||
|
||||
def placeonregion_old(frames): # old
|
||||
"""def placeonregion_old(frames): # old
|
||||
for colnum, col in enumerate(frames):
|
||||
groups = list()
|
||||
groups.append([col[0]])
|
||||
@@ -378,7 +376,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
rot = FreeCAD.Rotation(FreeCAD.Vector(-1, 0, 0), vec)
|
||||
pl.Rotation = FreeCAD.Rotation(rot.toEuler()[0], rot.toEuler()[1], 0)
|
||||
placements.append(pl)
|
||||
return placements
|
||||
return placements"""
|
||||
|
||||
def isInside(self, frame, point):
|
||||
if self.Area.isInside(point, 10, True):
|
||||
@@ -389,6 +387,7 @@ class _PVPlantPlacementTaskPanel:
|
||||
return False
|
||||
|
||||
def calculateAlignedArray(self):
|
||||
import FreeCAD
|
||||
pointsx, pointsy = self.getAligments()
|
||||
|
||||
footprints = []
|
||||
@@ -452,6 +451,8 @@ class _PVPlantPlacementTaskPanel:
|
||||
if countcols == self.form.editColCount.value():
|
||||
offsetcols += valcols
|
||||
countcols = 0
|
||||
print("/n/n")
|
||||
print(cols)
|
||||
return self.adjustToTerrain(cols)
|
||||
|
||||
def calculateNonAlignedArray(self):
|
||||
@@ -536,6 +537,11 @@ class _PVPlantPlacementTaskPanel:
|
||||
from datetime import datetime
|
||||
starttime = datetime.now()
|
||||
|
||||
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Document")
|
||||
auto_save_enabled = params.GetBool("AutoSaveEnabled")
|
||||
params.SetBool("AutoSaveEnabled", False)
|
||||
FreeCAD.ActiveDocument.RecomputesFrozen = True
|
||||
|
||||
items = []
|
||||
for x in range(self.form.listFrameSetups.count()):
|
||||
items.append(FreeCAD.ActiveDocument.getObject(self.form.listFrameSetups.item(x).text()))
|
||||
@@ -557,23 +563,25 @@ class _PVPlantPlacementTaskPanel:
|
||||
self.offsetY = FreeCAD.Units.Quantity(self.form.editOffsetVertical.text()).Value
|
||||
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Placement")
|
||||
# 1. Calculate working area:
|
||||
self.calculateWorkingArea()
|
||||
|
||||
# 2. Calculate aligned array:
|
||||
if self.form.cbAlignFrames.isChecked():
|
||||
dataframe = self.calculateAlignedArray()
|
||||
else:
|
||||
dataframe = self.calculateNonAlignedArray()
|
||||
|
||||
# last step: ------------------------------
|
||||
FreeCAD.ActiveDocument.RecomputesFrozen = True
|
||||
# 3. Adjust to terrain:
|
||||
self.createFrameFromPoints(dataframe)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
FreeCAD.ActiveDocument.RecomputesFrozen = False
|
||||
params.SetBool("AutoSaveEnabled", auto_save_enabled)
|
||||
|
||||
total_time = datetime.now() - starttime
|
||||
print(" -- Tiempo tardado:", total_time)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
return True
|
||||
#return True
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1066,7 +1074,7 @@ def ConvertObjectsTo(sel, objTo):
|
||||
|
||||
|
||||
## Comandos: -----------------------------------------------------------------------------------------------------------
|
||||
class _CommandPVPlantPlacement:
|
||||
class CommandPVPlantPlacement:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "way.svg")),
|
||||
@@ -1085,7 +1093,7 @@ class _CommandPVPlantPlacement:
|
||||
return False
|
||||
|
||||
|
||||
class _CommandAdjustToTerrain:
|
||||
class CommandAdjustToTerrain:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "adjust.svg")),
|
||||
@@ -1108,7 +1116,7 @@ class _CommandAdjustToTerrain:
|
||||
return False
|
||||
|
||||
|
||||
class _CommandConvert:
|
||||
class CommandConvert:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "convert.svg")),
|
||||
'Accel': "P, C",
|
||||
@@ -1126,7 +1134,7 @@ class _CommandConvert:
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
'''if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantPlacement', _CommandPVPlantPlacement())
|
||||
FreeCADGui.addCommand('PVPlantAdjustToTerrain', _CommandAdjustToTerrain())
|
||||
FreeCADGui.addCommand('PVPlantConvertTo', _CommandConvert())
|
||||
FreeCADGui.addCommand('PVPlantConvertTo', _CommandConvert())'''
|
||||
|
||||
@@ -578,22 +578,22 @@ class _PVPlantSite(ArchSite._Site):
|
||||
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"Boundary",
|
||||
"Site",
|
||||
"PVPlant",
|
||||
"Boundary of land")
|
||||
|
||||
obj.addProperty("App::PropertyLinkList",
|
||||
"Frames",
|
||||
"Site",
|
||||
"PVPlant",
|
||||
"Frames templates")
|
||||
|
||||
obj.addProperty("App::PropertyEnumeration",
|
||||
"UtmZone",
|
||||
"Base",
|
||||
"PVPlant",
|
||||
"UTM zone").UtmZone = zone_list
|
||||
|
||||
obj.addProperty("App::PropertyVector",
|
||||
"Origin",
|
||||
"Base",
|
||||
"PVPlant",
|
||||
"Origin point.").Origin = (0, 0, 0)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
@@ -771,10 +771,12 @@ class _PVPlantSite(ArchSite._Site):
|
||||
import PVPlantImportGrid
|
||||
x, y, zone_number, zone_letter = utm.from_latlon(lat, lon)
|
||||
self.obj.UtmZone = zone_list[zone_number - 1]
|
||||
# self.obj.UtmZone = "Z"+str(zone_number)
|
||||
#z = PVPlantImportGrid.get_elevation(lat, lon)
|
||||
zz = PVPlantImportGrid.getSinglePointElevationFromBing(lat, lon)
|
||||
self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz.z)
|
||||
zz = PVPlantImportGrid.getElevationFromOE([[lat, lon]])
|
||||
self.obj.Origin = FreeCAD.Vector(x * 1000, y * 1000, zz[0].z)
|
||||
#self.obj.OriginOffset = FreeCAD.Vector(x * 1000, y * 1000, 0) #??
|
||||
self.obj.Latitude = lat
|
||||
self.obj.Longitude = lon
|
||||
self.obj.Elevation = zz[0].z
|
||||
|
||||
|
||||
class _ViewProviderSite(ArchSite._ViewProviderSite):
|
||||
@@ -1171,7 +1173,7 @@ class _ViewProviderSite:
|
||||
|
||||
'''
|
||||
|
||||
class _CommandPVPlantSite:
|
||||
'''class _CommandPVPlantSite:
|
||||
"the Arch Site command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -1189,4 +1191,4 @@ class _CommandPVPlantSite:
|
||||
return
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantSite', _CommandPVPlantSite())
|
||||
FreeCADGui.addCommand('PVPlantSite', _CommandPVPlantSite())'''
|
||||
|
||||
@@ -120,12 +120,12 @@ class Terrain(ArchComponent.Component):
|
||||
"Surface",
|
||||
"Use a Point Group to generate the surface")
|
||||
|
||||
if not ("Mesh" in pl):
|
||||
if not ("mesh" in pl):
|
||||
obj.addProperty("Mesh::PropertyMeshKernel",
|
||||
"Mesh",
|
||||
"mesh",
|
||||
"Surface",
|
||||
"Mesh")
|
||||
obj.setEditorMode("Mesh", 1)
|
||||
obj.setEditorMode("mesh", 1)
|
||||
|
||||
if not ("InitialMesh" in pl):
|
||||
obj.addProperty("Mesh::PropertyMeshKernel",
|
||||
@@ -156,7 +156,7 @@ class Terrain(ArchComponent.Component):
|
||||
'''Do something when a property has changed'''
|
||||
|
||||
if prop == "InitialMesh":
|
||||
obj.Mesh = obj.InitialMesh.copy()
|
||||
obj.mesh = obj.InitialMesh.copy()
|
||||
|
||||
if prop == "DEM" or prop == "CuttingBoundary":
|
||||
from datetime import datetime
|
||||
@@ -197,11 +197,10 @@ class Terrain(ArchComponent.Component):
|
||||
del templist
|
||||
|
||||
# create xy coordinates
|
||||
import PVPlantSite
|
||||
offset = PVPlantSite.get().Origin
|
||||
x = 1000 * (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) - offset.x
|
||||
y = 1000 * (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) - offset.y
|
||||
datavals = 1000 * datavals # - offset.z
|
||||
offset = self.site.Origin
|
||||
x = (cellsize * np.arange(nx)[0::coarse_factor] + xllvalue) * 1000 - offset.x
|
||||
y = (cellsize * np.arange(ny)[-1::-1][0::coarse_factor] + yllvalue) * 1000 - offset.y
|
||||
datavals = datavals * 1000 # Ajuste de altura
|
||||
|
||||
# remove points out of area
|
||||
# 1. coarse:
|
||||
@@ -210,7 +209,6 @@ class Terrain(ArchComponent.Component):
|
||||
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]
|
||||
print(tmp)
|
||||
x_max = np.ndarray.max(tmp)
|
||||
x_min = np.ndarray.min(tmp)
|
||||
|
||||
@@ -249,10 +247,10 @@ class Terrain(ArchComponent.Component):
|
||||
pts.append([x[i], y[j], datavals[j][i]])
|
||||
if len(pts) > 3:
|
||||
try:
|
||||
mesh.addMesh(Triangulation.Triangulate(pts))
|
||||
#Mesh.show(mesh)
|
||||
triangulated = Triangulation.Triangulate(pts)
|
||||
mesh.addMesh(triangulated)
|
||||
except TypeError:
|
||||
print("error al procesar: {0} puntos".format(len(pts)))
|
||||
print(f"Error al procesar {len(pts)} puntos: {str(e)}")
|
||||
|
||||
mesh.removeDuplicatedPoints()
|
||||
mesh.removeFoldsOnSurface()
|
||||
@@ -284,12 +282,9 @@ class Terrain(ArchComponent.Component):
|
||||
|
||||
import MeshTools.Triangulation as Triangulation
|
||||
mesh = Triangulation.Triangulate(Data)
|
||||
'''shape = PVPlantCreateTerrainMesh.MeshToShape(mesh)
|
||||
shape.Placement.move(nbase)'''
|
||||
|
||||
obj.Shape = shape
|
||||
if obj.DEM:
|
||||
obj.DEM = None
|
||||
obj.mesh = mesh
|
||||
|
||||
def execute(self, obj):
|
||||
''''''
|
||||
@@ -307,7 +302,6 @@ class ViewProviderTerrain:
|
||||
"A View Provider for the Pipe object"
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.Object = vobj.Object
|
||||
self.boundary_color = None
|
||||
self.edge_style = None
|
||||
self.edge_color = None
|
||||
@@ -321,16 +315,16 @@ class ViewProviderTerrain:
|
||||
# Triangulation properties.
|
||||
pl = vobj.PropertiesList
|
||||
if not ("Transparency" in pl):
|
||||
vobj.addProperty("App::PropertyIntegerConstraint",
|
||||
'''vobj.addProperty("App::PropertyIntegerConstraint",
|
||||
"Transparency",
|
||||
"Surface Style",
|
||||
"Set triangle face transparency").Transparency = (50, 0, 100, 1)
|
||||
"Set triangle face transparency").Transparency = (50, 0, 100, 1)'''
|
||||
|
||||
if not ("ShapeColor" in pl):
|
||||
vobj.addProperty("App::PropertyColor",
|
||||
"ShapeColor",
|
||||
"Surface Style",
|
||||
"Set triangle face color").ShapeColor = (r, g, b, vobj.Transparency / 100)
|
||||
"Set triangle face color").ShapeColor = (0.0, 0.667, 0.49, vobj.Transparency / 100)
|
||||
|
||||
if not ("ShapeMaterial" in pl):
|
||||
vobj.addProperty("App::PropertyMaterial",
|
||||
@@ -413,18 +407,21 @@ class ViewProviderTerrain:
|
||||
"Set major contour line width").MinorWidth = (2.0, 1.0, 20.0, 1.0)
|
||||
|
||||
vobj.Proxy = self
|
||||
self.Object = vobj.Object
|
||||
# Inicializar colores correctamente
|
||||
vobj.ShapeMaterial.DiffuseColor = vobj.ShapeColor
|
||||
|
||||
def onDocumentRestored(self, vobj):
|
||||
self.setProperties(vobj)
|
||||
|
||||
def onChanged(self, vobj, prop):
|
||||
''' Update Object visuals when a view property changed. '''
|
||||
""" Update Object visuals when a view property changed. """
|
||||
|
||||
if prop == "ShapeColor" or prop == "Transparency":
|
||||
if hasattr(vobj, "ShapeColor") and hasattr(vobj, "Transparency"):
|
||||
color = vobj.getPropertyByName("ShapeColor")
|
||||
transparency = vobj.getPropertyByName("Transparency")
|
||||
color = (color[0], color[1], color[2], transparency / 100)
|
||||
color = (color[0], color[1], color[2], 50 / 100)
|
||||
vobj.ShapeMaterial.DiffuseColor = color
|
||||
|
||||
if prop == "ShapeMaterial":
|
||||
@@ -555,7 +552,7 @@ class ViewProviderTerrain:
|
||||
highlight.addChild(mat_binding)
|
||||
highlight.addChild(self.geo_coords)
|
||||
highlight.addChild(self.triangles)
|
||||
highlight.addChild(boundaries)
|
||||
#highlight.addChild(boundaries)
|
||||
|
||||
# Face root.
|
||||
face = coin.SoSeparator()
|
||||
@@ -573,14 +570,14 @@ class ViewProviderTerrain:
|
||||
surface_root.addChild(face)
|
||||
surface_root.addChild(offset)
|
||||
surface_root.addChild(edge)
|
||||
surface_root.addChild(major_contours)
|
||||
surface_root.addChild(minor_contours)
|
||||
#surface_root.addChild(major_contours)
|
||||
#surface_root.addChild(minor_contours)
|
||||
vobj.addDisplayMode(surface_root, "Surface")
|
||||
|
||||
# Boundary root.
|
||||
boundary_root = coin.SoSeparator()
|
||||
boundary_root.addChild(boundaries)
|
||||
vobj.addDisplayMode(boundary_root, "Boundary")
|
||||
#boundary_root = coin.SoSeparator()
|
||||
#boundary_root.addChild(boundaries)
|
||||
#vobj.addDisplayMode(boundary_root, "Boundary")
|
||||
|
||||
# Elevation/Shaded root.
|
||||
'''shaded_root = coin.SoSeparator()
|
||||
@@ -599,8 +596,8 @@ class ViewProviderTerrain:
|
||||
# Wireframe root.
|
||||
wireframe_root = coin.SoSeparator()
|
||||
wireframe_root.addChild(edge)
|
||||
wireframe_root.addChild(major_contours)
|
||||
wireframe_root.addChild(minor_contours)
|
||||
#wireframe_root.addChild(major_contours)
|
||||
#wireframe_root.addChild(minor_contours)
|
||||
vobj.addDisplayMode(wireframe_root, "Wireframe")
|
||||
|
||||
# Take features from properties.
|
||||
@@ -629,19 +626,19 @@ class ViewProviderTerrain:
|
||||
'''
|
||||
|
||||
if prop == "Mesh":
|
||||
print("update terrain mesh")
|
||||
mesh = obj.Mesh
|
||||
copy_mesh = mesh.copy()
|
||||
# copy_mesh.Placement.move(origin.Origin)
|
||||
if obj.mesh:
|
||||
print("Mostrar mesh")
|
||||
|
||||
triangles = []
|
||||
for i in copy_mesh.Topology[1]:
|
||||
triangles.extend(list(i))
|
||||
triangles.append(-1)
|
||||
mesh = obj.mesh
|
||||
vertices = [tuple(v) for v in mesh.Topology[0]]
|
||||
faces = []
|
||||
for face in mesh.Topology[1]:
|
||||
faces.extend(face)
|
||||
faces.append(-1)
|
||||
|
||||
self.geo_coords.point.values = copy_mesh.Topology[0]
|
||||
self.triangles.coordIndex.values = triangles
|
||||
del copy_mesh
|
||||
# Asignar a los nodos de visualización
|
||||
self.geo_coords.point.values = vertices # <-- ¡Clave!
|
||||
self.triangles.coordIndex.values = faces # <-- ¡Clave!
|
||||
|
||||
def getDisplayModes(self, vobj):
|
||||
''' Return a list of display modes. '''
|
||||
@@ -656,7 +653,9 @@ class ViewProviderTerrain:
|
||||
return "Surface"
|
||||
|
||||
def claimChildren(self):
|
||||
return [self.Object.CuttingBoundary, ]
|
||||
if hasattr(self, "Object") and self.Object:
|
||||
return [self.Object.CuttingBoundary, ]
|
||||
return []
|
||||
|
||||
def getIcon(self):
|
||||
return str(os.path.join(DirIcons, "terrain.svg"))
|
||||
@@ -670,7 +669,7 @@ class ViewProviderTerrain:
|
||||
return None
|
||||
|
||||
|
||||
class _CommandTerrain:
|
||||
'''class _CommandTerrain:
|
||||
"the PVPlant Terrain command definition"
|
||||
|
||||
def GetResources(self):
|
||||
@@ -692,4 +691,4 @@ class _CommandTerrain:
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Terrain', _CommandTerrain())
|
||||
FreeCADGui.addCommand('Terrain', _CommandTerrain())'''
|
||||
|
||||
@@ -23,6 +23,7 @@ except AttributeError:
|
||||
import os
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
|
||||
def Mest2FemMesh(obj):
|
||||
import Fem
|
||||
|
||||
@@ -44,6 +45,7 @@ def Mest2FemMesh(obj):
|
||||
FreeCAD.activeDocument().recompute()
|
||||
return obj2
|
||||
|
||||
|
||||
def makeContours(land, minor = 1000, mayor = 5000,
|
||||
minorColor=(0.0, 0.00, 0.80), mayorColor=(0.00, 0.00, 1.00),
|
||||
minorThickness = 2, mayorThickness = 5,
|
||||
@@ -58,6 +60,7 @@ def makeContours(land, minor = 1000, mayor = 5000,
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
def Contours_Mesh(Mesh, minor, mayor,
|
||||
minorColor, mayorColor,
|
||||
minorLineWidth, mayorLineWidth,
|
||||
@@ -144,6 +147,7 @@ def Contours_Mesh(Mesh, minor, mayor,
|
||||
calculateSection(minor_array)
|
||||
calculateSection(mayor_array)
|
||||
|
||||
|
||||
def Contours_Part(Terrain, minor, mayor,
|
||||
minorColor, mayorColor,
|
||||
minorLineWidth, mayorLineWidth,
|
||||
@@ -235,6 +239,8 @@ def Contours_Part(Terrain, minor, mayor,
|
||||
calculateSection(mayor_array)
|
||||
|
||||
# Base widget for task panel terrain analisys
|
||||
|
||||
|
||||
class _generalTaskPanel:
|
||||
'''The TaskPanel for Slope setup'''
|
||||
|
||||
@@ -324,7 +330,7 @@ class _generalTaskPanel:
|
||||
self.ranges[curentIndex.row()][2] = (color.red()/255, color.green()/255, color.blue()/255)
|
||||
|
||||
# Contours Analisys: ---------------------------------------------------------------------------------
|
||||
class _ContourTaskPanel():
|
||||
class ContourTaskPanel():
|
||||
'''The editmode TaskPanel for contours generator'''
|
||||
|
||||
def __init__(self):
|
||||
@@ -480,7 +486,7 @@ class _ContourTaskPanel():
|
||||
return True
|
||||
|
||||
# Height Analisys: ---------------------------------------------------------------------------------
|
||||
class _HeightTaskPanel(_generalTaskPanel):
|
||||
class HeightTaskPanel(_generalTaskPanel):
|
||||
'''The TaskPanel for Slope setup'''
|
||||
|
||||
def __init__(self):
|
||||
@@ -513,7 +519,7 @@ class _HeightTaskPanel(_generalTaskPanel):
|
||||
return True
|
||||
|
||||
# Slope Analisys: ---------------------------------------------------------------------------------
|
||||
class _SlopeTaskPanel(_generalTaskPanel):
|
||||
class SlopeTaskPanel(_generalTaskPanel):
|
||||
'''The TaskPanel for Slope setup'''
|
||||
|
||||
def __init__(self):
|
||||
@@ -603,7 +609,7 @@ class _SlopeTaskPanel(_generalTaskPanel):
|
||||
return True
|
||||
|
||||
# Orientation Analisys: ---------------------------------------------------------------------------------
|
||||
class _OrientationTaskPanel(_generalTaskPanel):
|
||||
class OrientationTaskPanel(_generalTaskPanel):
|
||||
'''The TaskPanel for Orientation setup'''
|
||||
|
||||
def __init__(self):
|
||||
@@ -707,7 +713,7 @@ class _OrientationTaskPanel(_generalTaskPanel):
|
||||
|
||||
## Commands ----------------------------------------------------------------------------------------------------------
|
||||
## 1. Contours:
|
||||
class _CommandContours:
|
||||
'''class _CommandContours:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "TerrainContours.svg")),
|
||||
'Accel': "T, C",
|
||||
@@ -803,4 +809,4 @@ if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('SlopeAnalisys', _CommandSlopeAnalisys())
|
||||
FreeCADGui.addCommand('HeightAnalisys', _CommandHeightAnalisys())
|
||||
FreeCADGui.addCommand('OrientationAnalisys', _CommandOrientationAnalisys())
|
||||
FreeCADGui.addCommand('TerrainAnalisys', CommandTerrainAnalisysGroup())
|
||||
FreeCADGui.addCommand('TerrainAnalisys', CommandTerrainAnalisysGroup())'''
|
||||
715
PVPlantTools.py
Normal file
715
PVPlantTools.py
Normal file
@@ -0,0 +1,715 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2017 - Amritpal Singh <amrit3701@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 *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "RebarCommands"
|
||||
__author__ = "Amritpal Singh"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
|
||||
import FreeCADGui, FreeCAD
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
import os
|
||||
|
||||
|
||||
class CommandPVPlantSite:
|
||||
"the PVPlant Site command definition"
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "icon.svg")),
|
||||
'MenuText': QT_TRANSLATE_NOOP("Arch_Site", "Site"),
|
||||
'Accel': "S, I",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Arch_Site", "Creates a site object including selected objects.")}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return ((not (FreeCAD.ActiveDocument is None)) and
|
||||
(FreeCAD.ActiveDocument.getObject("Site") is None))
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantSite
|
||||
PVPlantSite.makePVPlantSite()
|
||||
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:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "flash.svg")),
|
||||
'Accel': "P, S",
|
||||
'MenuText': "Project Setup",
|
||||
'ToolTip': "Setup all the variable for this project"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project import ProjectSetup
|
||||
taskd = ProjectSetup.ProjectSetupDialog()
|
||||
taskd.setParent(FreeCADGui.getMainWindow())
|
||||
taskd.setWindowFlags(QtCore.Qt.Window)
|
||||
taskd.show()
|
||||
|
||||
|
||||
class CommandTerrain:
|
||||
"the PVPlant Terrain command definition"
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "terrain.svg")),
|
||||
'MenuText': "Terrain",
|
||||
'Accel': "S, T",
|
||||
'ToolTip': "Creates a Terrain object from setup dialog."}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return (not (FreeCAD.ActiveDocument is None) and
|
||||
not (FreeCAD.ActiveDocument.getObject("Site") is None) and
|
||||
(FreeCAD.ActiveDocument.getObject("Terrain") is None))
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantTerrain
|
||||
PVPlantTerrain.makeTerrain()
|
||||
# task = _TerrainTaskPanel()
|
||||
# FreeCADGui.Control.showDialog(task)
|
||||
return
|
||||
|
||||
|
||||
class CommandCreateTerrainMesh:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "surface.svg")),
|
||||
'MenuText': QT_TRANSLATE_NOOP("PVPlant", "Create Surface"),
|
||||
'Accel': "C, S",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("PVPlant", "Creates a surface form a cloud of points.")}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantCreateTerrainMesh
|
||||
TaskPanel = PVPlantCreateTerrainMesh.TaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
|
||||
|
||||
class CommandDivideArea:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "area.svg")),
|
||||
'Accel': "A, D",
|
||||
'MenuText': "Divide Area",
|
||||
'ToolTip': "Allowed Area"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
sel = FreeCADGui.Selection.getSelection()[0]
|
||||
|
||||
|
||||
class CommandBoundary:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "area.svg")),
|
||||
'Accel': "A, B",
|
||||
'MenuText': "Area",
|
||||
'ToolTip': "Allowed Area"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project.Area import PVPlantArea
|
||||
sel = FreeCADGui.Selection.getSelection()[0]
|
||||
obj = PVPlantArea.makeArea([ver.Point for ver in sel.Shape.Vertexes])
|
||||
# taskd = _PVPlantPlacementTaskPanel()
|
||||
# FreeCADGui.Control.showDialog(taskd)
|
||||
|
||||
|
||||
class CommandFrameArea:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "FrameArea.svg")),
|
||||
'Accel': "A, F",
|
||||
'MenuText': "Frame Area",
|
||||
'ToolTip': "Frame Area"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
makeFramedArea(None, sel)
|
||||
|
||||
|
||||
class CommandProhibitedArea:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "area_forbidden.svg")),
|
||||
'Accel': "A, F",
|
||||
'MenuText': "Prohibited Area",
|
||||
'ToolTip': "Prohibited Area"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project.Area import PVPlantArea
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
PVPlantArea.makeProhibitedArea(sel[0])
|
||||
|
||||
|
||||
class CommandPVSubplant:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "subplant.svg")),
|
||||
'Accel': "A, P",
|
||||
'MenuText': "PV Subplant",
|
||||
'ToolTip': "PV Subplant"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project.Area import PVPlantArea
|
||||
area = PVPlantArea.makePVSubplant()
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
for obj in sel:
|
||||
if obj.Name[:7] == "Tracker":
|
||||
frame_list = area.Frames
|
||||
frame_list.append(obj)
|
||||
area.Frames = frame_list
|
||||
|
||||
|
||||
class CommandOffsetArea:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "offset.svg")),
|
||||
'Accel': "A, O",
|
||||
'MenuText': "OffsetArea",
|
||||
'ToolTip': "OffsetArea"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project.Area import PVPlantArea
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
base = None
|
||||
if sel:
|
||||
base = sel[0]
|
||||
obj = PVPlantArea.makeOffsetArea(base)
|
||||
|
||||
|
||||
class CommandSplitArea:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "split_area.svg")),
|
||||
'Accel': "A, S",
|
||||
'MenuText': "Split Area",
|
||||
'ToolTip': "Split Area"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return (not FreeCAD.ActiveDocument is None and
|
||||
not FreeCAD.ActiveDocument.findObjects(Name="ProhibitedArea") is None and
|
||||
not FreeCAD.ActiveDocument.findObjects(Name="OffsetArea") is None)
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project.Area import PVPlantAreaUtils
|
||||
TaskPanel = PVPlantAreaUtils.splitAreaTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
return
|
||||
|
||||
|
||||
class CommandJoinAreas:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "split_area.svg")),
|
||||
'Accel': "A, J",
|
||||
'MenuText': "Join Areas",
|
||||
'ToolTip': "Join Areas"}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return (not FreeCAD.ActiveDocument is None and
|
||||
not FreeCAD.ActiveDocument.findObjects(Name="ProhibitedArea") is None and
|
||||
not FreeCAD.ActiveDocument.findObjects(Name="OffsetArea") is None)
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
from Project.Area import PVPlantAreaUtils
|
||||
TaskPanel = PVPlantAreaUtils.splitAreaTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
return
|
||||
|
||||
|
||||
class CommandContours:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "TerrainContours.svg")),
|
||||
'Accel': "T, C",
|
||||
'MenuText': 'Curvas de nivel',
|
||||
'ToolTip': 'Curvas de nivel'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
# return not FreeCAD.ActiveDocument is None
|
||||
if FreeCAD.ActiveDocument is None:
|
||||
return False
|
||||
return True
|
||||
if FreeCADGui.Selection.getSelection() is not None:
|
||||
selection = FreeCADGui.Selection.getSelection()[-1]
|
||||
if selection.TypeId == 'Mesh::Feature':
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantTerrainAnalisys
|
||||
TaskPanel = PVPlantTerrainAnalisys.ContourTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
|
||||
|
||||
class CommandSlopeAnalisys:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "TerrainSlope.svg")),
|
||||
'Accel': "T, S",
|
||||
'MenuText': 'Analisis de Pendiente',
|
||||
'ToolTip': 'Analisis de Pendiente'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantTerrainAnalisys
|
||||
TaskPanel = PVPlantTerrainAnalisys.SlopeTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
|
||||
|
||||
class CommandHeightAnalisys:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "TerrainHeight.svg")),
|
||||
'Accel': "T, H",
|
||||
'MenuText': 'Analisis de Altura',
|
||||
'ToolTip': 'Analisis de Altura'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantTerrainAnalisys
|
||||
TaskPanel = PVPlantTerrainAnalisys.HeightTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
|
||||
|
||||
class CommandOrientationAnalisys:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "TerrainOrientation.svg")),
|
||||
'Accel': "T, H",
|
||||
'MenuText': 'Analisis de Orientación',
|
||||
'ToolTip': 'Analisis de Orientación'}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantTerrainAnalisys
|
||||
TaskPanel = PVPlantTerrainAnalisys.OrientationTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
|
||||
|
||||
class CommandTrench: # V1:
|
||||
"""Gui command for the Line tool."""
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
"""Set icon, menu and tooltip."""
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "trench.svg")),
|
||||
'MenuText': "Trench",
|
||||
'Accel': "C, T",
|
||||
'ToolTip': "Creates a Trench object from setup dialog."}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
active = not (FreeCAD.ActiveDocument is None)
|
||||
terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None)
|
||||
active = active and terrain
|
||||
if terrain:
|
||||
active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
|
||||
return active
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
"""Execute when the command is called."""
|
||||
from Civil import PVPlantTrench
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
done = False
|
||||
|
||||
if len(sel) > 0:
|
||||
import Draft
|
||||
for obj in sel:
|
||||
if Draft.getType(obj) == "Wire":
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Trench")
|
||||
PVPlantTrench.makeTrench(obj)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
done = True
|
||||
break
|
||||
|
||||
if not done:
|
||||
taskd = PVPlantTrench.TrenchTaskPanel()
|
||||
if taskd:
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
else:
|
||||
print(" No ha sido posible crear el formulario")
|
||||
|
||||
|
||||
class CommandSemiAutomaticTrench: # V1:
|
||||
"""Gui command for the Line tool."""
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
"""Set icon, menu and tooltip."""
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "trench.svg")),
|
||||
'MenuText': "Semi-Automatic Trench Generator",
|
||||
'Accel': "T, S",
|
||||
'ToolTip': "Creates a Trench object from setup dialog."}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
active = not (FreeCAD.ActiveDocument is None)
|
||||
terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None)
|
||||
active = active and terrain
|
||||
if terrain:
|
||||
active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
|
||||
return active
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
"""Execute when the command is called."""
|
||||
from Civil import PVPlantTrench
|
||||
semi = PVPlantTrench.semiAutomaticTrench()
|
||||
|
||||
|
||||
class CommandCalculateEarthworks:
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "pico.svg")),
|
||||
'Accel': "C, E",
|
||||
'MenuText': QT_TRANSLATE_NOOP("Placement", "Movimiento de tierras"),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el movimiento de tierras")}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
active = not (FreeCAD.ActiveDocument is None)
|
||||
if not (FreeCAD.ActiveDocument.getObject("Terrain") is None):
|
||||
active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
|
||||
return active
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantEarthWorks
|
||||
TaskPanel = PVPlantEarthWorks.EarthWorksTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
|
||||
|
||||
class CommandManhole:
|
||||
"the PVPlant Manhole command definition"
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "manhole.svg")),
|
||||
'MenuText': "Manhole",
|
||||
'Accel': "C, M",
|
||||
'ToolTip': "Creates a Manhole object from setup dialog."}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not (FreeCAD.ActiveDocument is None)
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
if FreeCADGui.Selection.getCompleteSelection():
|
||||
for ob in FreeCAD.ActiveDocument.Objects:
|
||||
if ob.Name[:4] == "Site":
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def Activated():
|
||||
import PVPlantManhole
|
||||
task_panel = PVPlantManhole._ManholeTaskPanel()
|
||||
FreeCADGui.Control.showDialog(task_panel)
|
||||
return
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantSite', CommandPVPlantSite())
|
||||
import PVPlantGeoreferencing
|
||||
|
||||
FreeCADGui.addCommand('PVPlantGeoreferencing', PVPlantGeoreferencing.CommandPVPlantGeoreferencing())
|
||||
FreeCADGui.addCommand('ProjectSetup', CommandProjectSetup())
|
||||
FreeCADGui.addCommand('Terrain', CommandTerrain())
|
||||
FreeCADGui.addCommand('PVPlantCreateTerrainMesh', CommandCreateTerrainMesh())
|
||||
|
||||
|
||||
class CommandAreaGroup:
|
||||
@staticmethod
|
||||
def GetCommands():
|
||||
return tuple([ # 'Area',
|
||||
'FrameArea',
|
||||
'ForbiddenArea',
|
||||
'PVSubplant',
|
||||
'OffsetArea'
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'MenuText': 'Areas',
|
||||
'ToolTip': 'Areas'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
|
||||
# FreeCADGui.addCommand('Area', CommandBoundary())
|
||||
FreeCADGui.addCommand('FrameArea', CommandFrameArea())
|
||||
FreeCADGui.addCommand('ForbiddenArea', CommandProhibitedArea())
|
||||
FreeCADGui.addCommand('PVSubplant', CommandPVSubplant())
|
||||
FreeCADGui.addCommand('OffsetArea', CommandOffsetArea())
|
||||
FreeCADGui.addCommand('PVPlantAreas', CommandAreaGroup())
|
||||
|
||||
FreeCADGui.addCommand('SplitArea', CommandSplitArea())
|
||||
FreeCADGui.addCommand('JoinAreas', CommandJoinAreas())
|
||||
|
||||
|
||||
class CommandTerrainAnalisysGroup:
|
||||
@staticmethod
|
||||
def GetCommands():
|
||||
return tuple(['Contours',
|
||||
'HeightAnalisys',
|
||||
'SlopeAnalisys',
|
||||
'OrientationAnalisys'
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'MenuText': QT_TRANSLATE_NOOP("", 'Terrain Analisys'),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("", 'Terrain Analisys')
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
|
||||
FreeCADGui.addCommand('Contours', CommandContours())
|
||||
FreeCADGui.addCommand('SlopeAnalisys', CommandSlopeAnalisys())
|
||||
FreeCADGui.addCommand('HeightAnalisys', CommandHeightAnalisys())
|
||||
FreeCADGui.addCommand('OrientationAnalisys', CommandOrientationAnalisys())
|
||||
FreeCADGui.addCommand('TerrainAnalisys', CommandTerrainAnalisysGroup())
|
||||
|
||||
|
||||
class CommandTrenchGroup:
|
||||
@staticmethod
|
||||
def GetCommands():
|
||||
return tuple(['PVPlantTrench',
|
||||
'PVPlantSemiAutomaticTrench',
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def GetResources():
|
||||
return {'MenuText': 'Rack Types',
|
||||
'ToolTip': 'Rack Types'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def IsActive():
|
||||
active = not (FreeCAD.ActiveDocument is None)
|
||||
terrain = not (FreeCAD.ActiveDocument.getObject("Terrain") is None)
|
||||
active = active and terrain
|
||||
if terrain:
|
||||
active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None)
|
||||
return active
|
||||
|
||||
|
||||
FreeCADGui.addCommand('PVPlantTrench', CommandTrench())
|
||||
FreeCADGui.addCommand('PVPlantSemiAutomaticTrench', CommandSemiAutomaticTrench())
|
||||
FreeCADGui.addCommand('Trenches', CommandTrenchGroup())
|
||||
FreeCADGui.addCommand('PVPlantEarthworks', CommandCalculateEarthworks())
|
||||
|
||||
FreeCADGui.addCommand('PVPlantManhole', CommandManhole())
|
||||
|
||||
import PVPlantPlacement
|
||||
FreeCADGui.addCommand('PVPlantPlacement', PVPlantPlacement.CommandPVPlantPlacement())
|
||||
FreeCADGui.addCommand('PVPlantAdjustToTerrain', PVPlantPlacement.CommandAdjustToTerrain())
|
||||
FreeCADGui.addCommand('PVPlantConvertTo', PVPlantPlacement.CommandConvert())
|
||||
|
||||
import hydro.hydrological as hydro
|
||||
FreeCADGui.addCommand('HydrologicalAnalysis', hydro.CommandHydrologicalAnalysis())
|
||||
|
||||
import Vegetation.PVPlantTreeGenerator as TreeGenerator
|
||||
FreeCADGui.addCommand('PVPlantTree', TreeGenerator.CommandTree())
|
||||
|
||||
import Project.GenerateExternalDocument as GED
|
||||
FreeCADGui.addCommand('newExternalDocument', GED.CommandGenerateExternalDocument())
|
||||
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
class CommandRackGroup:
|
||||
|
||||
def GetCommands(self):
|
||||
return tuple(['PVPlantFixedRack',
|
||||
'PVPlantTrackerSetup',
|
||||
'PVPlantTracker'
|
||||
])
|
||||
|
||||
def GetResources(self):
|
||||
return {'MenuText': QT_TRANSLATE_NOOP("", 'Rack Types'),
|
||||
'ToolTip': QT_TRANSLATE_NOOP("", 'Rack Types')
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
FreeCADGui.addCommand('PVPlantFixedRack', PVPlantFrame.CommandFixedRack())
|
||||
FreeCADGui.addCommand('PVPlantTrackerSetup', PVPlantFrame.CommandTrackerSetup())
|
||||
FreeCADGui.addCommand('PVPlantTracker', PVPlantFrame.CommandTracker())
|
||||
FreeCADGui.addCommand('RackType', CommandRackGroup())
|
||||
|
||||
|
||||
import PVPlantFence
|
||||
FreeCADGui.addCommand('PVPlantFenceGroup', PVPlantFence.CommandFenceGroup())
|
||||
|
||||
projectlist = [ # "Reload",
|
||||
"PVPlantSite",
|
||||
"ProjectSetup",
|
||||
"PVPlantGeoreferencing",
|
||||
"Separator",
|
||||
# "ImportGrid",
|
||||
"Terrain",
|
||||
"TerrainAnalisys",
|
||||
"PVPlantCreateTerrainMesh",
|
||||
"Separator",
|
||||
# "PointsGroup",
|
||||
"PVPlantAreas",
|
||||
"SplitArea",
|
||||
"Separator",
|
||||
"Trenches",
|
||||
"PVPlantEarthworks",
|
||||
# "PVPlantPad",
|
||||
# "PVPlantRoad",
|
||||
"PVPlantManhole",
|
||||
# "PVPlantFoundation"
|
||||
# "GraphTerrainProfile",
|
||||
# "Trace",
|
||||
"Separator",
|
||||
'HydrologicalAnalysis',
|
||||
'newExternalDocument',
|
||||
]
|
||||
|
||||
pv_mechanical = [
|
||||
"RackType",
|
||||
"PVPlantPlacement",
|
||||
"PVPlantAdjustToTerrain",
|
||||
"PVPlantConvertTo",
|
||||
]
|
||||
|
||||
objectlist = ['PVPlantTree',
|
||||
'PVPlantFence',]
|
||||
@@ -1,368 +0,0 @@
|
||||
|
||||
import math
|
||||
|
||||
import ArchComponent
|
||||
import FreeCAD
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from DraftTools import translate
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import Part
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "FreeCAD Fixed Rack"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant")
|
||||
DirResources = os.path.join(__dir__, "Resources")
|
||||
DirIcons = os.path.join(DirResources, "Icons")
|
||||
DirImages = os.path.join(DirResources, "Images")
|
||||
|
||||
|
||||
def makeTree():
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tree")
|
||||
Tree(obj)
|
||||
ViewProviderTree(obj.ViewObject)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
try:
|
||||
folder = FreeCAD.ActiveDocument.Vegetation
|
||||
except:
|
||||
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Vegetation')
|
||||
folder.Label = "Vegetation"
|
||||
folder.addObject(obj)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class Tree(ArchComponent.Component):
|
||||
""" A Shadow Tree Obcject """
|
||||
|
||||
def __init__(self, obj):
|
||||
# Definición de variables:
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
self.obj = obj
|
||||
self.setProperties(obj)
|
||||
|
||||
def setProperties(self, obj):
|
||||
# Definicion de Propiedades:
|
||||
pl = obj.PropertiesList
|
||||
|
||||
# CANOPY: ---------------------------------------------------------
|
||||
if not ("CanopyHeight" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"CanopyHeight",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).CanopyHeight = 4000
|
||||
|
||||
if not ("CanopyRadius" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"CanopyRadius",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).CanopyRadius = 1500
|
||||
|
||||
if not ("Spikiness" in pl):
|
||||
obj.addProperty("App::PropertyFloatConstraint",
|
||||
"Spikiness",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).Spikiness = (0.5, 0.0, 1.0, 0.05) # (Default, Start, Finish, Step)
|
||||
|
||||
'''
|
||||
if not ("Lumpiness" in pl):
|
||||
obj.addProperty("App::PropertyFloatConstraint",
|
||||
"Lumpiness",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).Lumpiness = (0.0, 0.0, 1.0, 0.05) #(Default, Start, Finish, Step)'''
|
||||
|
||||
if not ("CrownExpansion" in pl):
|
||||
obj.addProperty("App::PropertyFloatConstraint",
|
||||
"CrownExpansion",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).CrownExpansion = (1.0, 0.0, 2.0, 0.05) # (Default, Start, Finish, Step)
|
||||
|
||||
if not ("UmbrellaEffect" in pl):
|
||||
obj.addProperty("App::PropertyFloatConstraint",
|
||||
"UmbrellaEffect",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).UmbrellaEffect = (0.0, 0.0, 1.0, 0.05) # (Default, Start, Finish, Step)
|
||||
|
||||
if not ("LeafCount" in pl):
|
||||
obj.addProperty("App::PropertyQuantity",
|
||||
"LeafCount",
|
||||
"Canopy",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).LeafCount = 20
|
||||
|
||||
# TRUNK: ------------------------------------------------------------------------------------------------------
|
||||
if not ("TrunkHeight" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"TrunkHeight",
|
||||
"Trunk",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).TrunkHeight = 2000
|
||||
|
||||
if not ("TrunkRadius" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"TrunkRadius",
|
||||
"Trunk",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).TrunkRadius = 150
|
||||
|
||||
if not ("TrunkFaces" in pl):
|
||||
obj.addProperty("App::PropertyQuantity",
|
||||
"TrunkFaces",
|
||||
"Trunk",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The height of self object")
|
||||
).TrunkFaces = 6
|
||||
|
||||
if not ("Type" in pl):
|
||||
obj.addProperty("App::PropertyString",
|
||||
"Type",
|
||||
"Base",
|
||||
"Type").Type = "Vegetable-Tree"
|
||||
obj.setEditorMode("Type", 1)
|
||||
|
||||
self.Type = obj.Type
|
||||
obj.Proxy = self
|
||||
obj.IfcType = "Shading Device"
|
||||
obj.setEditorMode("IfcType", 1)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
'''if prop in ["CanopyHeight", "CanopyHeight", "Spikiness", "CrownExpansion", "UmbrellaEffect",
|
||||
"LeafCount"]:
|
||||
self.canopy = self.createCanopy(obj)
|
||||
|
||||
if prop in ["TrunkHeight", "TrunkRadius", "TrunkFaces"]:
|
||||
self.trunk = self.createTrunk(obj)'''
|
||||
|
||||
def createTrunk(self, obj):
|
||||
import Part
|
||||
angle = (math.pi * 2) / obj.TrunkFaces.Value
|
||||
delta = obj.TrunkRadius.Value
|
||||
pts = [FreeCAD.Vector(delta, 0, 0)]
|
||||
for i in range(int(obj.TrunkFaces.Value) - 1):
|
||||
ang = (i + 1) * angle
|
||||
pts.append(FreeCAD.Vector(delta * math.cos(ang),
|
||||
delta * math.sin(ang),
|
||||
0))
|
||||
pts.append(pts[0])
|
||||
p1 = Part.makePolygon(pts)
|
||||
p0 = p1.makeOffset2D(90, 2, False, False, True)
|
||||
p2 = p1.makeOffset2D(-50, 2, False, False, True)
|
||||
p0.Placement.Base.z = -150
|
||||
p1.Placement.Base.z = 150
|
||||
p2.Placement.Base.z = obj.TrunkHeight.Value - 250
|
||||
return Part.makeLoft([p0, p1, p2], True, True, False)
|
||||
|
||||
def createCanopy(self, obj):
|
||||
import Part
|
||||
import random
|
||||
import Mesh
|
||||
import numpy as np
|
||||
|
||||
ncircles = int(obj.LeafCount.Value)
|
||||
if ncircles % 2 == 0:
|
||||
ncircles += 1
|
||||
half_ncircles = int(ncircles / 2)
|
||||
ncirclesumbrella = int(half_ncircles/2)
|
||||
ncirclestop = ncircles - ncirclesumbrella
|
||||
|
||||
# 1. Create circles to define the sphere
|
||||
circles = []
|
||||
dist = 2 * obj.CanopyRadius.Value / (ncircles - 1)
|
||||
margin = dist * 0.01
|
||||
'''for i in range(half_ncircles + 1):
|
||||
if i > 0:
|
||||
d = (obj.CanopyRadius.Value - dist * i)
|
||||
else:
|
||||
d = obj.CanopyRadius.Value - margin
|
||||
r = (obj.CanopyRadius.Value ** 2 - d ** 2) ** 0.5
|
||||
c = Part.makeCircle(r)
|
||||
circles.append(c)
|
||||
|
||||
ctmp = [c.copy() for c in circles]
|
||||
ctmp.pop()
|
||||
ctmp.reverse()
|
||||
circles.extend(ctmp)'''
|
||||
|
||||
d = - obj.CanopyRadius.Value - dist
|
||||
b = (obj.CanopyRadius.Value ** 2 - (dist * (ncirclesumbrella - 1)) ** 2) ** 0.5
|
||||
|
||||
for i in range(ncircles):
|
||||
d += dist
|
||||
r = (obj.CanopyRadius.Value ** 2 - d ** 2) ** 0.5
|
||||
if i > ncirclesumbrella:
|
||||
if obj.CrownExpansion < 1:
|
||||
r = r * obj.CrownExpansion
|
||||
if r == 0:
|
||||
r = obj.CanopyRadius.Value * 0.01
|
||||
c = Part.makeCircle(r)
|
||||
circles.append(c)
|
||||
|
||||
# 2. Place circles
|
||||
dist = obj.CanopyHeight.Value / ncircles
|
||||
z = 0
|
||||
#zmax = dist * half_ncircles
|
||||
for idx in range(1, half_ncircles):
|
||||
z += dist
|
||||
circles[idx].Placement.Base.z = (1 - obj.UmbrellaEffect) * z
|
||||
#c.Placement.Base.z = obj.UmbrellaEffect * (zmax - z) + z
|
||||
|
||||
dist1 = (obj.CanopyHeight.Value - z) / (half_ncircles + 1)
|
||||
for idx in range(half_ncircles, ncircles):
|
||||
c = circles[idx]
|
||||
z += dist1
|
||||
c.Placement.Base.z = z
|
||||
|
||||
# 3. noise generator
|
||||
pts = []
|
||||
val = (dist / 2 - margin) * obj.Spikiness
|
||||
for c in circles:
|
||||
tmppts = c.discretize(ncircles)
|
||||
for j in range(len(tmppts)):
|
||||
point = tmppts[j]
|
||||
point.x += random.uniform(-val, val)
|
||||
point.y += random.uniform(-val, val)
|
||||
point.z += random.uniform(-val, val)
|
||||
pts.append(point)
|
||||
|
||||
# 4. generate the mesh / solid
|
||||
from scipy import spatial as sp_spatial
|
||||
pts = np.array(pts)
|
||||
hull = sp_spatial.ConvexHull(pts)
|
||||
indices = hull.simplices
|
||||
faces = pts[indices]
|
||||
mesh = Mesh.Mesh(faces.tolist())
|
||||
if len(mesh.Facets) == 0:
|
||||
return None
|
||||
mesh.harmonizeNormals()
|
||||
'''if mesh.Facets[0].Normal.z < 0:
|
||||
mesh.flipNormals()'''
|
||||
shape = Part.Shape()
|
||||
shape.makeShapeFromMesh(mesh.Topology, 0.1)
|
||||
return Part.makeSolid(shape)
|
||||
|
||||
def execute(self, obj):
|
||||
pl = obj.Placement
|
||||
|
||||
trunk = self.createTrunk(obj)
|
||||
canopy = self.createCanopy(obj)
|
||||
canopy.Placement.Base.z = obj.TrunkHeight.Value - 250 # - obj.CanopyRadius.Value / 2
|
||||
obj.Shape = Part.makeCompound([trunk, canopy])
|
||||
obj.Placement = pl
|
||||
|
||||
color = [(0.2510, 0.1255, 0.0)] * len(trunk.Faces)
|
||||
color.extend([(0.0, 0.3922, 0.0)] * len(canopy.Faces))
|
||||
obj.ViewObject.DiffuseColor = color
|
||||
|
||||
|
||||
class ViewProviderTree(ArchComponent.ViewProviderComponent):
|
||||
"A View Provider for the Pipe object"
|
||||
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
|
||||
def getIcon(self):
|
||||
return str(os.path.join(DirIcons, "tree(1).svg"))
|
||||
|
||||
|
||||
class TreeTaskPanel(QtGui.QWidget):
|
||||
def __init__(self, obj=None):
|
||||
QtGui.QWidget.__init__(self)
|
||||
self.obj = obj
|
||||
if self.obj is None:
|
||||
self.obj = makeTree()
|
||||
|
||||
self.form = FreeCADGui.PySideUic.loadUi(__dir__ + "/PVPlantTree.ui")
|
||||
self.layout = QtGui.QHBoxLayout(self)
|
||||
self.layout.setContentsMargins(4, 4, 4, 4)
|
||||
self.layout.addWidget(self.form)
|
||||
|
||||
self.form.editCanopyHeight.valueChanged.connect(self.Canopy)
|
||||
self.form.editCanopyRadius.valueChanged.connect(self.Canopy)
|
||||
self.form.editSpikiness.valueChanged.connect(self.Canopy)
|
||||
self.form.editCrownExpansion.valueChanged.connect(self.Canopy)
|
||||
self.form.editLeftUmbrellaEffect.valueChanged.connect(self.Canopy)
|
||||
self.form.editLeafCount.valueChanged.connect(self.Canopy)
|
||||
|
||||
def Canopy(self):
|
||||
self.obj.CanopyHeight = FreeCAD.Units.Quantity(self.form.editCanopyHeight.text()).Value
|
||||
self.obj.CanopyRadius = FreeCAD.Units.Quantity(self.form.editCanopyRadius.text()).Value
|
||||
self.obj.Spikiness = self.form.editSpikiness.value()
|
||||
self.obj.CrownExpansion = self.form.editCrownExpansion.value()
|
||||
self.obj.UmbrellaEffect = self.form.editLeftUmbrellaEffect.value()
|
||||
self.obj.LeafCount = self.form.editLeafCount.value()
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def accept(self):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
|
||||
class _CommandTree:
|
||||
"the PVPlant Tree command definition"
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "tree(1).svg")),
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantTree", "Tree"),
|
||||
'Accel': "S, T",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlanTree",
|
||||
"Creates a Tree object from setup dialog.")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
import draftguitools.gui_trackers as DraftTrackers
|
||||
self.tree = makeTree()
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint,
|
||||
movecallback=self.mousemove,
|
||||
extradlg=self.taskbox(),
|
||||
title="Position of the tree:")
|
||||
|
||||
def getPoint(self, point=None, obj=None):
|
||||
self.tree.Placement.Base = point
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
self.tracker.finalize()
|
||||
|
||||
def mousemove(self, pt, snapInfo):
|
||||
self.tree.Placement.Base = pt
|
||||
|
||||
def taskbox(self):
|
||||
self.form = TreeTaskPanel(self.tree)
|
||||
return self.form
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantTree', _CommandTree())
|
||||
|
||||
301
Photovoltaic/Module.py
Normal file
301
Photovoltaic/Module.py
Normal file
@@ -0,0 +1,301 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * 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 ArchComponent
|
||||
import FreeCAD
|
||||
import Part
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore
|
||||
import draftguitools.gui_trackers as DraftTrackers
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
try:
|
||||
_fromUtf8 = QtCore.QString.fromUtf8
|
||||
except AttributeError:
|
||||
def _fromUtf8(s):
|
||||
return s
|
||||
|
||||
import os
|
||||
import PVPlantResources
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
|
||||
def makeModule(name="Module"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = name
|
||||
Module(obj)
|
||||
ViewProviderModule(obj.ViewObject)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
return obj
|
||||
|
||||
|
||||
class Module(ArchComponent.Component):
|
||||
"A Manhole Obcject"
|
||||
|
||||
def __init__(self, obj):
|
||||
# Definición de Variables:
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def setProperties(self, obj):
|
||||
pl = obj.PropertiesList
|
||||
# Base de datos: -----------------------------------------------------------------------------------------------
|
||||
if not ("Manufacturer" in pl):
|
||||
obj.addProperty("App::PropertyStringList",
|
||||
"Manufacturer",
|
||||
"Module",
|
||||
"The manufacturer of this object"
|
||||
)
|
||||
|
||||
if not ("Model" in pl):
|
||||
obj.addProperty("App::PropertyStringList",
|
||||
"Model",
|
||||
"Module",
|
||||
"The model of this object"
|
||||
)
|
||||
|
||||
# Dimensions: --------------------------------------------------------------------------------------------------
|
||||
if not ("Height" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"Height",
|
||||
"Manhole",
|
||||
"The height of this object"
|
||||
).Height = 2804
|
||||
|
||||
if not ("Width" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"Width",
|
||||
"Manhole",
|
||||
"The width of this object"
|
||||
).Width = 1303
|
||||
|
||||
if not ("Thickness" in pl):
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"Thickness",
|
||||
"Manhole",
|
||||
"The height of this object"
|
||||
).Thickness = 35
|
||||
|
||||
# Electrical: --------------------------------------------------------------------------------------------------
|
||||
if not ("Power" in pl):
|
||||
obj.addProperty("App::PropertyPower",
|
||||
"Power",
|
||||
"Outputs",
|
||||
"The height of this object"
|
||||
).Power = 650
|
||||
|
||||
if not ("Umpp" in pl):
|
||||
obj.addProperty("App::PropertyElectricCurrent",
|
||||
"Umpp",
|
||||
"Outputs",
|
||||
"The height of this object"
|
||||
).Umpp = 650
|
||||
|
||||
if not ("Impp" in pl):
|
||||
obj.addProperty("App::PropertyElectricCurrent",
|
||||
"Impp",
|
||||
"Outputs",
|
||||
"The height of this object"
|
||||
).Impp = 650
|
||||
|
||||
if not ("Uoc" in pl):
|
||||
obj.addProperty("App::PropertyElectricCurrent",
|
||||
"Uoc",
|
||||
"Outputs",
|
||||
"The height of this object"
|
||||
).Uoc = 650
|
||||
|
||||
if not ("Isc" in pl):
|
||||
obj.addProperty("App::PropertyElectricCurrent",
|
||||
"Isc",
|
||||
"Outputs",
|
||||
"The height of this object"
|
||||
).Isc = 650
|
||||
|
||||
if not ("Isc" in pl):
|
||||
obj.addProperty("App::PropertyElectricCurrent",
|
||||
"Impp",
|
||||
"Outputs",
|
||||
"The height of this object"
|
||||
).Impp = 650
|
||||
|
||||
self.Type = "Module"
|
||||
obj.Proxy = self
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
"""Method run when the document is restored.
|
||||
Re-adds the Arch component, and Arch wall properties."""
|
||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
'''Do something when a property has changed'''
|
||||
|
||||
def execute(self, obj):
|
||||
box = Part.makeBox(obj.Width, obj.Height, obj.Thickness)
|
||||
box.translate(FreeCAD.Vector(-obj.Width, -obj.Height, 0) / 2)
|
||||
obj.Shape = box
|
||||
|
||||
|
||||
class ViewProviderModule(ArchComponent.ViewProviderComponent):
|
||||
"A View Provider for the Module object"
|
||||
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
|
||||
def getIcon(self):
|
||||
return str(os.path.join(DirIcons, "manhole.svg"))
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
"""Method called when the document requests the object to enter edit mode.
|
||||
|
||||
Edit mode is entered when a user double clicks on an object in the tree
|
||||
view, or when they use the menu option [Edit -> Toggle Edit Mode].
|
||||
|
||||
Just display the standard Arch component task panel.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mode: int or str
|
||||
The edit mode the document has requested. Set to 0 when requested via
|
||||
a double click or [Edit -> Toggle Edit Mode].
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
If edit mode was entered.
|
||||
"""
|
||||
|
||||
if (mode == 0) and hasattr(self, "Object"):
|
||||
taskd = _ManholeTaskPanel(self.Object)
|
||||
taskd.obj = self.Object
|
||||
# taskd.update()
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class ManholeTaskPanel:
|
||||
def __init__(self, obj=None):
|
||||
self.new = False
|
||||
if obj is None:
|
||||
self.new = True
|
||||
obj = makeManhole()
|
||||
|
||||
self.obj = obj
|
||||
|
||||
self.form = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantManhole.ui")
|
||||
|
||||
self.node = None
|
||||
self.view = FreeCADGui.ActiveDocument.ActiveView
|
||||
self.tracker = DraftTrackers.ghostTracker(obj)
|
||||
self.tracker.on()
|
||||
self.call = self.view.addEventCallback("SoEvent", self.action)
|
||||
|
||||
def action(self, arg):
|
||||
"""Handle the 3D scene events.
|
||||
|
||||
This is installed as an EventCallback in the Inventor view.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg: dict
|
||||
Dictionary with strings that indicates the type of event received
|
||||
from the 3D view.
|
||||
"""
|
||||
|
||||
if arg["Type"] == "SoKeyboardEvent" and arg["Key"] == "ESCAPE":
|
||||
self.finish()
|
||||
|
||||
elif arg["Type"] == "SoLocation2Event":
|
||||
point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
|
||||
if info:
|
||||
self.tracker.move(FreeCAD.Vector(info["x"], info["y"], info["z"]))
|
||||
else:
|
||||
self.tracker.move(point)
|
||||
|
||||
elif (arg["Type"] == "SoMouseButtonEvent" and
|
||||
arg["State"] == "DOWN" and
|
||||
arg["Button"] == "BUTTON1"):
|
||||
|
||||
point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg)
|
||||
if info:
|
||||
self.obj.Placement.Base = FreeCAD.Vector(info["x"], info["y"], info["z"])
|
||||
else:
|
||||
self.obj.Placement.Base = point
|
||||
self.finish()
|
||||
|
||||
def finish(self):
|
||||
self.accept()
|
||||
|
||||
def accept(self):
|
||||
self.closeForm()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
if self.new:
|
||||
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
|
||||
self.closeForm()
|
||||
return True
|
||||
|
||||
def closeForm(self):
|
||||
self.tracker.finalize()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
self.view.removeEventCallback("SoEvent", self.call)
|
||||
|
||||
|
||||
'''class _CommandManhole:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "manhole.svg")),
|
||||
'MenuText': "Manhole",
|
||||
'Accel': "C, M",
|
||||
'ToolTip': "Creates a Manhole object from setup dialog."}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
if FreeCADGui.Selection.getCompleteSelection():
|
||||
for ob in FreeCAD.ActiveDocument.Objects:
|
||||
if ob.Name[:4] == "Site":
|
||||
return True
|
||||
|
||||
def Activated(self):
|
||||
TaskPanel = _ManholeTaskPanel()
|
||||
FreeCADGui.Control.showDialog(TaskPanel)
|
||||
return
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('PVPlantManhole', _CommandManhole())'''
|
||||
|
||||
@@ -95,6 +95,12 @@ class _Area:
|
||||
""" Method run when the document is restored """
|
||||
self.setProperties(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
return None # No necesitamos guardar estado adicional
|
||||
|
||||
def __setstate__(self, state):
|
||||
pass
|
||||
|
||||
|
||||
class _ViewProviderArea:
|
||||
def __init__(self, vobj):
|
||||
@@ -263,7 +269,7 @@ class FrameArea(_Area):
|
||||
if not hasattr(o, "Proxy"):
|
||||
continue
|
||||
if o.Proxy.Type == "Tracker":
|
||||
lf.append(obj)
|
||||
lf.append(o)
|
||||
obj.Frames = lf
|
||||
obj.FramesNumber = len(obj.Frames)
|
||||
|
||||
@@ -274,10 +280,17 @@ class FrameArea(_Area):
|
||||
|
||||
def execute(self, obj):
|
||||
''' execute '''
|
||||
#_Area.execute(self, obj)
|
||||
|
||||
|
||||
if not hasattr(obj, "Frames"):
|
||||
return
|
||||
obj.FrameNumber = len(obj.Frames)
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
pass
|
||||
|
||||
|
||||
class ViewProviderFrameArea(_ViewProviderArea):
|
||||
def __init__(self, vobj):
|
||||
@@ -354,6 +367,12 @@ class OffsetArea(_Area):
|
||||
pts.extend(section)
|
||||
obj.Shape = Part.makePolygon(pts)
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
pass
|
||||
|
||||
|
||||
class ViewProviderOffsetArea(_ViewProviderArea):
|
||||
def getIcon(self):
|
||||
@@ -372,15 +391,15 @@ class ViewProviderOffsetArea(_ViewProviderArea):
|
||||
|
||||
|
||||
def makeProhibitedArea(base = None):
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ProhibitedArea")
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ExclusionArea")
|
||||
ProhibitedArea(obj)
|
||||
ViewProviderForbiddenArea(obj.ViewObject)
|
||||
if base:
|
||||
obj.Base = base
|
||||
try:
|
||||
group = FreeCAD.ActiveDocument.Exclusion
|
||||
group = FreeCAD.ActiveDocument.getObject("Exclusions")
|
||||
except:
|
||||
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Exclusion')
|
||||
group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Exclusions')
|
||||
group.Label = "Exclusions"
|
||||
group.addObject(obj)
|
||||
|
||||
@@ -401,6 +420,12 @@ class ProhibitedArea(OffsetArea):
|
||||
"""Method run when the document is restored."""
|
||||
self.setProperties(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
pass
|
||||
|
||||
|
||||
class ViewProviderForbiddenArea(_ViewProviderArea):
|
||||
def getIcon(self):
|
||||
@@ -556,6 +581,12 @@ class PVSubplant:
|
||||
''' '''
|
||||
pass
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
pass
|
||||
|
||||
|
||||
class ViewProviderPVSubplant:
|
||||
def __init__(self, vobj):
|
||||
@@ -702,8 +733,8 @@ class CommandFrameArea:
|
||||
'ToolTip': "Frame Area"}
|
||||
|
||||
def Activated(self):
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
makeFramedArea(None, sel)
|
||||
for base in FreeCADGui.Selection.getSelection():
|
||||
makeFramedArea(None, base)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
@@ -720,8 +751,8 @@ class CommandProhibitedArea:
|
||||
'ToolTip': "Prohibited Area"}
|
||||
|
||||
def Activated(self):
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
makeProhibitedArea(sel[0])
|
||||
for base in FreeCADGui.Selection.getSelection():
|
||||
makeProhibitedArea(base)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
@@ -742,12 +773,11 @@ class CommandPVSubplant:
|
||||
area = makePVSubplant()
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
for obj in sel:
|
||||
if obj.Name[:7] == "Tracker":
|
||||
if hasattr(obj, 'Proxy') and obj.Proxy.Type == "Tracker":
|
||||
frame_list = area.Frames
|
||||
frame_list.append(obj)
|
||||
area.Frames = frame_list
|
||||
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
@@ -763,11 +793,8 @@ class CommandOffsetArea:
|
||||
'ToolTip': "OffsetArea"}
|
||||
|
||||
def Activated(self):
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
base = None
|
||||
if sel:
|
||||
base = sel[0]
|
||||
obj = makeOffsetArea(base)
|
||||
for base in FreeCADGui.Selection.getSelection():
|
||||
makeOffsetArea(base)
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
@@ -776,7 +803,7 @@ class CommandOffsetArea:
|
||||
return False
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
'''if FreeCAD.GuiUp:
|
||||
class CommandAreaGroup:
|
||||
|
||||
def GetCommands(self):
|
||||
@@ -800,4 +827,4 @@ if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('ForbiddenArea', CommandProhibitedArea())
|
||||
FreeCADGui.addCommand('PVSubplant', CommandPVSubplant())
|
||||
FreeCADGui.addCommand('OffsetArea', CommandOffsetArea())
|
||||
FreeCADGui.addCommand('PVPlantAreas', CommandAreaGroup())
|
||||
FreeCADGui.addCommand('PVPlantAreas', CommandAreaGroup())'''
|
||||
|
||||
@@ -128,7 +128,7 @@ def joinAreas(areas):
|
||||
shape.fuse(shapes)
|
||||
return shape
|
||||
|
||||
class CommandSplitArea:
|
||||
'''class CommandSplitArea:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "split_area.svg")),
|
||||
'Accel': "A, S",
|
||||
@@ -162,6 +162,6 @@ class CommandJoinAreas:
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('SplitArea', CommandSplitArea())
|
||||
FreeCADGui.addCommand('JoinAreas', CommandJoinAreas())
|
||||
FreeCADGui.addCommand('JoinAreas', CommandJoinAreas())'''
|
||||
|
||||
|
||||
|
||||
92
Project/GenerateExternalDocument.py
Normal file
92
Project/GenerateExternalDocument.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from PySide2 import QtWidgets
|
||||
import os
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui, QtWidgets
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "PVPlant Export to DXF"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
|
||||
def copy_object_with_reference():
|
||||
try:
|
||||
# Verificar selección
|
||||
selected = FreeCADGui.Selection.getSelection()
|
||||
if len(selected) != 1:
|
||||
QtWidgets.QMessageBox.critical(None, "Error", "Selecciona exactamente un objeto")
|
||||
return
|
||||
|
||||
original_doc = FreeCAD.ActiveDocument
|
||||
original_obj = selected[0]
|
||||
original_center = original_obj.Shape.BoundBox.Center
|
||||
|
||||
# Crear nuevo documento
|
||||
new_doc = FreeCAD.newDocument(f"{original_doc.Name} - {original_obj.Label}")
|
||||
|
||||
# Copiar objeto al nuevo documento
|
||||
new_obj = new_doc.copyObject(original_obj, True)
|
||||
new_obj.Label = f"Linked_{original_obj.Label}"
|
||||
new_obj.Placement.Base = original_obj.Placement.Base - original_center
|
||||
|
||||
# Guardar el documenton nuevp
|
||||
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
|
||||
new_doc.saveAs(os.path.join(path, new_doc.Name))
|
||||
|
||||
# Mantener posición original en el nuevo documento
|
||||
# new_obj.Placement = original_obj.Placement
|
||||
|
||||
# Crear referencia (App::Link) en el documento original
|
||||
link = original_doc.addObject("App::Link", f"Link_{new_obj.Label}")
|
||||
link.LinkedObject = new_obj
|
||||
|
||||
# Mantener posición original del objeto
|
||||
link.Placement = original_obj.Placement
|
||||
|
||||
# Actualizar vistas
|
||||
original_doc.recompute()
|
||||
new_doc.recompute()
|
||||
|
||||
# Regresar al documento original
|
||||
FreeCAD.setActiveDocument(original_doc.Name)
|
||||
|
||||
#QtWidgets.QMessageBox.information(None, "Éxito", "Operación completada correctamente")
|
||||
|
||||
except Exception as e:
|
||||
QtWidgets.QMessageBox.critical(None, "Error", f"Error: {str(e)}")
|
||||
|
||||
|
||||
# Ejecutar la función
|
||||
class CommandGenerateExternalDocument:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "dxf.svg")),
|
||||
'Accel': "P, E",
|
||||
'MenuText': "Export to DXF",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Export choosed layers to dxf")}
|
||||
|
||||
def Activated(self):
|
||||
''' '''
|
||||
copy_object_with_reference()
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -138,7 +138,7 @@ class ProjectSetupDialog(QtGui.QWidget):
|
||||
def closeForm(self):
|
||||
self.close()
|
||||
|
||||
class CommandProjectSetup:
|
||||
'''class CommandProjectSetup:
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "flash.svg")),
|
||||
'Accel': "P, S",
|
||||
@@ -159,5 +159,5 @@ class CommandProjectSetup:
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('ProjectSetup', CommandProjectSetup())
|
||||
FreeCADGui.addCommand('ProjectSetup', CommandProjectSetup())'''
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
# ***********************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from PySide import QtGui, QtCore
|
||||
import datetime
|
||||
import getpass
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui, os
|
||||
@@ -43,51 +47,99 @@ except AttributeError:
|
||||
import PVPlantResources
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
class SafeDict(dict):
|
||||
"""Diccionario seguro para manejar placeholders no definidos"""
|
||||
|
||||
def rename(objects, mask, mode=0):
|
||||
'''
|
||||
mode = 0/1/2/3
|
||||
0: izquierda a derecha - arriba a abajo
|
||||
1: arriba a abajo - izquierda a derecha
|
||||
'''
|
||||
|
||||
# sort:
|
||||
tmp = sorted(objects, key=lambda x: (x.Placement.Base.x,
|
||||
x.Placement.Base.y))
|
||||
|
||||
for idx, obj in tmp:
|
||||
obj.Name = name
|
||||
|
||||
class renamerTaskPanel:
|
||||
def __init__(self, obj=None):
|
||||
self.obj = obj
|
||||
|
||||
# -------------------------------------------------------------------------------------------------------------
|
||||
# Module widget form
|
||||
# -------------------------------------------------------------------------------------------------------------
|
||||
self.formRack = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantFrame.ui")
|
||||
self.formRack.widgetTracker.setVisible(False)
|
||||
self.formRack.comboFrameType.currentIndexChanged.connect(self.selectionchange)
|
||||
|
||||
self.formPiling = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantRackFixedPiling.ui")
|
||||
self.formPiling.editBreadthwaysNumOfPost.valueChanged.connect(self.editBreadthwaysNumOfPostChange)
|
||||
self.formPiling.editAlongNumOfPost.valueChanged.connect(self.editAlongNumOfPostChange)
|
||||
|
||||
self.form = [self.formRack, self.formPiling]
|
||||
|
||||
def accept(self):
|
||||
self.closeForm()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
self.closeForm()
|
||||
return False
|
||||
|
||||
def closeForm(self):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
def __missing__(self, key):
|
||||
return f'{{{key}}}'
|
||||
|
||||
|
||||
class _CommandRenamer:
|
||||
class RenameDialog(QtGui.QDialog):
|
||||
def __init__(self):
|
||||
super(RenameDialog, self).__init__()
|
||||
self.setupUI()
|
||||
|
||||
def setupUI(self):
|
||||
self.setWindowTitle("Renombrar objetos con plantilla")
|
||||
self.setMinimumWidth(400)
|
||||
layout = QtGui.QVBoxLayout(self)
|
||||
|
||||
# Campo para la plantilla
|
||||
layout.addWidget(QtGui.QLabel("Plantilla de nombre:"))
|
||||
self.template_input = QtGui.QLineEdit()
|
||||
self.template_input.setPlaceholderText("Ej: {label}_mod_{index:03d}_{date:%Y%m%d}")
|
||||
layout.addWidget(self.template_input)
|
||||
|
||||
# Info de placeholders
|
||||
info = QtGui.QLabel(
|
||||
"Placeholders disponibles:\n"
|
||||
"• {index} - Número en orden\n"
|
||||
"• {label} - Nombre actual del objeto\n"
|
||||
"• {name} - Nombre interno\n"
|
||||
"• {date} - Fecha actual\n"
|
||||
"• {time} - Hora actual\n"
|
||||
"• {user} - Usuario del sistema\n"
|
||||
"• {datetime} - Fecha y hora completa\n"
|
||||
"Formatos: {date:%Y/%m/%d}, {index:03d}, etc."
|
||||
)
|
||||
layout.addWidget(info)
|
||||
|
||||
# Botones
|
||||
btn_box = QtGui.QDialogButtonBox()
|
||||
btn_box.addButton(QtGui.QDialogButtonBox.Apply)
|
||||
btn_box.addButton(QtGui.QDialogButtonBox.Close)
|
||||
btn_box.clicked.connect(self.on_button_click)
|
||||
layout.addWidget(btn_box)
|
||||
|
||||
def on_button_click(self, button):
|
||||
if button == btn_box.button(QtGui.QDialogButtonBox.Apply):
|
||||
self.rename_objects()
|
||||
else:
|
||||
self.close()
|
||||
|
||||
def rename_objects(self):
|
||||
template = self.template_input.text()
|
||||
if not template:
|
||||
QtGui.QMessageBox.warning(self, "Error", "¡La plantilla no puede estar vacía!")
|
||||
return
|
||||
|
||||
selected_objects = FreeCADGui.Selection.getSelection()
|
||||
if not selected_objects:
|
||||
QtGui.QMessageBox.warning(self, "Error", "¡No hay objetos seleccionados!")
|
||||
return
|
||||
|
||||
now = datetime.datetime.now()
|
||||
user_name = getpass.getuser()
|
||||
errors = []
|
||||
|
||||
for idx, obj in enumerate(selected_objects, 1):
|
||||
try:
|
||||
placeholders = SafeDict({
|
||||
'index': idx,
|
||||
'label': obj.Label,
|
||||
'name': obj.Name,
|
||||
'date': now.date(),
|
||||
'time': now.time(),
|
||||
'datetime': now,
|
||||
'user': user_name
|
||||
})
|
||||
|
||||
new_name = template.format_map(placeholders)
|
||||
obj.Label = new_name
|
||||
except Exception as e:
|
||||
errors.append(f"{obj.Name}: {str(e)}")
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if errors:
|
||||
error_msg = "\n".join(errors)
|
||||
QtGui.QMessageBox.critical(self, "Errores", f"Error(es) encontrado(s):\n{error_msg}")
|
||||
else:
|
||||
QtGui.QMessageBox.information(self, "Éxito", "¡Objetos renombrados correctamente!")
|
||||
|
||||
|
||||
|
||||
class CommandRenamer:
|
||||
"the Arch Building command definition"
|
||||
|
||||
def GetResources(self):
|
||||
|
||||
70
README.md
70
README.md
@@ -1,53 +1,53 @@
|
||||
# 🚧 FreeCAD Road Workbench
|
||||
# 🚧 FreeCAD PVPlant Workbench
|
||||
|
||||
Road is the Transportation and Geomatics Engineering workbench for FreeCAD.
|
||||
PVPlant es el workbench de Ingeniería Fotovoltaica para FreeCAD.
|
||||
|
||||
## ✨ Features
|
||||
## ✨ Características
|
||||
|
||||
* Geopoints
|
||||
* Terrain
|
||||
* Alignment
|
||||
* Profile
|
||||
* Regions
|
||||
* Sections
|
||||
* Volume
|
||||
* GeoLine
|
||||
* LandXML
|
||||
* Puntos Geográficos
|
||||
* Terreno
|
||||
* Diseño de Estructuras
|
||||
* Generación de Perfiles
|
||||
* Regiones
|
||||
* Secciones
|
||||
* Cálculo de Volumen
|
||||
* Líneas Geográficas
|
||||
* Importación/Exportación LandXML
|
||||
|
||||
## 📥 Installation
|
||||
### 🔹 Option 1: Install via Addon Manager (Recommended)
|
||||
## 📥 Instalación
|
||||
### 🔹 Opción 1: Instalar mediante el Administrador de Complementos (Recomendado)
|
||||
|
||||
1. Open FreeCAD.
|
||||
2. Go to **Tools > Addon Manager**.
|
||||
3. In the Addon Manager window, search for **Road**.
|
||||
4. Select the workbench and click the **Install** button.
|
||||
5. Restart FreeCAD to complete the installation.
|
||||
1. Abre FreeCAD.
|
||||
2. Ve a **Herramientas > Administrador de Complementos**.
|
||||
3. En la ventana del Administrador de Complementos, busca **PVPlant**.
|
||||
4. Selecciona el workbench y haz clic en el botón **Instalar**.
|
||||
5. Reinicia FreeCAD para completar la instalación.
|
||||
|
||||
### 🔹 Option 2: Manual Installation
|
||||
### 🔹 Opción 2: Instalación Manual
|
||||
|
||||
1. Download the latest release the repository.
|
||||
2. Copy the downloaded folder to your FreeCAD Mod directory:
|
||||
1. Descarga la última versión del repositorio.
|
||||
2. Copia la carpeta descargada en el directorio Mod de FreeCAD:
|
||||
|
||||
```
|
||||
Windows: C:\Users\<YourUsername>\AppData\Roaming\FreeCAD\Mod
|
||||
Linux(Flatpak): /home/<YourUsername>/.var/app/org.freecad.FreeCAD/data/FreeCAD/Mod
|
||||
Windows: C:\Users\<TuUsuario>\AppData\Roaming\FreeCAD\Mod
|
||||
Linux(Flatpak): /home/<TuUsuario>/.var/app/org.freecad.FreeCAD/data/FreeCAD/Mod
|
||||
MacOS: ~/Library/Preferences/FreeCAD/Mod
|
||||
```
|
||||
|
||||
3. Restart FreeCAD to complete the installation.
|
||||
3. Reinicia FreeCAD para completar la instalación.
|
||||
|
||||
## 💬 Feedback and Support
|
||||
## 💬 Feedback y Soporte
|
||||
|
||||
💡 Need help? Join the discussion on the FreeCAD Forum: [FreeCAD Road Workbench](https://forum.freecadweb.org/viewtopic.php?f=8&t=34371).
|
||||
💡 ¿Necesitas ayuda? Únete a la discusión en el foro de FreeCAD: [FreeCAD PVPlant Workbench](https://forum.freecadweb.org/).
|
||||
|
||||
🐞 Found a bug? Report issues on [GitHub](https://github.com/HakanSeven12/Road/issues).
|
||||
🐞 ¿Encontraste un error? Reporta problemas en [GitHub](https://github.com/HakanSeven12/PVPlant/issues).
|
||||
|
||||
## 👨💻 Developer
|
||||
## 👨💻 Desarrollador
|
||||
|
||||
Developed with passion by Hakan Seven ([@HakanSeven12](https://github.com/HakanSeven12)) with inspiration and contributions from the FreeCAD community.
|
||||
Desarrollado con pasión por Javier Braña con inspiración y contribuciones de la comunidad de FreeCAD.
|
||||
|
||||
## 📸 Screenshots
|
||||

|
||||

|
||||

|
||||

|
||||
## 📸 Capturas de Pantalla
|
||||

|
||||

|
||||

|
||||

|
||||
BIN
Resources/Icons/drop.jpg
Normal file
BIN
Resources/Icons/drop.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
2087
Resources/Libraries/Electrical/Modules/CEC Inverters.csv
Normal file
2087
Resources/Libraries/Electrical/Modules/CEC Inverters.csv
Normal file
File diff suppressed because it is too large
Load Diff
20746
Resources/Libraries/Electrical/Modules/CEC Modules.csv
Normal file
20746
Resources/Libraries/Electrical/Modules/CEC Modules.csv
Normal file
File diff suppressed because it is too large
Load Diff
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"
|
||||
}
|
||||
@@ -38,7 +38,10 @@ new QWebChannel(qt.webChannelTransport, function (channel)
|
||||
|
||||
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();
|
||||
MyApp.onMapZoom(bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast(), map.getZoom());
|
||||
});
|
||||
|
||||
var DrawShapes;
|
||||
|
||||
97
Utils/PVPlantDownloadOrtophoto.py
Normal file
97
Utils/PVPlantDownloadOrtophoto.py
Normal file
@@ -0,0 +1,97 @@
|
||||
import FreeCAD
|
||||
import Part
|
||||
import math
|
||||
import requests
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
# ==================================================================
|
||||
# CONFIGURACIÓN INICIAL (AJUSTAR ESTOS VALORES)
|
||||
# ==================================================================
|
||||
lat_min = 40.4165 # Latitud mínima (Ej: Madrid)
|
||||
lat_max = 40.4300
|
||||
lon_min = -3.7150 # Longitud mínima
|
||||
lon_max = -3.6900
|
||||
zoom = 16 # Nivel de zoom (13-19 para mejor resolución)
|
||||
terrain_resolution = 50 # Puntos por eje en la malla del terreno
|
||||
|
||||
|
||||
# ==================================================================
|
||||
def download_tile(lon_deg, lat_deg, zoom):
|
||||
"""Descarga un tile de OpenStreetMap"""
|
||||
xtile = int((lon_deg + 180) / 360 * (2 ** zoom))
|
||||
ytile = int((1 - math.log(math.tan(math.radians(lat_deg)) + 1 / math.cos(math.radians(lat_deg))) / math.pi) / 2 * (
|
||||
2 ** zoom))
|
||||
|
||||
url = f"https://tile.openstreetmap.org/{zoom}/{xtile}/{ytile}.png"
|
||||
response = requests.get(url)
|
||||
return Image.open(BytesIO(response.content))
|
||||
|
||||
|
||||
def get_tile_grid(lon_min, lon_max, lat_min, lat_max, zoom):
|
||||
"""Calcula la cuadrícula de tiles necesarios"""
|
||||
xtile_min = int((lon_min + 180) / 360 * (2 ** zoom))
|
||||
xtile_max = int((lon_max + 180) / 360 * (2 ** zoom))
|
||||
|
||||
ytile_min = int((1 - math.log(math.tan(math.radians(lat_min)) + 1 / math.cos(math.radians(lat_min))) / math.pi) / 2 * (2 ** zoom))
|
||||
ytile_max = int((1 - math.log(math.tan(math.radians(lat_max)) + 1 / math.cos(math.radians(lat_max))) / math.pi) / 2 * (2 ** zoom))
|
||||
|
||||
return range(xtile_min, xtile_max + 1), range(ytile_min, ytile_max + 1)
|
||||
|
||||
|
||||
def create_stitched_image(xtiles, ytiles, zoom):
|
||||
"""Une los tiles en una sola imagen"""
|
||||
tile_size = 256
|
||||
full_image = Image.new('RGB', (len(xtiles) * tile_size, len(ytiles) * tile_size))
|
||||
|
||||
for i, x in enumerate(xtiles):
|
||||
for j, y in enumerate(ytiles):
|
||||
tile = download_tile(x, y, zoom)
|
||||
full_image.paste(tile, (i * tile_size, j * tile_size))
|
||||
|
||||
return full_image
|
||||
|
||||
|
||||
def create_parametric_terrain(lon_min, lon_max, lat_min, lat_max, resolution):
|
||||
"""Crea una superficie paramétrica basada en coordenadas geográficas"""
|
||||
points = []
|
||||
for i in np.linspace(lat_min, lat_max, resolution):
|
||||
row = []
|
||||
for j in np.linspace(lon_min, lon_max, resolution):
|
||||
row.append(FreeCAD.Vector(j, i, 0)) # Añadir altura aquí si hay datos de elevación
|
||||
points.append(row)
|
||||
|
||||
terrain = Part.makeFilledFace([points])
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "Terrain")
|
||||
obj.Shape = terrain
|
||||
return obj
|
||||
|
||||
|
||||
def apply_texture(obj, image_path):
|
||||
"""Aplica la textura al objeto en FreeCAD"""
|
||||
obj.ViewObject.TextureMode = "EMBEDDED"
|
||||
obj.ViewObject.TextureImage = image_path
|
||||
obj.ViewObject.DisplayMode = "Shaded"
|
||||
|
||||
|
||||
# Ejecución principal
|
||||
'''if __name__ == "__main__":
|
||||
# Paso 1: Descargar y unir tiles
|
||||
xtiles, ytiles = get_tile_grid(lon_min, lon_max, lat_min, lat_max, zoom)
|
||||
stitched_image = create_stitched_image(xtiles, ytiles, zoom)
|
||||
texture_path = "/tmp/combined_texture.png"
|
||||
stitched_image.save(texture_path)
|
||||
|
||||
# Paso 2: Crear terreno
|
||||
doc = FreeCAD.newDocument()
|
||||
terrain = create_parametric_terrain(lon_min, lon_max, lat_min, lat_max, terrain_resolution)
|
||||
|
||||
# Paso 3: Aplicar textura
|
||||
apply_texture(terrain, texture_path)
|
||||
|
||||
# Ajustar vista
|
||||
FreeCAD.Gui.SendMsgToActiveView("ViewFit")
|
||||
FreeCAD.Gui.updateGui()
|
||||
|
||||
print("¡Proceso completado!")'''
|
||||
@@ -201,8 +201,8 @@ def simplifyWire(wire):
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if closed:
|
||||
points.append(FreeCAD.Vector(points[0]))
|
||||
#if closed:
|
||||
points.append(FreeCAD.Vector(points[0]))
|
||||
|
||||
pol = Part.makePolygon(points)
|
||||
return pol
|
||||
|
||||
305
Vegetation/PVPlantTreeGenerator.py
Normal file
305
Vegetation/PVPlantTreeGenerator.py
Normal file
@@ -0,0 +1,305 @@
|
||||
|
||||
import math
|
||||
|
||||
import ArchComponent
|
||||
import FreeCAD
|
||||
import Part
|
||||
import random
|
||||
from FreeCAD import Qt
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
try:
|
||||
from scipy import spatial
|
||||
has_scipy = True
|
||||
except ImportError:
|
||||
has_scipy = False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
from PySide import QtCore, QtGui
|
||||
from DraftTools import translate
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
import Part
|
||||
import os
|
||||
else:
|
||||
# \cond
|
||||
def translate(ctxt, txt):
|
||||
return txt
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctxt, txt):
|
||||
return txt
|
||||
# \endcond
|
||||
|
||||
__title__ = "FreeCAD Fixed Rack"
|
||||
__author__ = "Javier Braña"
|
||||
__url__ = "http://www.sogos-solar.com"
|
||||
|
||||
__dir__ = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", "PVPlant")
|
||||
DirResources = os.path.join(__dir__, "../Resources")
|
||||
DirIcons = os.path.join(DirResources, "Icons")
|
||||
DirImages = os.path.join(DirResources, "Images")
|
||||
|
||||
|
||||
def makeTree():
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Tree")
|
||||
Tree(obj)
|
||||
ViewProviderTree(obj.ViewObject)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
try:
|
||||
folder = FreeCAD.ActiveDocument.Vegetation
|
||||
except:
|
||||
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Vegetation')
|
||||
folder.Label = "Vegetation"
|
||||
folder.addObject(obj)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class Tree(ArchComponent.Component):
|
||||
"""A parametric tree object for architectural design"""
|
||||
|
||||
def __init__(self, obj):
|
||||
ArchComponent.Component.__init__(self, obj)
|
||||
self.obj = obj
|
||||
self.setProperties(obj)
|
||||
random.seed(42) # Semilla para resultados consistentes
|
||||
|
||||
def setProperties(self, obj):
|
||||
"""Define y configura las propiedades del objeto"""
|
||||
pl = obj.PropertiesList
|
||||
|
||||
# Propiedades de la copa
|
||||
canopy_props = [
|
||||
("CanopyHeight", "App::PropertyLength", "Altura total de la copa"),
|
||||
("CanopyRadius", "App::PropertyLength", "Radio máximo de la copa"),
|
||||
("Spikiness", "App::PropertyFloatConstraint", "Irregularidad de la superficie", (0.5, 0.0, 1.0, 0.05)),
|
||||
(
|
||||
"CrownExpansion", "App::PropertyFloatConstraint", "Expansión de la corona superior", (1.0, 0.0, 2.0, 0.05)),
|
||||
("UmbrellaEffect", "App::PropertyFloatConstraint", "Efecto de dosel/paraguas", (0.0, 0.0, 1.0, 0.05)),
|
||||
("LeafCount", "App::PropertyInteger", "Densidad de follaje (número de segmentos)")
|
||||
]
|
||||
|
||||
for prop in canopy_props:
|
||||
name, ptype, doc, *args = prop
|
||||
if name not in pl:
|
||||
if ptype == "App::PropertyFloatConstraint":
|
||||
obj.addProperty(ptype, name, "Canopy", doc).__setattr__(name, args[0])
|
||||
else:
|
||||
obj.addProperty(ptype, name, "Canopy", doc)
|
||||
# Valores por defecto
|
||||
if name == "LeafCount":
|
||||
setattr(obj, name, 20)
|
||||
elif name in ["CanopyHeight", "CanopyRadius"]:
|
||||
setattr(obj, name, 4000 if "Height" in name else 1500)
|
||||
|
||||
# Propiedades del tronco
|
||||
trunk_props = [
|
||||
("TrunkHeight", "App::PropertyLength", "Altura del tronco", 2000),
|
||||
("TrunkRadius", "App::PropertyLength", "Radio base del tronco", 150),
|
||||
("TrunkFaces", "App::PropertyInteger", "Caras del tronco", 6)
|
||||
]
|
||||
|
||||
for prop in trunk_props:
|
||||
name, ptype, doc, default = prop
|
||||
if name not in pl:
|
||||
obj.addProperty(ptype, name, "Trunk", doc)
|
||||
setattr(obj, name, default)
|
||||
|
||||
# Propiedades base
|
||||
if "Type" not in pl:
|
||||
obj.addProperty("App::PropertyString", "Type", "Base", "Tipo de objeto").Type = "Vegetable-Tree"
|
||||
obj.setEditorMode("Type", 1) # Hacerla de solo lectura
|
||||
|
||||
obj.Proxy = self
|
||||
obj.IfcType = "Shading Device"
|
||||
obj.setEditorMode("IfcType", 1)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
ArchComponent.Component.onDocumentRestored(self, obj)
|
||||
self.setProperties(obj)
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
"""Actualiza la forma cuando cambian propiedades"""
|
||||
if prop in ["CanopyHeight", "CanopyRadius", "Spikiness", "CrownExpansion",
|
||||
"UmbrellaEffect", "LeafCount", "TrunkHeight", "TrunkRadius", "TrunkFaces"]:
|
||||
self.execute(obj)
|
||||
|
||||
def createTrunk(self, obj):
|
||||
"""Crea la geometría del tronco usando un loft"""
|
||||
try:
|
||||
# Calcula dimensiones proporcionales
|
||||
base_radius = obj.TrunkRadius.Value
|
||||
top_radius = base_radius * 0.8
|
||||
height = obj.TrunkHeight.Value
|
||||
|
||||
# Crea tres perfiles circulares
|
||||
profiles = []
|
||||
for z, radius in [(0, base_radius),
|
||||
(height / 3, base_radius),
|
||||
(height, top_radius)]:
|
||||
circle = Part.makeCircle(radius, FreeCAD.Vector(0, 0, z))
|
||||
profiles.append(Part.Wire([circle]))
|
||||
|
||||
return Part.makeLoft(profiles, True, True)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creando tronco: {str(e)}\n")
|
||||
return None
|
||||
|
||||
def createCanopy(self, obj):
|
||||
"""Genera la forma de la copa usando una envoltura convexa"""
|
||||
if not has_scipy:
|
||||
FreeCAD.Console.PrintError("Scipy no está instalado. No se puede generar la copa.\n")
|
||||
return None
|
||||
|
||||
try:
|
||||
# Configuración inicial
|
||||
n_segments = max(3, obj.LeafCount) # Mínimo 3 segmentos
|
||||
radius = obj.CanopyRadius.Value
|
||||
height = obj.CanopyHeight.Value
|
||||
|
||||
# Genera puntos distribuidos esféricamente con ruido
|
||||
points = []
|
||||
for _ in range(n_segments * 10): # 10 puntos por segmento
|
||||
theta = random.uniform(0, 2 * math.pi)
|
||||
phi = math.acos(random.uniform(-1, 1))
|
||||
|
||||
# Aplica parámetros de forma
|
||||
r = radius * (1 - obj.Spikiness * random.random())
|
||||
x = r * math.sin(phi) * math.cos(theta)
|
||||
y = r * math.sin(phi) * math.sin(theta)
|
||||
z = height * (0.5 + 0.5 * math.cos(phi)) # Distribución vertical
|
||||
|
||||
# Aplica efectos de forma
|
||||
z *= (1 - obj.UmbrellaEffect)
|
||||
if z > height / 2:
|
||||
x *= obj.CrownExpansion
|
||||
y *= obj.CrownExpansion
|
||||
|
||||
points.append(FreeCAD.Vector(x, y, z))
|
||||
|
||||
# Crea la envoltura convexa
|
||||
hull = spatial.ConvexHull([(p.x, p.y, p.z) for p in points])
|
||||
faces = []
|
||||
for simplex in hull.simplices:
|
||||
triangle = [points[i] for i in simplex]
|
||||
faces.append(Part.Face(Part.makePolygon(triangle + [triangle[0]])))
|
||||
|
||||
return Part.Compound(faces)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error creando copa: {str(e)}\n")
|
||||
return None
|
||||
|
||||
def execute(self, obj):
|
||||
"""Ensambla el objeto final"""
|
||||
try:
|
||||
# Crea componentes
|
||||
trunk = self.createTrunk(obj)
|
||||
canopy = self.createCanopy(obj)
|
||||
|
||||
# Verifica componentes válidos
|
||||
if not trunk or not canopy:
|
||||
raise ValueError("Error en la generación de componentes")
|
||||
|
||||
# Posiciona la copa sobre el tronco
|
||||
canopy_placement = FreeCAD.Placement()
|
||||
canopy_placement.Base.z = obj.TrunkHeight.Value
|
||||
canopy.Placement = canopy_placement
|
||||
|
||||
# Combina las formas
|
||||
compound = Part.Compound([trunk, canopy])
|
||||
obj.Shape = compound
|
||||
|
||||
# Configura apariencia
|
||||
if obj.ViewObject:
|
||||
obj.ViewObject.DiffuseColor = ([(0.35, 0.2, 0.05)] * len(trunk.Faces) +
|
||||
[(0.1, 0.6, 0.2)] * len(canopy.Faces)) # Color copa
|
||||
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error al ejecutar: {str(e)}\n")
|
||||
|
||||
|
||||
class ViewProviderTree(ArchComponent.ViewProviderComponent):
|
||||
"A View Provider for the Pipe object"
|
||||
|
||||
def __init__(self, vobj):
|
||||
ArchComponent.ViewProviderComponent.__init__(self, vobj)
|
||||
|
||||
def getIcon(self):
|
||||
return str(os.path.join(DirIcons, "tree(1).svg"))
|
||||
|
||||
|
||||
class TreeTaskPanel(QtGui.QWidget):
|
||||
def __init__(self, obj=None):
|
||||
QtGui.QWidget.__init__(self)
|
||||
self.obj = obj
|
||||
if self.obj is None:
|
||||
self.obj = makeTree()
|
||||
|
||||
self.form = FreeCADGui.PySideUic.loadUi(__dir__ + "/PVPlantTree.ui")
|
||||
self.layout = QtGui.QHBoxLayout(self)
|
||||
self.layout.setContentsMargins(4, 4, 4, 4)
|
||||
self.layout.addWidget(self.form)
|
||||
|
||||
self.form.editCanopyHeight.valueChanged.connect(self.Canopy)
|
||||
self.form.editCanopyRadius.valueChanged.connect(self.Canopy)
|
||||
self.form.editSpikiness.valueChanged.connect(self.Canopy)
|
||||
self.form.editCrownExpansion.valueChanged.connect(self.Canopy)
|
||||
self.form.editLeftUmbrellaEffect.valueChanged.connect(self.Canopy)
|
||||
self.form.editLeafCount.valueChanged.connect(self.Canopy)
|
||||
|
||||
def Canopy(self):
|
||||
self.obj.CanopyHeight = FreeCAD.Units.Quantity(self.form.editCanopyHeight.text()).Value
|
||||
self.obj.CanopyRadius = FreeCAD.Units.Quantity(self.form.editCanopyRadius.text()).Value
|
||||
self.obj.Spikiness = self.form.editSpikiness.value()
|
||||
self.obj.CrownExpansion = self.form.editCrownExpansion.value()
|
||||
self.obj.UmbrellaEffect = self.form.editLeftUmbrellaEffect.value()
|
||||
self.obj.LeafCount = self.form.editLeafCount.value()
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def accept(self):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return True
|
||||
|
||||
|
||||
class CommandTree:
|
||||
"the PVPlant Tree command definition"
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "tree(1).svg")),
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PVPlantTree", "Tree"),
|
||||
'Accel': "S, T",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PVPlanTree",
|
||||
"Creates a Tree object from setup dialog.")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
import draftguitools.gui_trackers as DraftTrackers
|
||||
self.tree = makeTree()
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint,
|
||||
movecallback=self.mousemove,
|
||||
extradlg=self.taskbox(),
|
||||
title="Position of the tree:")
|
||||
|
||||
def getPoint(self, point=None, obj=None):
|
||||
self.tree.Placement.Base = point
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
self.tracker.finalize()
|
||||
|
||||
def mousemove(self, pt, snapInfo):
|
||||
self.tree.Placement.Base = pt
|
||||
|
||||
def taskbox(self):
|
||||
self.form = TreeTaskPanel(self.tree)
|
||||
return self.form
|
||||
|
||||
389
hydro/hydrological.py
Normal file
389
hydro/hydrological.py
Normal file
@@ -0,0 +1,389 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Mesh
|
||||
import Part
|
||||
import numpy as np
|
||||
import random
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from multiprocessing import Pool, cpu_count
|
||||
from collections import deque
|
||||
|
||||
|
||||
import os
|
||||
from PVPlantResources import DirIcons as DirIcons
|
||||
|
||||
|
||||
def mesh_to_numpy(mesh_obj):
|
||||
"""Convierte la malla a arrays de NumPy con validación robusta"""
|
||||
mesh = mesh_obj.Mesh
|
||||
|
||||
# Convertir vértices a array NumPy (shape: Nx3)
|
||||
vertices = np.array([(v.x, v.y, v.z) for v in mesh.Points], dtype=np.float32)
|
||||
|
||||
# Convertir facetas a array NumPy (shape: Mx3)
|
||||
facets = np.array( [f.PointIndices for f in mesh.Facets], dtype=np.uint32)
|
||||
|
||||
# Verificar integridad de índices
|
||||
max_index = len(mesh.Points) - 1
|
||||
if facets.size > 0 and (facets > max_index).any():
|
||||
raise ValueError("Índices de vértices fuera de rango")
|
||||
|
||||
return vertices, facets
|
||||
|
||||
|
||||
def build_adjacency_matrix(facets):
|
||||
"""Construye matriz de adyacencia con conversión segura de tipos"""
|
||||
edges = {}
|
||||
adjacency = [[] for _ in range(len(facets))]
|
||||
|
||||
for idx, facet in enumerate(facets):
|
||||
if len(facet) != 3:
|
||||
continue
|
||||
|
||||
v0, v1, v2 = facet
|
||||
|
||||
for edge in [(v0, v1), (v1, v2), (v2, v0)]:
|
||||
sorted_edge = tuple(sorted(edge))
|
||||
|
||||
if sorted_edge not in edges:
|
||||
edges[sorted_edge] = []
|
||||
edges[sorted_edge].append(idx)
|
||||
|
||||
# Procesar solo aristas con 2 facetas
|
||||
for edge, facet_indices in edges.items():
|
||||
if len(facet_indices) == 2:
|
||||
f1, f2 = facet_indices
|
||||
adjacency[f1].append(f2)
|
||||
adjacency[f2].append(f1)
|
||||
|
||||
return adjacency
|
||||
|
||||
|
||||
def calculate_incenters_parallel(vertices, facets):
|
||||
"""Cálculo paralelizado de incentros usando NumPy"""
|
||||
v0 = vertices[facets[:, 0]]
|
||||
v1 = vertices[facets[:, 1]]
|
||||
v2 = vertices[facets[:, 2]]
|
||||
|
||||
a = np.linalg.norm(v1 - v2, axis=1)
|
||||
b = np.linalg.norm(v0 - v2, axis=1)
|
||||
c = np.linalg.norm(v0 - v1, axis=1)
|
||||
|
||||
perimeters = a + b + c
|
||||
return (a[:, None] * v0 + b[:, None] * v1 + c[:, None] * v2) / perimeters[:, None]
|
||||
|
||||
|
||||
def find_basins_parallel(args):
|
||||
"""Función paralelizable para procesamiento de cuencas"""
|
||||
chunk, adjacency, elevations = args
|
||||
basins = []
|
||||
visited = np.zeros(len(elevations), dtype=bool)
|
||||
|
||||
for seed in chunk:
|
||||
if visited[seed]:
|
||||
continue
|
||||
|
||||
queue = deque([seed])
|
||||
basin = []
|
||||
min_elev = elevations[seed]
|
||||
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
if visited[current]:
|
||||
continue
|
||||
|
||||
visited[current] = True
|
||||
basin.append(current)
|
||||
|
||||
neighbors = [n for n in adjacency[current] if elevations[n] >= min_elev]
|
||||
queue.extend(neighbors)
|
||||
|
||||
if len(basin) > 0:
|
||||
basins.append(basin)
|
||||
|
||||
return basins
|
||||
|
||||
|
||||
def find_hydrological_basins(mesh_obj, min_area=100):
|
||||
"""Identificación de cuencas optimizada"""
|
||||
FreeCAD.Console.PrintMessage(f" -- vertices y facets: ")
|
||||
FreeCADGui.updateGui()
|
||||
vertices, facets = mesh_to_numpy(mesh_obj)
|
||||
FreeCAD.Console.PrintMessage(f" -- Adjacency: ")
|
||||
FreeCADGui.updateGui()
|
||||
adjacency = build_adjacency_matrix(facets)
|
||||
FreeCAD.Console.PrintMessage(f" -- Elevations: ")
|
||||
FreeCADGui.updateGui()
|
||||
elevations = calculate_incenters_parallel(vertices, facets)[:, 2]
|
||||
|
||||
# Dividir trabajo en chunks
|
||||
chunk_size = len(facets) // (cpu_count() * 2)
|
||||
chunks = [
|
||||
(chunk_range, adjacency, elevations) # Empaqueta los 3 argumentos
|
||||
for chunk_range in [
|
||||
range(i, min(i + chunk_size, len(facets)))
|
||||
for i in range(0, len(facets), chunk_size)
|
||||
]
|
||||
]
|
||||
|
||||
# Procesamiento paralelo
|
||||
with ThreadPoolExecutor(max_workers=cpu_count()) as executor:
|
||||
results = list(executor.map(find_basins_parallel, chunks))
|
||||
|
||||
# Combinar resultados
|
||||
all_basins = [b for sublist in results for b in sublist]
|
||||
|
||||
# Filtrar por área mínima
|
||||
valid_basins = []
|
||||
for basin in all_basins:
|
||||
area = sum(triangle_area(vertices[facets[i]]) for i in basin)
|
||||
if area >= min_area:
|
||||
valid_basins.append({'facets': basin, 'area': area})
|
||||
|
||||
return valid_basins
|
||||
|
||||
|
||||
def triangle_area(vertices):
|
||||
"""Cálculo rápido de área con producto cruz"""
|
||||
return 0.5 * np.linalg.norm(
|
||||
np.cross(vertices[1] - vertices[0], vertices[2] - vertices[0])
|
||||
)
|
||||
|
||||
|
||||
def validate_facet(facet):
|
||||
"""Valida que la faceta sea un triángulo válido"""
|
||||
return hasattr(facet, 'Points') and len(facet.Points) == 3
|
||||
|
||||
|
||||
def calculate_incenter(facet):
|
||||
"""Calcula el incentro usando la función nativa de FreeCAD"""
|
||||
try:
|
||||
return facet.InCircle[0] # (x, y, z)
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def build_adjacency(mesh):
|
||||
"""Construye matriz de adyacencia eficiente en memoria"""
|
||||
edges = {}
|
||||
adjacency = [[] for _ in mesh.Facets]
|
||||
|
||||
for idx, facet in enumerate(mesh.Facets):
|
||||
if not validate_facet(facet):
|
||||
continue
|
||||
|
||||
pts = facet.Points
|
||||
for edge in [(min(pts[0], pts[1]), max(pts[0], pts[1])),
|
||||
(min(pts[1], pts[2]), max(pts[1], pts[2])),
|
||||
(min(pts[2], pts[0]), max(pts[2], pts[0]))]:
|
||||
if edge in edges:
|
||||
neighbor = edges[edge]
|
||||
adjacency[idx].append(neighbor)
|
||||
adjacency[neighbor].append(idx)
|
||||
del edges[edge] # Liberar memoria
|
||||
else:
|
||||
edges[edge] = idx
|
||||
return adjacency
|
||||
|
||||
|
||||
def find_hydrological_basins_old(mesh_obj, min_area=100):
|
||||
"""Identificación de cuencas con validación de datos"""
|
||||
mesh = mesh_obj.Mesh
|
||||
adjacency = build_adjacency(mesh)
|
||||
basin_map = {}
|
||||
current_basin = 0
|
||||
|
||||
for seed in range(len(mesh.Facets)):
|
||||
if seed in basin_map or not validate_facet(mesh.Facets[seed]):
|
||||
continue
|
||||
|
||||
queue = deque([seed])
|
||||
basin_area = 0.0
|
||||
basin_facets = []
|
||||
|
||||
while queue:
|
||||
facet_idx = queue.popleft()
|
||||
if facet_idx in basin_map:
|
||||
continue
|
||||
|
||||
facet = mesh.Facets[facet_idx]
|
||||
in_center = calculate_incenter(facet)
|
||||
if not in_center:
|
||||
continue
|
||||
|
||||
# Verificar mínimo local
|
||||
is_sink = True
|
||||
for neighbor in adjacency[facet_idx]:
|
||||
if neighbor >= len(mesh.Facets) or not validate_facet(mesh.Facets[neighbor]):
|
||||
continue
|
||||
|
||||
n_center = calculate_incenter(mesh.Facets[neighbor])
|
||||
if n_center and n_center[2] < in_center[2]:
|
||||
is_sink = False
|
||||
break
|
||||
|
||||
if is_sink:
|
||||
basin_map[facet_idx] = current_basin
|
||||
basin_facets.append(facet_idx)
|
||||
basin_area += facet.Area
|
||||
|
||||
# Expansión controlada
|
||||
for neighbor in adjacency[facet_idx]:
|
||||
if neighbor not in basin_map:
|
||||
queue.append(neighbor)
|
||||
|
||||
if basin_area >= min_area:
|
||||
yield {
|
||||
'facets': basin_facets,
|
||||
'area': basin_area,
|
||||
'depth': calculate_basin_depth(mesh, basin_facets)
|
||||
}
|
||||
current_basin += 1
|
||||
|
||||
|
||||
def calculate_basin_depth(mesh, basin_facets):
|
||||
"""Calcula la profundidad máxima de la cuenca"""
|
||||
min_z = float('inf')
|
||||
max_z = -float('inf')
|
||||
for idx in basin_facets:
|
||||
center = calculate_incenter(mesh.Facets[idx])
|
||||
if center:
|
||||
min_z = min(min_z, center[2])
|
||||
max_z = max(max_z, center[2])
|
||||
return max_z - min_z if max_z != min_z else 0.0
|
||||
|
||||
|
||||
def simulate_water_flow(mesh_obj, basins, rainfall=1.0):
|
||||
""" Simulación de flujo con prevención de bucles infinitos """
|
||||
mesh = mesh_obj.Mesh
|
||||
adjacency = build_adjacency(mesh)
|
||||
flow_paths = []
|
||||
|
||||
for basin in basins:
|
||||
start_facets = basin['facets'][:2] # Muestra primeros 10 caminos
|
||||
for start in start_facets:
|
||||
path = []
|
||||
visited = set()
|
||||
current = start
|
||||
|
||||
while current is not None and current not in visited:
|
||||
visited.add(current)
|
||||
facet = mesh.Facets[current]
|
||||
center = calculate_incenter(facet)
|
||||
if not center:
|
||||
break
|
||||
|
||||
path.append(FreeCAD.Vector(*center))
|
||||
|
||||
# Buscar vecino más bajo
|
||||
next_facet = None
|
||||
min_elev = float('inf')
|
||||
for neighbor in adjacency[current]:
|
||||
if neighbor >= len(mesh.Facets):
|
||||
continue
|
||||
|
||||
n_center = calculate_incenter(mesh.Facets[neighbor])
|
||||
if n_center and n_center[2] < min_elev:
|
||||
min_elev = n_center[2]
|
||||
next_facet = neighbor
|
||||
|
||||
current = next_facet if min_elev < center[2] else None
|
||||
|
||||
if len(path) > 1:
|
||||
flow_paths.append(path)
|
||||
|
||||
return flow_paths
|
||||
|
||||
|
||||
def colorize_mesh(mesh_obj, facet_indices, color):
|
||||
"""Coloriza facetas específicas de forma compatible"""
|
||||
mesh = mesh_obj.Mesh
|
||||
|
||||
# Crear nuevo objeto Mesh
|
||||
colored_mesh = Mesh.Mesh()
|
||||
colored_mesh.addMesh(mesh)
|
||||
|
||||
# Crear nuevo objeto en el documento
|
||||
new_obj = FreeCAD.ActiveDocument.addObject("Mesh::Feature", "ColoredBasin")
|
||||
new_obj.Mesh = colored_mesh
|
||||
|
||||
# Asignar colores a los vértices
|
||||
vcolors = []
|
||||
for idx in range(len(mesh.Points)):
|
||||
vcolors.append((0.8, 0.8, 0.8)) # Color base
|
||||
|
||||
for facet_id in facet_indices:
|
||||
facet = mesh.Facets[facet_id]
|
||||
for vtx in facet.PointIndices:
|
||||
vcolors[vtx] = color # Color de la cuenca
|
||||
|
||||
new_obj.ViewObject.PointColor = vcolors
|
||||
new_obj.ViewObject.Lighting = "One side"
|
||||
new_obj.ViewObject.Shading = "Flat Lines"
|
||||
|
||||
|
||||
def create_polyline(points):
|
||||
"""Crea un objeto Polyline en FreeCAD"""
|
||||
if len(points) < 2:
|
||||
return
|
||||
|
||||
poly = Part.makePolygon(points)
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Feature", "FlowPath")
|
||||
obj.Shape = poly
|
||||
obj.ViewObject.LineWidth = 2.0
|
||||
obj.ViewObject.LineColor = (0.0, 0.0, 1.0)
|
||||
|
||||
|
||||
class CommandHydrologicalAnalysis:
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': str(os.path.join(DirIcons, "drop.jpg")),
|
||||
'MenuText': "Hidrological analysis",
|
||||
'Accel': "H, A",
|
||||
'ToolTip': "Hidrological analysis"}
|
||||
|
||||
def IsActive(self):
|
||||
return True
|
||||
|
||||
def Activated(self):
|
||||
# User input parameters (example values)
|
||||
os.environ['OMP_NUM_THREADS'] = str(cpu_count())
|
||||
os.environ['MKL_NUM_THREADS'] = str(cpu_count())
|
||||
os.environ["FREECAD_NO_FORK"] = "1" # Desactiva el fork en sistemas Unix
|
||||
#try:
|
||||
# Parámetros de usuario
|
||||
min_basin_area = 100 # m²
|
||||
rainfall_intensity = 1.0
|
||||
|
||||
# Validar selección
|
||||
mesh_obj = FreeCADGui.Selection.getSelection()[0]
|
||||
if not mesh_obj.isDerivedFrom("Mesh::Feature"):
|
||||
raise ValueError("Selecciona un objeto de malla")
|
||||
|
||||
# Procesamiento principal
|
||||
FreeCAD.Console.PrintMessage(f"buscar basins: ")
|
||||
FreeCADGui.updateGui()
|
||||
basins = list(find_hydrological_basins(mesh_obj, min_basin_area))
|
||||
FreeCAD.Console.PrintMessage(f" - Cuencas identificadas: {len(basins)}\n")
|
||||
'''FreeCAD.Console.PrintMessage(f"simulate_water_flow: ")
|
||||
FreeCADGui.updateGui()
|
||||
flow_paths = simulate_water_flow(mesh_obj, basins, rainfall_intensity)
|
||||
FreeCAD.Console.PrintMessage(f" - Trayectorias de flujo generadas: {len(flow_paths)}\n")
|
||||
FreeCADGui.updateGui()'''
|
||||
|
||||
# Visualización
|
||||
for basin in basins:
|
||||
color = (random.random(), random.random(), random.random())
|
||||
colorize_mesh(mesh_obj, basin['facets'], color)
|
||||
|
||||
'''for path in flow_paths:
|
||||
create_polyline(path)'''
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
'''except Exception as e:
|
||||
FreeCAD.Console.PrintError(f"Error: {str(e)}\n")
|
||||
finally:
|
||||
# Limpieza de memoria
|
||||
import gc
|
||||
gc.collect()'''
|
||||
@@ -7,7 +7,7 @@
|
||||
# Find the associated blog post at: http://blog.eskriett.com/2013/07/19/downloading-google-maps/
|
||||
|
||||
import math
|
||||
# from PIL import Image
|
||||
from PIL import Image
|
||||
import os
|
||||
import urllib
|
||||
|
||||
@@ -15,7 +15,7 @@ import urllib
|
||||
# alternativa a PIL: Image
|
||||
# CV2
|
||||
|
||||
class GoogleMapDownloader:
|
||||
class GoogleMapDownloader1:
|
||||
"""
|
||||
A class which generates high resolution google maps images given
|
||||
a longitude, latitude and zoom level
|
||||
|
||||
207
lib/GoogleSatelitalImageDownload.py
Normal file
207
lib/GoogleSatelitalImageDownload.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import math
|
||||
from PIL import Image
|
||||
import urllib.request
|
||||
from io import BytesIO
|
||||
import time
|
||||
|
||||
|
||||
class GoogleMapDownloader:
|
||||
def __init__(self, zoom=12, layer='raw_satellite'):
|
||||
self._zoom = zoom
|
||||
self.layer_map = {
|
||||
'roadmap': 'm',
|
||||
'terrain': 'p',
|
||||
'satellite': 's',
|
||||
'hybrid': 'y',
|
||||
'raw_satellite': 's'
|
||||
}
|
||||
self._layer = self.layer_map.get(layer, 's')
|
||||
self._style = 'style=feature:all|element:labels|visibility:off' if layer == 'raw_satellite' else ''
|
||||
|
||||
def latlng_to_tile(self, lat, lng):
|
||||
"""Convierte coordenadas a tiles X/Y con precisión decimal"""
|
||||
tile_size = 256
|
||||
numTiles = 1 << self._zoom
|
||||
|
||||
point_x = (tile_size / 2 + lng * tile_size / 360.0) * numTiles / tile_size
|
||||
sin_y = math.sin(lat * (math.pi / 180.0))
|
||||
point_y = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) *
|
||||
-(tile_size / (2 * math.pi))) * numTiles / tile_size
|
||||
|
||||
return point_x, point_y
|
||||
|
||||
def generateImage(self, sw_lat, sw_lng, ne_lat, ne_lng):
|
||||
"""Genera la imagen para un área rectangular definida por coordenadas"""
|
||||
# Convertir coordenadas a tiles con precisión decimal
|
||||
sw_x, sw_y = self.latlng_to_tile(sw_lat, sw_lng)
|
||||
ne_x, ne_y = self.latlng_to_tile(ne_lat, ne_lng)
|
||||
|
||||
# Asegurar que las coordenadas estén en el orden correcto
|
||||
min_x = min(sw_x, ne_x)
|
||||
max_x = max(sw_x, ne_x)
|
||||
min_y = min(sw_y, ne_y)
|
||||
max_y = max(sw_y, ne_y)
|
||||
|
||||
# Calcular los tiles mínimos y máximos necesarios
|
||||
min_tile_x = math.floor(min_x)
|
||||
max_tile_x = math.ceil(max_x)
|
||||
min_tile_y = math.floor(min_y)
|
||||
max_tile_y = math.ceil(max_y)
|
||||
|
||||
# Calcular dimensiones en tiles
|
||||
tile_width = int(max_tile_x - min_tile_x) + 1
|
||||
tile_height = int(max_tile_y - min_tile_y) + 1
|
||||
|
||||
# Crear imagen temporal para todos los tiles necesarios
|
||||
full_img = Image.new('RGB', (tile_width * 256, tile_height * 256))
|
||||
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
|
||||
servers = ['mt0', 'mt1', 'mt2', 'mt3']
|
||||
|
||||
for x in range(min_tile_x, max_tile_x + 1):
|
||||
for y in range(min_tile_y, max_tile_y + 1):
|
||||
server = servers[(x + y) % len(servers)]
|
||||
base_url = f"https://{server}.google.com/vt?lyrs={self._layer}&x={x}&y={y}&z={self._zoom}"
|
||||
url = f"{base_url}&{self._style}" if self._style else base_url
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
with urllib.request.urlopen(req) as response:
|
||||
tile_data = response.read()
|
||||
|
||||
img = Image.open(BytesIO(tile_data))
|
||||
pos_x = (x - min_tile_x) * 256
|
||||
pos_y = (y - min_tile_y) * 256
|
||||
full_img.paste(img, (pos_x, pos_y))
|
||||
#print(f"✅ Tile ({x}, {y}) descargado")
|
||||
|
||||
except Exception as e:
|
||||
#print(f"❌ Error en tile ({x},{y}): {str(e)}")
|
||||
error_tile = Image.new('RGB', (256, 256), (255, 0, 0))
|
||||
full_img.paste(error_tile, (pos_x, pos_y))
|
||||
|
||||
time.sleep(0.05)
|
||||
|
||||
# Calcular desplazamientos para recorte final
|
||||
left_offset = int((min_x - min_tile_x) * 256)
|
||||
right_offset = int((max_tile_x - max_x) * 256)
|
||||
top_offset = int((min_y - min_tile_y) * 256)
|
||||
bottom_offset = int((max_tile_y - max_y) * 256)
|
||||
|
||||
# Calcular coordenadas de recorte
|
||||
left = left_offset
|
||||
top = top_offset
|
||||
right = full_img.width - right_offset
|
||||
bottom = full_img.height - bottom_offset
|
||||
|
||||
# Asegurar que las coordenadas sean válidas
|
||||
if right < left:
|
||||
right = left + 1
|
||||
if bottom < top:
|
||||
bottom = top + 1
|
||||
# Recortar la imagen al área exacta solicitada
|
||||
result = full_img.crop((
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom
|
||||
))
|
||||
|
||||
return full_img
|
||||
|
||||
|
||||
class GoogleMapDownloader_1:
|
||||
def __init__(self, zoom=12, layer='hybrid'):
|
||||
"""
|
||||
Args:
|
||||
zoom: Zoom level (0-23)
|
||||
layer: Map type (roadmap, terrain, satellite, hybrid)
|
||||
"""
|
||||
self._zoom = zoom
|
||||
self.layer_map = {
|
||||
'roadmap': 'm',
|
||||
'terrain': 'p',
|
||||
'satellite': 's',
|
||||
'hybrid': 'y',
|
||||
'raw_satellite': 's' # Capa especial sin etiquetas
|
||||
}
|
||||
self._layer = self.layer_map.get(layer, 's')
|
||||
self._style = 'style=feature:all|element:labels|visibility:off' if layer == 'raw_satellite' else ''
|
||||
|
||||
def latlng_to_tile(self, lat, lng):
|
||||
"""Convierte coordenadas a tiles X/Y"""
|
||||
tile_size = 256
|
||||
numTiles = 1 << self._zoom
|
||||
|
||||
# Cálculo para coordenada X
|
||||
point_x = (tile_size / 2 + lng * tile_size / 360.0) * numTiles / tile_size
|
||||
|
||||
# Cálculo para coordenada Y
|
||||
sin_y = math.sin(lat * (math.pi / 180.0))
|
||||
point_y = ((tile_size / 2) + 0.5 * math.log((1 + sin_y) / (1 - sin_y)) *
|
||||
-(tile_size / (2 * math.pi))) * numTiles / tile_size
|
||||
|
||||
return int(point_x), int(point_y)
|
||||
|
||||
def generateImage(self, sw_lat, sw_lng, ne_lat, ne_lng):
|
||||
"""
|
||||
Genera la imagen para un área rectangular definida por:
|
||||
- sw_lat, sw_lng: Esquina suroeste (latitud, longitud)
|
||||
- ne_lat, ne_lng: Esquina noreste (latitud, longitud)
|
||||
"""
|
||||
# Convertir coordenadas a tiles
|
||||
sw_x, sw_y = self.latlng_to_tile(sw_lat, sw_lng)
|
||||
ne_x, ne_y = self.latlng_to_tile(ne_lat, ne_lng)
|
||||
|
||||
# Determinar rango de tiles
|
||||
min_x = min(sw_x, ne_x)
|
||||
max_x = max(sw_x, ne_x)
|
||||
min_y = min(sw_y, ne_y)
|
||||
max_y = max(sw_y, ne_y)
|
||||
|
||||
# Calcular dimensiones en tiles
|
||||
tile_width = max_x - min_x + 1
|
||||
tile_height = max_y - min_y + 1
|
||||
|
||||
# Crear imagen final
|
||||
result = Image.new('RGB', (256 * tile_width, 256 * tile_height))
|
||||
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
|
||||
servers = ['mt0', 'mt1', 'mt2', 'mt3']
|
||||
|
||||
print(f"Descargando {tile_width}x{tile_height} tiles ({tile_width * tile_height} total)")
|
||||
|
||||
for x in range(min_x, max_x + 1):
|
||||
for y in range(min_y, max_y + 1):
|
||||
# Seleccionar servidor rotatorio
|
||||
server = servers[(x + y) % len(servers)]
|
||||
# Construir URL con parámetro para quitar etiquetas si es necesario
|
||||
url = f"https://{server}.google.com/vt?lyrs={self._layer}&x={x}&y={y}&z={self._zoom}"
|
||||
if self._style:
|
||||
url = f"{url}&{self._style}"
|
||||
|
||||
print("Descargando tile:", url)
|
||||
try:
|
||||
# Descargar tile
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
with urllib.request.urlopen(req) as response:
|
||||
tile_data = response.read()
|
||||
|
||||
# Procesar en memoria
|
||||
img = Image.open(BytesIO(tile_data))
|
||||
pos_x = (x - min_x) * 256
|
||||
pos_y = (y - min_y) * 256
|
||||
result.paste(img, (pos_x, pos_y))
|
||||
|
||||
print(f"✅ Tile ({x}, {y}) descargado")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error en tile ({x},{y}): {str(e)}")
|
||||
# Crear tile de error (rojo)
|
||||
error_tile = Image.new('RGB', (256, 256), (255, 0, 0))
|
||||
pos_x = (x - min_x) * 256
|
||||
pos_y = (y - min_y) * 256
|
||||
result.paste(error_tile, (pos_x, pos_y))
|
||||
|
||||
# Pausa para evitar bloqueos
|
||||
time.sleep(0.05)
|
||||
|
||||
return result
|
||||
20
package.xml
Normal file
20
package.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||
<name>PVPlant</name>
|
||||
<description>FreeCAD Fotovoltaic Power Plant Toolkit</description>
|
||||
<version>2025.07.06</version>
|
||||
<date>2025.07.06</date>
|
||||
<maintainer email="javier.branagutierrez@gmail.com">Javier Braña</maintainer>
|
||||
<license file="LICENSE">LGPL-2.1-or-later</license>
|
||||
<url type="repository" branch="main">https://homehud.duckdns.org/javier/PVPlant</url>
|
||||
<url type="bugtracker">https://homehud.duckdns.org/javier/PVPlant/issues</url>
|
||||
<icon>PVPlant/Resources/Icons/PVPlantWorkbench.svg</icon>
|
||||
|
||||
<content>
|
||||
<workbench>
|
||||
<classname>RoadWorkbench</classname>
|
||||
<subdirectory>./</subdirectory>
|
||||
</workbench>
|
||||
</content>
|
||||
|
||||
</package>
|
||||
14
reload.py
14
reload.py
@@ -26,16 +26,17 @@ class _CommandReload:
|
||||
PVPlantGeoreferencing, PVPlantImportGrid, PVPlantTerrainAnalisys, \
|
||||
PVPlantSite, PVPlantRackChecking, PVPlantFence, PVPlantFencePost, PVPlantFenceGate, \
|
||||
PVPlantCreateTerrainMesh, \
|
||||
PVPlantFoundation, PVPlantTreeGenerator, PVPlantBuilding, PVPlantTrench, PVPlantEarthWorks, PVPlantPad, \
|
||||
PVPlantFoundation, PVPlantBuilding, PVPlantEarthWorks, PVPlantPad, \
|
||||
PVPlantRoad, PVPlantTerrain, PVPlantStringing, PVPlantManhole, \
|
||||
GraphProfile
|
||||
|
||||
from Civil import PVPlantTrench
|
||||
from Vegetation import PVPlantTreeGenerator
|
||||
|
||||
from Mechanical.Frame import PVPlantFrame
|
||||
from Project.Area import PVPlantArea, PVPlantAreaUtils
|
||||
#from Importer import importDXF
|
||||
from Export import PVPlantBOQCivil, PVPlantBOQElectrical, PVPlantBOQMechanical, exportPVSyst, exportDXF
|
||||
from Utils import PVPlantUtils, PVPlantTrace, m_gui_edit, profile_editor, graphics
|
||||
#from Lib import GoogleMapDownloader
|
||||
|
||||
from Electrical.Cable import PVPlantCable, PVPlantElectricalLine
|
||||
from Electrical import Conduit
|
||||
@@ -47,6 +48,8 @@ class _CommandReload:
|
||||
import MeshTools.Triangulation as Triangulation
|
||||
from Project import ProjectSetup
|
||||
import importlib
|
||||
import hydro.hydrological as hydro
|
||||
import Importer.importOSM as iOSM
|
||||
|
||||
importlib.reload(ProjectSetup)
|
||||
importlib.reload(PVPlantPlacement)
|
||||
@@ -98,6 +101,11 @@ class _CommandReload:
|
||||
importlib.reload(layoutToExcel)
|
||||
importlib.reload(Conduit)
|
||||
|
||||
importlib.reload(hydro)
|
||||
importlib.reload(iOSM)
|
||||
|
||||
import Project.GenerateExternalDocument as GED
|
||||
importlib.reload(GED)
|
||||
|
||||
#importlib.reload(GoogleMapDownloader)
|
||||
print("Reload modules...")
|
||||
|
||||
@@ -5,4 +5,20 @@ openpyxl~=3.1.2
|
||||
utm~=0.7.0
|
||||
PySide2~=5.15.8
|
||||
requests~=2.31.0
|
||||
setuptools~=68.2.2
|
||||
setuptools~=68.2.2
|
||||
laspy~=2.5.3
|
||||
geopy~=2.4.1
|
||||
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
|
||||
pyproj~=3.7.1
|
||||
simplekml~=1.3.6
|
||||
geojson~=3.1.0
|
||||
certifi~=2023.11.17
|
||||
SciPy~=1.11.4
|
||||
ezdxf~=1.4.1
|
||||
Reference in New Issue
Block a user