Ejemplos de programación

Módulo: crskit

Programas completos que usan el paquete crskit con la base de datos de EPSG y las rejillas que instala Digi3D.AI en C:\ProgramData\Digi3D.NET\OpenGis\. La salida de cada ejemplo se ha obtenido ejecutando el programa con crskit 0.6.0 y la base de datos de EPSG 12.057.

Antes de ejecutar los ejemplos, instala el paquete:

pip install crskit

En un equipo sin Digi3D.AI, instala pip install crskit[epsg], cambia la llamada a crskit.init por crskit.init(data_directory=...) con la carpeta de las rejillas y consulta Inicialización.

El tutorial de crskit en español tiene más ejemplos: elección entre varias operaciones, alturas sobre EGM2008 y transformación de muchos puntos con NumPy.

Convertir un punto de ETRS89 / UTM 30N a geográficas

Convierte las coordenadas UTM de la Puerta del Sol de Madrid (EPSG:25830) a coordenadas geográficas ETRS89 (EPSG:4258).

import crskit

crskit.init(r"C:\ProgramData\Digi3D.NET\OpenGis\epsg-fiel.sqlite")

utm30n = crskit.crs_from_epsg(25830)    # ETRS89 / UTM zone 30N: este, norte
etrs89 = crskit.crs_from_epsg(4258)     # ETRS89 geográficas 2D: latitud, longitud
print(utm30n.axes)
print(etrs89.axes)

transformacion = crskit.transformation(utm30n, etrs89)

latitud, longitud = transformacion.transform([440291.284, 4474254.600])
print(f"Latitud:  {latitud:.8f}")
print(f"Longitud: {longitud:.8f}")

Salida:

[('E', 'East'), ('N', 'North')]
[('Lat', 'North'), ('Lon', 'East')]
Latitud:  40.41677500
Longitud: -3.70379000

EPSG:4258 declara primero la latitud, así que transform devuelve [latitud, longitud].

Convertir de ETRS89 / UTM 30N + altura de Alicante a ETRS89 geográficas 3D

Convierte un punto con coordenadas UTM y altura ortométrica (altura sobre el nivel medio del mar en Alicante, EPSG:5782) a coordenadas geográficas ETRS89 con altura elipsoidal (EPSG:4937). EPSG no tiene código para el SRC compuesto UTM 30N + altura de Alicante, así que el programa lo crea con compound_crs.

La transformación usa la operación EPSG:9410, que necesita la rejilla del geoide EGM08_REDNAP.txt. Digi3D.AI instala esa rejilla en la misma carpeta que la base de datos.

import crskit

crskit.init(r"C:\ProgramData\Digi3D.NET\OpenGis\epsg-fiel.sqlite")

# EPSG no tiene código para UTM 30N + altura de Alicante: se compone a partir de los dos códigos.
origen = crskit.compound_crs(25830, 5782)
destino = crskit.crs_from_epsg(4937)    # ETRS89 geográficas 3D: latitud, longitud, altura elipsoidal
print(origen)
print(origen.axes)
print(destino.axes)

try:
    transformacion = crskit.transformation(origen, destino)
except crskit.GridFileNotFoundError as error:
    print(f"Falta la rejilla {error.grid_file}; se ha buscado en {error.searched_path}")
    raise

latitud, longitud, altura = transformacion.transform([440291.284, 4474254.600, 648.888])
print(f"Latitud:          {latitud:.8f}")
print(f"Longitud:         {longitud:.8f}")
print(f"Altura elipsoidal: {altura:.3f} m")

Salida:

<Crs no code "ETRS89 / UTM zone 30N + Alicante height" (3D)>
[('E', 'East'), ('N', 'North'), ('H', 'Up')]
[('Lat', 'North'), ('Lon', 'East'), ('h', 'Up')]
Latitud:          40.41677500
Longitud:         -3.70379000
Altura elipsoidal: 700.000 m

La altura ortométrica de 648,888 m corresponde a una altura elipsoidal de 700,000 m: en ese punto, el geoide EGM08-REDNAP está 51,112 m por encima del elipsoide.

Leer el WKT de un sistema

Escribe el WKT de ETRS89 / UTM 30N en tres dialectos, guarda un archivo .prj con el dialecto de ESRI y crea un SRC a partir de ese archivo.

from pathlib import Path

import crskit

crskit.init(r"C:\ProgramData\Digi3D.NET\OpenGis\epsg-fiel.sqlite")

