Source code for py_tools_ds.geo.projection
# -*- coding: utf-8 -*-
# py_tools_ds - A collection of geospatial data analysis tools that simplify standard
# operations when handling geospatial raster and vector data as well as projections.
#
# Copyright (C) 2016-2024
# - Daniel Scheffler (GFZ Potsdam, daniel.scheffler@gfz-potsdam.de)
# - Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences Potsdam,
# Germany (https://www.gfz-potsdam.de/)
#
# This software was developed within the context of the GeoMultiSens project funded
# by the German Federal Ministry of Education and Research
# (project grant code: 01 IS 14 010 A-C).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
import warnings
from pyproj import CRS
from typing import Union
from osgeo import osr
from ..environment import gdal_env
__author__ = "Daniel Scheffler"
# try to set GDAL_DATA if not set or invalid
gdal_env.try2set_GDAL_DATA()
[docs]
def get_proj4info(proj: Union[str, int] = None) -> str:
"""Return PROJ4 formatted projection info for the given projection.
e.g. '+proj=utm +zone=43 +datum=WGS84 +units=m +no_defs '
:param proj: the projection to get PROJ4 formatted info for (WKT or 'epsg:1234' or <EPSG_int>)
"""
return CRS.from_user_input(proj).to_proj4().strip()
[docs]
def proj4_to_dict(proj4: str) -> dict:
"""Convert a PROJ4-like string into a dictionary.
:param proj4: the PROJ4-like string
"""
return CRS.from_proj4(proj4).to_dict()
[docs]
def dict_to_proj4(proj4dict: dict) -> str:
"""Convert a PROJ4-like dictionary into a PROJ4 string.
:param proj4dict: the PROJ4-like dictionary
"""
return CRS.from_dict(proj4dict).to_proj4()
[docs]
def proj4_to_WKT(proj4str: str) -> str:
"""Convert a PROJ4-like string into a WKT string.
:param proj4str: the PROJ4-like string
"""
return CRS.from_proj4(proj4str).to_wkt()
[docs]
def prj_equal(prj1: Union[None, int, str],
prj2: Union[None, int, str]
) -> bool:
"""Check if the given two projections are equal.
:param prj1: projection 1 (WKT or 'epsg:1234' or <EPSG_int>)
:param prj2: projection 2 (WKT or 'epsg:1234' or <EPSG_int>)
"""
if prj1 is None and prj2 is None or prj1 == prj2:
return True
else:
# CRS.equals was added in pyproj 2.5
crs1 = CRS.from_user_input(prj1)
crs2 = CRS.from_user_input(prj2)
return crs1.equals(crs2)
[docs]
def isProjectedOrGeographic(prj: Union[str, int, dict]) -> Union[str, None]:
"""
:param prj: accepts EPSG, Proj4 and WKT projections
"""
if prj is None:
return None
crs = CRS.from_user_input(prj)
return 'projected' if crs.is_projected else 'geographic' if crs.is_geographic else None
[docs]
def isLocal(prj: Union[str, int, dict]) -> Union[bool, None]:
"""
:param prj: accepts EPSG, Proj4 and WKT projections
"""
if not prj:
return True
srs = osr.SpatialReference()
if prj.startswith('EPSG:'):
srs.ImportFromEPSG(int(prj.split(':')[1]))
elif prj.startswith('+proj='):
srs.ImportFromProj4(prj)
elif 'GEOGCS' in prj or \
'GEOGCRS' in prj or \
'PROJCS' in prj or \
'PROJCS' in prj or \
'LOCAL_CS' in prj:
srs.ImportFromWkt(prj)
else:
raise RuntimeError('Unknown input projection: \n%s' % prj)
return srs.IsLocal()
[docs]
def EPSG2Proj4(EPSG_code: int) -> str:
return CRS.from_epsg(EPSG_code).to_proj4() if EPSG_code is not None else ''
[docs]
def EPSG2WKT(EPSG_code: int) -> str:
return CRS.from_epsg(EPSG_code).to_wkt(pretty=False) if EPSG_code is not None else ''
[docs]
def WKT2EPSG(wkt: str) -> Union[int, None]:
"""Transform a WKT string to an EPSG code.
:param wkt: WKT definition
:returns: EPSG code
"""
if not isinstance(wkt, str):
raise TypeError("'wkt' must be a string. Received %s." % type(wkt))
if not wkt:
return None
wkt = ''.join([line.strip().replace('\n', '').replace('\r', '')
for line in wkt.splitlines()])
ccrs = CRS.from_wkt(wkt)
if not ccrs.is_bound:
epsg = ccrs.to_epsg()
else:
epsg = ccrs.source_crs.to_epsg()
if epsg is None:
warnings.warn('Could not find a suitable EPSG code for the input WKT string.', RuntimeWarning)
else:
return epsg
[docs]
def get_UTMzone(prj: str) -> Union[int, None]:
if isProjectedOrGeographic(prj) == 'projected':
srs = osr.SpatialReference()
srs.ImportFromWkt(prj)
return srs.GetUTMZone()
else:
return None
[docs]
def get_prjLonLat(fmt: str = 'wkt') -> Union[str, dict]:
"""Return standard geographic projection (EPSG 4326) in the WKT or PROJ4 format.
:param fmt: target format - 'WKT' or 'PROJ4'
"""
if not re.search('wkt', fmt, re.I) or re.search('Proj4', fmt, re.I):
raise ValueError(fmt, 'Unsupported output format.')
if re.search('wkt', fmt, re.I):
return CRS.from_epsg(4326).to_wkt()
else:
return CRS.from_epsg(4326).to_proj4()