6 Commits

7 changed files with 143 additions and 42 deletions
+2
View File
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
+1 -1
View File
@@ -114,7 +114,7 @@ def makeTrench(base=None):
try: try:
folder = FreeCAD.ActiveDocument.Trenches folder = FreeCAD.ActiveDocument.Trenches
except: except AttributeError:
folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Trenches') folder = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Trenches')
folder.Label = "Trenches" folder.Label = "Trenches"
folder.addObject(obj) folder.addObject(obj)
+89 -28
View File
@@ -58,8 +58,9 @@ class MapWindow(QtGui.QWidget):
self.setupUi() self.setupUi()
def setupUi(self): def setupUi(self):
from PySide2.QtWebEngineWidgets import QWebEngineView # Intentar cargar QtWebEngine (no siempre disponible, ej: FreeCAD flatpak)
from PySide2.QtWebChannel import QWebChannel QWebEngineView, QWebChannel = self._load_webengine()
self._webengine_available = QWebEngineView is not None
self.ui = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantGeoreferencing.ui", self) self.ui = FreeCADGui.PySideUic.loadUi(PVPlantResources.__dir__ + "/PVPlantGeoreferencing.ui", self)
@@ -86,36 +87,54 @@ class MapWindow(QtGui.QWidget):
self.layout.addWidget(RightWidget) self.layout.addWidget(RightWidget)
# Left Widgets: # Left Widgets:
# -- Search Bar: if self._webengine_available:
self.valueSearch = QtGui.QLineEdit(self) # -- Search Bar:
self.valueSearch.setPlaceholderText("Search") self.valueSearch = QtGui.QLineEdit(self)
self.valueSearch.returnPressed.connect(self.onSearch) self.valueSearch.setPlaceholderText("Search")
self.valueSearch.returnPressed.connect(self.onSearch)
searchbutton = QtGui.QPushButton('Search') searchbutton = QtGui.QPushButton('Search')
searchbutton.setFixedWidth(80) searchbutton.setFixedWidth(80)
searchbutton.clicked.connect(self.onSearch) searchbutton.clicked.connect(self.onSearch)
SearchBarLayout = QtGui.QHBoxLayout(self) SearchBarLayout = QtGui.QHBoxLayout(self)
SearchBarLayout.addWidget(self.valueSearch) SearchBarLayout.addWidget(self.valueSearch)
SearchBarLayout.addWidget(searchbutton) SearchBarLayout.addWidget(searchbutton)
LeftLayout.addLayout(SearchBarLayout) LeftLayout.addLayout(SearchBarLayout)
# -- Webbroser: # -- Web browser:
self.view = QWebEngineView() self.view = QWebEngineView()
self.channel = QWebChannel(self.view.page()) self.channel = QWebChannel(self.view.page())
self.view.page().setWebChannel(self.channel) self.view.page().setWebChannel(self.channel)
self.channel.registerObject("MyApp", self) self.channel.registerObject("MyApp", self)
file = os.path.join(DirResources, "webs", "main.html") file = os.path.join(DirResources, "webs", "main.html")
self.view.page().loadFinished.connect(self.onLoadFinished) self.view.page().loadFinished.connect(self.onLoadFinished)
self.view.page().load(QtCore.QUrl.fromLocalFile(file)) self.view.page().load(QtCore.QUrl.fromLocalFile(file))
LeftLayout.addWidget(self.view) LeftLayout.addWidget(self.view)
# self.layout.addWidget(self.view, 1, 0, 1, 3) else:
# -- Modo manual: entrada de coordenadas sin mapa web
self.valueSearch = QtGui.QLineEdit(self)
self.valueSearch.setPlaceholderText("Latitud, Longitud (ej: 40.4168, -3.7038)")
self.valueSearch.returnPressed.connect(self.onManualCoords)
searchbutton = QtGui.QPushButton('Ir')
searchbutton.setFixedWidth(80)
searchbutton.clicked.connect(self.onManualCoords)
SearchBarLayout = QtGui.QHBoxLayout(self)
SearchBarLayout.addWidget(self.valueSearch)
SearchBarLayout.addWidget(searchbutton)
LeftLayout.addLayout(SearchBarLayout)
info = QtGui.QLabel("Mapa web no disponible. Introduce coordenadas manualmente.")
info.setStyleSheet("color: #888; font-style: italic; padding: 20px;")
info.setAlignment(QtCore.Qt.AlignCenter)
LeftLayout.addWidget(info)
# -- Latitud y longitud: # -- Latitud y longitud:
self.labelCoordinates = QtGui.QLabel() self.labelCoordinates = QtGui.QLabel()
self.labelCoordinates.setFixedHeight(21) self.labelCoordinates.setFixedHeight(21)
LeftLayout.addWidget(self.labelCoordinates) LeftLayout.addWidget(self.labelCoordinates)
# self.layout.addWidget(self.labelCoordinates, 2, 0, 1, 3)
# Right Widgets: # Right Widgets:
labelKMZ = QtGui.QLabel() labelKMZ = QtGui.QLabel()
@@ -139,9 +158,6 @@ class MapWindow(QtGui.QWidget):
radio3 = QtGui.QRadioButton("Datos GPS") radio3 = QtGui.QRadioButton("Datos GPS")
radio1.setChecked(True) radio1.setChecked(True)
# buttonDialog = QtGui.QPushButton('...')
# buttonDialog.setEnabled(False)
vbox = QtGui.QVBoxLayout(self) vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(radio1) vbox.addWidget(radio1)
vbox.addWidget(radio2) vbox.addWidget(radio2)
@@ -149,7 +165,6 @@ class MapWindow(QtGui.QWidget):
self.groupbox.setLayout(vbox) self.groupbox.setLayout(vbox)
RightLayout.addWidget(self.groupbox) RightLayout.addWidget(self.groupbox)
# ------------------------
self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS") self.checkboxImportGis = QtGui.QCheckBox("Importar datos GIS")
RightLayout.addWidget(self.checkboxImportGis) RightLayout.addWidget(self.checkboxImportGis)
@@ -174,6 +189,52 @@ class MapWindow(QtGui.QWidget):
with open(file, 'r') as f: with open(file, 'r') as f:
frame.runJavaScript(f.read()) frame.runJavaScript(f.read())
def _load_webengine(self):
"""Intenta cargar QWebEngineView desde cualquier versión de PySide.
Retorna (QWebEngineView_class, QWebChannel_class) o (None, None)."""
for modpath in [
'PySide6.QtWebEngineWidgets',
'PySide6.QtWebEngineCore',
'PySide6.QtWebEngineQuick',
'PySide2.QtWebEngineWidgets',
'PySide.QtWebEngineWidgets',
]:
try:
parts = modpath.split('.')
mod = __import__(parts[0], fromlist=parts[1:])
for p in parts[1:]:
mod = getattr(mod, p)
View = getattr(mod, 'QWebEngineView', None)
Channel = getattr(mod, 'QWebChannel', None)
if View is not None:
return View, Channel
except (ImportError, AttributeError):
continue
# Fallback: intentar por separado QtWebChannel (sí existe en flatpak)
try:
from PySide6.QtWebChannel import QWebChannel as Channel
except ImportError:
Channel = None
FreeCAD.Console.PrintWarning(
"PVPlantGeoreferencing: QtWebEngine no disponible. "
"Usando modo manual de coordenadas.\n")
return None, Channel
def onManualCoords(self):
"""Procesa entrada manual de latitud,longitud"""
text = self.valueSearch.text().strip()
if not text:
return
try:
parts = text.replace(',', ' ').split()
lat = float(parts[0])
lon = float(parts[1])
self.georeference_coordinates = {'lat': lat, 'lon': lon}
self.labelCoordinates.setText(f"{lat:.6f}, {lon:.6f}")
FreeCAD.Console.PrintMessage(f"Coordenadas: {lat:.6f}, {lon:.6f}\n")
except (ValueError, IndexError):
FreeCAD.Console.PrintError("Formato inválido. Usa: latitud, longitud\n")
def onSearch(self): def onSearch(self):
if self.valueSearch.text() == "": if self.valueSearch.text() == "":
return return
+45 -6
View File
@@ -39,6 +39,51 @@ import os
from PVPlantResources import DirIcons as DirIcons from PVPlantResources import DirIcons as DirIcons
import PVPlantSite import PVPlantSite
# ---------------------------------------------------------------------------
# Adaptador UTM: emula la API de la librería 'utm' usando pyproj
# La librería 'utm' dejó de usarse en favor de pyproj (más completa y mantenida).
# from_latlon(lat, lon) -> (easting, northing, zone_number, zone_letter)
# to_latlon(easting, northing, zone_number, zone_letter) -> (lat, lon)
# ---------------------------------------------------------------------------
_utm_cache = {}
def _get_transformer(lat, lon):
"""Obtiene o crea un transformador UTM para las coordenadas dadas."""
from pyproj import Transformer
zone = int((lon + 180) / 6) + 1
hem = 'S' if lat < 0 else 'N'
key = (zone, hem)
if key not in _utm_cache:
crs_utm = f'+proj=utm +zone={zone} +{hem.lower()} +ellps=WGS84 +datum=WGS84 +units=m +no_defs'
_utm_cache[key] = Transformer.from_crs('EPSG:4326', crs_utm, always_xy=True)
return _utm_cache[key], zone, hem
def from_latlon(lat, lon):
"""Convierte (lat, lon) a UTM. Retorna (easting, northing, zone_number, zone_letter)."""
transformer, zone, hem = _get_transformer(lat, lon)
easting, northing = transformer.transform(lon, lat)
return (easting, northing, zone, hem)
def to_latlon(easting, northing, zone_number, zone_letter):
"""Convierte UTM a (lat, lon)."""
from pyproj import Transformer
hem = zone_letter.upper()
key = (zone_number, hem)
if key not in _utm_cache:
crs_utm = f'+proj=utm +zone={zone_number} +{hem.lower()} +ellps=WGS84 +datum=WGS84 +units=m +no_defs'
_utm_cache[key] = Transformer.from_crs(crs_utm, 'EPSG:4326', always_xy=True)
lon, lat = _utm_cache[key].transform(easting, northing)
return (lat, lon)
# Parche: reemplazar el módulo 'utm' por nuestro adaptador
import sys
class _UTMWrapper:
"""Wrapper para que 'import utm' devuelva nuestras funciones."""
from_latlon = staticmethod(from_latlon)
to_latlon = staticmethod(to_latlon)
sys.modules['utm'] = _UTMWrapper
# ---------------------------------------------------------------------------
def get_elevation_from_oe(coordinates): # v1 deepseek def get_elevation_from_oe(coordinates): # v1 deepseek
"""Obtiene elevaciones de Open-Elevation API y devuelve vectores FreeCAD en coordenadas UTM. """Obtiene elevaciones de Open-Elevation API y devuelve vectores FreeCAD en coordenadas UTM.
@@ -52,7 +97,6 @@ def get_elevation_from_oe(coordinates): # v1 deepseek
return [] return []
import requests import requests
import utm
from requests.exceptions import RequestException from requests.exceptions import RequestException
# Construcción más eficiente de parámetros # Construcción más eficiente de parámetros
@@ -110,7 +154,6 @@ def getElevationFromOE(coordinates):
return None return None
from requests import get from requests import get
import utm
locations_str="" locations_str=""
total = len(coordinates) - 1 total = len(coordinates) - 1
@@ -141,7 +184,6 @@ def getElevationFromOE(coordinates):
def getSinglePointElevationFromBing(lat, lng): def getSinglePointElevationFromBing(lat, lng):
#http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey} #http://dev.virtualearth.net/REST/v1/Elevation/List?points={lat1,long1,lat2,long2,latN,longnN}&heights={heights}&key={BingMapsAPIKey}
import utm
source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points=" source = "http://dev.virtualearth.net/REST/v1/Elevation/List?points="
source += str(lat) + "," + str(lng) source += str(lat) + "," + str(lng)
@@ -166,7 +208,6 @@ def getSinglePointElevationFromBing(lat, lng):
def getGridElevationFromBing(polygon, lat, lng, resolution = 1000): def getGridElevationFromBing(polygon, lat, lng, resolution = 1000):
#http://dev.virtualearth.net/REST/v1/Elevation/Polyline?points=35.89431,-110.72522,35.89393,-110.72578,35.89374,-110.72606,35.89337,-110.72662 #http://dev.virtualearth.net/REST/v1/Elevation/Polyline?points=35.89431,-110.72522,35.89393,-110.72578,35.89374,-110.72606,35.89337,-110.72662
# &heights=ellipsoid&samples=10&key={BingMapsAPIKey} # &heights=ellipsoid&samples=10&key={BingMapsAPIKey}
import utm
import math import math
import requests import requests
@@ -311,7 +352,6 @@ def getSinglePointElevationUtm(lat, lon):
res = s['results'] res = s['results']
print (res) print (res)
import utm
for r in res: for r in res:
c = utm.from_latlon(r['location']['lat'], r['location']['lng']) c = utm.from_latlon(r['location']['lat'], r['location']['lng'])
v = FreeCAD.Vector( v = FreeCAD.Vector(
@@ -323,7 +363,6 @@ def getSinglePointElevationUtm(lat, lon):
def getElevationUTM(polygon, lat, lng, resolution = 10000): def getElevationUTM(polygon, lat, lng, resolution = 10000):
import utm
geo = utm.from_latlon(lat, lng) geo = utm.from_latlon(lat, lng)
# result = (679434.3578335291, 4294023.585627955, 30, 'S') # result = (679434.3578335291, 4294023.585627955, 30, 'S')
# EASTING, NORTHING, ZONE NUMBER, ZONE LETTER # EASTING, NORTHING, ZONE NUMBER, ZONE LETTER
+2 -2
View File
@@ -1164,8 +1164,8 @@ from scipy.interpolate import LinearNDInterpolator
import Part import Part
import FreeCAD import FreeCAD
import FreeCADGui import FreeCADGui
from PySide2 import QtCore, QtGui from PySide import QtCore, QtGui
from PySide2.QtWidgets import QListWidgetItem from PySide.QtWidgets import QListWidgetItem
import os import os
import PVPlantResources import PVPlantResources
+2 -2
View File
@@ -1,8 +1,8 @@
# Script para FreeCAD - Procesador de Documentos Word con Carátula # Script para FreeCAD - Procesador de Documentos Word con Carátula
import os import os
import glob import glob
from PySide2 import QtWidgets, QtCore from PySide import QtWidgets, QtCore
from PySide2.QtWidgets import (QFileDialog, QMessageBox, QProgressDialog, from PySide.QtWidgets import (QFileDialog, QMessageBox, QProgressDialog,
QApplication, QVBoxLayout, QWidget, QPushButton, QApplication, QVBoxLayout, QWidget, QPushButton,
QLabel, QTextEdit) QLabel, QTextEdit)
import FreeCAD import FreeCAD
+2 -3
View File
@@ -2,8 +2,6 @@ numpy~=1.26.2
opencv-python~=4.8.1 opencv-python~=4.8.1
matplotlib~=3.8.2 matplotlib~=3.8.2
openpyxl~=3.1.2 openpyxl~=3.1.2
utm~=0.7.0
PySide2~=5.15.8
requests~=2.31.0 requests~=2.31.0
setuptools~=68.2.2 setuptools~=68.2.2
laspy~=2.5.3 laspy~=2.5.3
@@ -17,4 +15,5 @@ certifi~=2023.11.17
SciPy~=1.11.4 SciPy~=1.11.4
pycollada~=0.7.2 pycollada~=0.7.2
shapely shapely
rtree rtree
pandas