Skip to content

Commit

Permalink
Merge pull request python-rasters#22 from gregory-halverson/main
Browse files Browse the repository at this point in the history
fixing imports, including docstrings
  • Loading branch information
gregory-halverson authored Oct 31, 2024
2 parents 0f2eff6 + 505309b commit 0bf525b
Show file tree
Hide file tree
Showing 25 changed files with 1,061 additions and 118 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,7 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

.vscode

.DS_Store
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "rasters"
version = "1.3.2"
version = "1.3.3"
description = "raster processing toolkit"
readme = "README.md"
authors = [
Expand All @@ -31,6 +31,7 @@ dependencies = [
"astropy",
"pykdtree",
"pyresample",
"pytest",
"scipy",
"six",
]
Expand Down
145 changes: 100 additions & 45 deletions rasters/CRS.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,47 @@
from __future__ import annotations

from typing import Any, Union
from typing import Any, Optional, Union

import math
import warnings
from collections import OrderedDict

import pyproj

import shapely.wkt

class CRS(pyproj.CRS):
"""
A class representing a Coordinate Reference System (CRS), extending pyproj.CRS.
This class provides additional functionalities and representations for CRS objects,
including EPSG code handling, proj4 string representation, and conversion to different formats.
Attributes:
projparams (Any): Projection parameters used to initialize the CRS object.
"""

def __init__(self, projparams: Any = None, **kwargs) -> None:
"""
Initializes a new CRS object.
Args:
projparams (Any): Projection parameters used to initialize the CRS object.
**kwargs: Additional keyword arguments passed to the pyproj.CRS constructor.
"""
super(CRS, self).__init__(projparams=projparams, **kwargs)
self._proj4_string = None # Cache for proj4 string

def __repr__(self) -> str:
"""
Returns a string representation of the CRS object.
If the CRS has an EPSG code, it returns the EPSG string (e.g., "EPSG:4326").
Otherwise, it returns the proj4 string representation.
Returns:
str: The string representation of the CRS object.
"""
epsg_string = self.epsg_string

if epsg_string is None:
Expand All @@ -20,52 +52,41 @@ def __repr__(self) -> str:
return epsg_string

def __eq__(self, other: Union[CRS, str]) -> bool:
if self.to_epsg() is not None and other.to_epsg() is not None:
return self.to_epsg() == other.to_epsg()

return super(CRS, self).__eq__(other=other)

@property
def _crs(self):
"""
Retrieve the Cython based _CRS object for this thread.
"""
if not hasattr(self, "_local") or self._local is None:
from pyproj.crs.crs import CRSLocal
self._local = CRSLocal()
Checks if two CRS objects are equal.
if self._local.crs is None:
self._local.crs = _CRS(self.srs)
return self._local.crs
If both CRS objects have EPSG codes, it compares the EPSG codes.
Otherwise, it uses the default equality check from pyproj.CRS.
# factories
Args:
other (Union[CRS, str]): The other CRS object or string representation to compare.
@classmethod
def center_aeqd(cls, center_coord: Point) -> CRS:
Returns:
bool: True if the CRS objects are equal, False otherwise.
"""
Generate Azimuthal Equal Area CRS centered at given lat/lon.
:param center_coord: shapely.geometry.Point object containing latitute and longitude point of center of CRS
:return: pyproj.CRS object of centered CRS
"""
return CRS(f"+proj=aeqd +lat_0={center_coord.y} +lon_0={center_coord.x}")

@classmethod
def local_UTM_proj4(cls, point_latlon: Union[Point, str]) -> CRS:
if isinstance(point_latlon, str):
point_latlon = shapely.wkt.loads(point_latlon)
if isinstance(other, CRS) and self.to_epsg() is not None and other.to_epsg() is not None:
return self.to_epsg() == other.to_epsg()

lat = point_latlon.y
lon = point_latlon.x
UTM_zone = (math.floor((lon + 180) / 6) % 60) + 1
UTM_proj4 = CRS(
f"+proj=utm +zone={UTM_zone} {'+south ' if lat < 0 else ''}+ellps=WGS84 +datum=WGS84 +units=m +no_defs")
return super(CRS, self).__eq__(other=other)

return UTM_proj4
@property
def _crs(self) -> Any:
"""
Retrieve the underlying Cython based _CRS object. This is used internally by pyproj
for performance optimization.
# properties
Note: The implementation might change in future pyproj versions.
"""
return self._crs # Directly access the _crs attribute

@property
def epsg_string(self) -> Optional[str]:
"""
Returns the EPSG string representation of the CRS if it has an EPSG code.
Returns:
Optional[str]: The EPSG string (e.g., "EPSG:4326") or None if no EPSG code is found.
"""
epsg_code = self.to_epsg()

if epsg_code is None:
Expand All @@ -77,17 +98,33 @@ def epsg_string(self) -> Optional[str]:

@property
def epsg_url(self) -> Optional[str]:
"""
Returns the URL of the EPSG registry entry for the CRS if it has an EPSG code.
Returns:
Optional[str]: The EPSG URL or None if no EPSG code is found.
"""
epsg_code = self.to_epsg()

if epsg_code is None:
return None

epsg_url = f"http://www.opengis.net/def/crs/EPSG/9.9.1/{epsg_code}"
# You might need to verify this URL
epsg_url = f"https://epsg.org/{epsg_code}"

return epsg_url

@property
def coverage(self) -> OrderedDict:
"""
Returns an OrderedDict representing the coverage of the CRS.
This includes the coordinate system type (GeographicCRS or ProjectedCRS) and
the EPSG URL if available.
Returns:
OrderedDict: The coverage information.
"""
coverage = OrderedDict()
coverage["coordinates"] = ["x", "y"]
system = OrderedDict()
Expand All @@ -102,22 +139,40 @@ def coverage(self) -> OrderedDict:
if epsg_url is not None:
system["id"] = epsg_url

try:
area_of_use = self.area_of_use
coverage["area_of_use"] = {
"name": area_of_use.name,
"bounds": area_of_use.bounds,
}
except Exception: # Catch potential exceptions from pyproj
pass

coverage["system"] = system

return coverage

def to_pyproj(self) -> pyproj.CRS:
return pyproj.CRS(self.to_wkt())
"""
Converts the CRS to a pyproj.CRS object.
@property
def pyproj(self) -> pyproj.CRS:
return self.to_pyproj()
Returns:
pyproj.CRS: The pyproj.CRS object.
"""
return pyproj.CRS(self.to_wkt())

@property
def proj4(self) -> str:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
return self.to_proj4()
"""
Returns the proj4 string representation of the CRS.
Returns:
str: The proj4 string.
"""
if self._proj4_string is None:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
self._proj4_string = self.to_proj4()
return self._proj4_string

WGS84 = CRS("EPSG:4326")
WGS84 = CRS("EPSG:4326")
Loading

0 comments on commit 0bf525b

Please sign in to comment.