Files
PVPlant/Export/exportKMZ.py
T

279 lines
9.7 KiB
Python
Raw Normal View History

2025-03-11 21:13:09 +01:00
# -*- coding: utf-8 -*-
import os
import sys
import zipfile
import tempfile
import shutil
import xml.etree.ElementTree as ET
from PySide import QtWidgets, QtCore, QtGui
2025-03-11 21:13:09 +01:00
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