primera versión
This commit is contained in:
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
|
||||
|
||||
Reference in New Issue
Block a user