139 lines
5.0 KiB
Python
139 lines
5.0 KiB
Python
# /**********************************************************************
|
||
# * *
|
||
# * Copyright (c) 2026 Javier Brana <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 *
|
||
# * *
|
||
# ***********************************************************************
|
||
|
||
"""
|
||
Proyecciones y transformaciones geodésicas unificadas para PVPlant.
|
||
|
||
Reemplaza el uso disperso de la librería 'utm' con pyproj (PROJ),
|
||
soporte multi-zona UTM y transformaciones entre datums.
|
||
|
||
Uso básico:
|
||
from lib.projection import latlon_to_utm, utm_to_latlon, get_utm_zone
|
||
"""
|
||
|
||
import FreeCAD
|
||
from pyproj import CRS, Transformer
|
||
from pyproj.aoi import AreaOfInterest
|
||
from pyproj.database import query_utm_crs_info
|
||
|
||
# WGS84 – sistema de coordenadas geográfico de referencia
|
||
_WGS84 = CRS.from_epsg(4326)
|
||
|
||
# Cache de transformadores UTM por zona (lazy)
|
||
_utm_transformers = {}
|
||
|
||
|
||
def _get_utm_transformer(lat, lon):
|
||
"""Obtiene (o crea en caché) un transformador UTM para la zona de las coordenadas dadas.
|
||
Returns:
|
||
tuple: (transformer, zone_number, zone_letter)
|
||
"""
|
||
# Determinar la zona UTM a partir de lat/lon
|
||
zone_number = int((lon + 180) / 6) + 1
|
||
|
||
if lat >= 0:
|
||
zone_letter = 'N'
|
||
epsg = 32600 + zone_number
|
||
else:
|
||
zone_letter = 'S'
|
||
epsg = 32700 + zone_number
|
||
|
||
cache_key = (zone_number, zone_letter)
|
||
if cache_key not in _utm_transformers:
|
||
utm_crs = CRS.from_epsg(epsg)
|
||
_utm_transformers[cache_key] = Transformer.from_crs(
|
||
_WGS84, utm_crs, always_xy=True
|
||
)
|
||
|
||
return _utm_transformers[cache_key], zone_number, zone_letter
|
||
|
||
|
||
def latlon_to_utm(lat, lon):
|
||
"""Convierte coordenadas geográficas (WGS84) a UTM (este, norte, zona, letra).
|
||
|
||
Args:
|
||
lat (float): Latitud en grados.
|
||
lon (float): Longitud en grados.
|
||
|
||
Returns:
|
||
tuple: (easting, northing, zone_number, zone_letter)
|
||
easting/northing en metros.
|
||
"""
|
||
transformer, zone_number, zone_letter = _get_utm_transformer(lat, lon)
|
||
easting, northing = transformer.transform(lon, lat)
|
||
return easting, northing, zone_number, zone_letter
|
||
|
||
|
||
def utm_to_latlon(easting, northing, zone_number, zone_letter='N'):
|
||
"""Convierte coordenadas UTM a geográficas (WGS84).
|
||
|
||
Args:
|
||
easting (float): Coordenada E en metros.
|
||
northing (float): Coordenada N en metros.
|
||
zone_number (int): Número de zona UTM (1-60).
|
||
zone_letter (str): Letra de zona ('N' o 'S').
|
||
|
||
Returns:
|
||
tuple: (latitude, longitude) en grados.
|
||
"""
|
||
if zone_letter.upper() not in ('N', 'S'):
|
||
zone_letter = 'N'
|
||
|
||
epsg = 32600 + zone_number if zone_letter.upper() == 'N' else 32700 + zone_number
|
||
utm_crs = CRS.from_epsg(epsg)
|
||
transformer = Transformer.from_crs(utm_crs, _WGS84, always_xy=True)
|
||
lon, lat = transformer.transform(easting, northing)
|
||
return lat, lon
|
||
|
||
|
||
def get_utm_zone(lat, lon):
|
||
"""Obtiene la zona UTM para unas coordenadas dadas.
|
||
|
||
Args:
|
||
lat (float): Latitud en grados.
|
||
lon (float): Longitud en grados.
|
||
|
||
Returns:
|
||
tuple: (zone_number, zone_letter)
|
||
"""
|
||
_, _, zone_number, zone_letter = latlon_to_utm(lat, lon)
|
||
return zone_number, zone_letter
|
||
|
||
|
||
def latlon_to_utm_vector(lat, lon, elevation=0.0):
|
||
"""Convierte lat/lon/elevación a un FreeCAD.Vector en UTM (mm).
|
||
|
||
Args:
|
||
lat (float): Latitud en grados.
|
||
lon (float): Longitud en grados.
|
||
elevation (float): Elevación en metros (default 0).
|
||
|
||
Returns:
|
||
FreeCAD.Vector: Coordenadas UTM en milímetros.
|
||
"""
|
||
transformer, _, _ = _get_utm_transformer(lat, lon)
|
||
easting, northing = transformer.transform(lon, lat)
|
||
return FreeCAD.Vector(
|
||
round(easting, 0),
|
||
round(northing, 0),
|
||
round(elevation, 0)
|
||
) * 1000 |