utm30n = crskit.crs_from_epsg(25830)

print(utm30n.to_wkt())                                  # WKT 1 de OGC
print(utm30n.to_wkt(crskit.WktVersion.WKT2_2019))       # ISO 19162:2019

# Archivo .prj con el dialecto de ESRI.
prj = Path("capa.prj")
prj.write_text(utm30n.to_wkt(crskit.WktVersion.WKT1_ESRI), encoding="utf-8")

leido = crskit.crs_from_wkt(prj.read_text(encoding="utf-8"))
print(leido)
print(leido.name, leido.authority_code, leido.dimension, leido.axes)

# El WKT 1 de OGC conserva el código EPSG y la definición completa.
copia = crskit.crs_from_wkt(utm30n.to_wkt())
print(copia == utm30n, copia.compare(utm30n))

Salida (cada WKT ocupa una sola línea):

PROJCS["ETRS89 / UTM zone 30N",GEOGCS["ETRS89",DATUM["European Terrestrial Reference System 1989 ensemble",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree (supplier to define representation)",0.01745329251994328,AUTHORITY["EPSG","9122"]],AXIS["Lat",North],AXIS["Lon",East],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-2.999999999999997],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],PARAMETER["semi_major",6378137],PARAMETER["semi_minor",6356752.314140356],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["E",East],AXIS["N",North],AUTHORITY["EPSG","25830"]]
PROJCRS["ETRS89 / UTM zone 30N",BASEGEOGCRS["ETRS89",DATUM["European Terrestrial Reference System 1989 ensemble",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",7019]],ID["EPSG",6258]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.01745329251994328,ID["EPSG",9102]],ID["EPSG",8901]],ID["EPSG",4258]],CONVERSION["unnamed",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",-2.999999999999997,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["E",east,ORDER[1]],AXIS["N",north,ORDER[2]],LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",25830]]
<Crs no code "ETRS_1989_UTM_Zone_30N" (2D)>
ETRS_1989_UTM_Zone_30N None 2 [('X', 'East'), ('Y', 'North')]
True 100

El WKT de ESRI no lleva código EPSG: el SRC leído del archivo .prj tiene authority_code igual a None. Para conservar el código en un .prj, escribe el dialecto WktVersion.WKT1_ESRI_WITH_EPSG_CODE.

Usar los códigos propios de Digi3D.AI

Digi3D.AI guarda la altura sobre el geoide EGM08-REDNAP con los códigos propios 69036406 (península) y 69036407 (Canarias), que no están en la base de datos de EPSG. Digi3D.AI registra en crskit un alias que resuelve cada código propio como su código de EPSG equivalente; el paquete crskit no registra esos alias (consulta Códigos propios de Digi3D.AI).

El programa aplica la misma equivalencia con un diccionario antes de llamar a crskit:

import crskit

# Códigos propios de Digi3D.AI que no están en la base de datos de EPSG y su equivalente en EPSG.
ALIAS_DIGI3D = {
    69036406: 5782,     # EGM08_REDNAP Península -> Alicante height
    69036407: 9398,     # EGM08_REDNAP Canarias  -> Tenerife height
}


def resuelve(codigo: int) -> int:
    """Código EPSG que corresponde a un código de Digi3D.AI."""
    return ALIAS_DIGI3D.get(codigo, codigo)


crskit.init(r"C:\ProgramData\Digi3D.NET\OpenGis\epsg-fiel.sqlite")

codigo_vertical = 69036406      # sistema vertical que guarda un archivo de Digi3D.AI

try:
    crskit.crs_from_epsg(codigo_vertical)
except crskit.AuthorityCodeNotFoundError as error:
    print(f"Sin alias: {error}")

origen = crskit.compound_crs(25830, resuelve(codigo_vertical))
print(origen)

transformacion = crskit.transformation(origen, crskit.crs_from_epsg(4937))
print(transformacion.transform([440291.284, 4474254.600, 648.888]))

Salida:

Sin alias: Could not locate the coordinate system with code: 69036406
<Crs no code "ETRS89 / UTM zone 30N + Alicante height" (3D)>
[40.41677499862431, -3.70379000408711, 699.9996790771485]

crs_from_epsg(69036406) lanza AuthorityCodeNotFoundError. Con el código 5782 que devuelve resuelve, la transformación usa la rejilla EGM08_REDNAP.txt, igual que en el ejemplo anterior.