Files
PVPlant/Export/exportKMZ.py

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 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