2024-06-29 23:44:03 +08:00

764 lines
27 KiB
Python

from org.meteoinfo.projection import ProjectionInfo
from org.meteoinfo.projection.info import LongLat, Albers, LambertConformalConic, LambertEqualAreaConic, \
GeostationarySatellite, OrthographicAzimuthal, StereographicAzimuthal
from org.meteoinfo.projection.info import TransverseMercator as JTransverseMercator
from org.meteoinfo.projection.info import Mercator as JMercator
from org.meteoinfo.projection.info import AzimuthEquidistant as JAzimuthEquidistant
from org.meteoinfo.projection.info import EquidistantConic as JEquidistantConic
from org.meteoinfo.projection.info import Hammer as JHammer
from org.meteoinfo.projection.info import LambertAzimuthalEqualArea as JLambertAzimuthalEqualArea
from org.meteoinfo.projection.info import Mollweide as JMollweide
from org.meteoinfo.projection.info import Robinson as JRobinson
from org.meteoinfo.projection.info import Sinusoidal as JSinusoidal
from org.meteoinfo.projection.info import Wagner3 as JWagner3
from org.meteoinfo.projection.info import Airy as JAiry
from org.meteoinfo.projection.info import Aitoff as JAitoff
from org.meteoinfo.projection.info import August as JAugust
from org.locationtech.proj4j import CRSFactory
__all__ = ['AlbersEqualArea','Airy','Aitoff','August','AzimuthalEquidistant','EquidistantConic',
'Geostationary','Hammer','LambertAzimuthalEqualArea','LambertConformal','LambertEqualArea',
'Mercator','Mollweide','NorthPolarStereo','Orthographic',
'PlateCarree','Robinson','Sinusoidal','SouthPolarStereo','Stereographic',
'TransverseMercator','Wagner3']
crs_factory = CRSFactory()
class PlateCarree(LongLat):
def __init__(self, central_longitude=0.0):
proj4_params = ['+proj=longlat', '+lon_0=' + str(central_longitude)]
crs = crs_factory.createFromParameters('custom', proj4_params)
LongLat.__init__(self, crs)
class TransverseMercator(JTransverseMercator):
"""
A Transverse Mercator projection.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0, scale_factor=1.0):
"""
Parameters
----------
central_longitude: optional
The true longitude of the central meridian in degrees.
Defaults to 0.
central_latitude: optional
The true latitude of the planar origin in degrees. Defaults to 0.
false_easting: optional
X offset from the planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from the planar origin in metres. Defaults to 0.
scale_factor: optional
Scale factor at the central meridian. Defaults to 1.
"""
proj4_params = ['+proj=tmerc',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing),
'+k=' + str(scale_factor)]
crs = crs_factory.createFromParameters('custom', proj4_params)
JTransverseMercator.__init__(self, crs)
class Mercator(JMercator):
"""
A Transverse Mercator projection.
"""
def __init__(self, central_longitude=0.0, latitude_true_scale=None,
false_easting=0.0, false_northing=0.0, scale_factor=1.0):
"""
Parameters
----------
central_longitude: optional
The true longitude of the central meridian in degrees.
Defaults to 0.
latitude_true_scale: optional
The latitude where the scale is 1. Defaults to 0 degrees.
false_easting: optional
X offset from the planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from the planar origin in metres. Defaults to 0.
scale_factor: optional
Scale factor at the central meridian. Defaults to 1.
"""
proj4_params = ['+proj=merc',
'+lon_0=' + str(central_longitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
# If it's None, we don't pass it to Proj4, in which case its default
# of 0.0 will be used.
if latitude_true_scale is not None:
proj4_params.append('+lat_ts=' + str(latitude_true_scale))
if scale_factor is not None:
if latitude_true_scale is not None:
raise ValueError('It does not make sense to provide both '
'"scale_factor" and "latitude_true_scale". ')
else:
proj4_params.append('+k_0=' + str(scale_factor))
crs = crs_factory.createFromParameters('custom', proj4_params)
JMercator.__init__(self, crs)
class AlbersEqualArea(Albers):
"""
An Albers Equal Area projection.
This projection is conic and equal-area.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0,
standard_parallels=(20.0, 50.0)):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
central_latitude: optional
The central latitude. Defaults to 0.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
standard_parallels: optional
The one or two latitudes of correct scale. Defaults to (20, 50).
"""
proj4_params = ['+proj=' + 'aea',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
if standard_parallels is not None:
try:
proj4_params.append('+lat_1=' + str(standard_parallels[0]))
try:
proj4_params.append('+lat_2=' + str(standard_parallels[1]))
except IndexError:
pass
except TypeError:
proj4_params.append('+lat_1=' + str(standard_parallels))
crs = crs_factory.createFromParameters('custom', proj4_params)
Albers.__init__(self, crs)
class AzimuthalEquidistant(JAzimuthEquidistant):
"""
An Azimuthal Equidistant projection
This projection provides accurate angles about and distances through the
central position. Other angles, distances, or areas may be distorted.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0):
"""
Parameters
----------
central_longitude: optional
The true longitude of the central meridian in degrees.
Defaults to 0.
central_latitude: optional
The true latitude of the planar origin in degrees.
Defaults to 0.
false_easting: optional
X offset from the planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from the planar origin in metres. Defaults to 0.
"""
proj4_params = ['+proj=' + 'aeqd',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
crs = crs_factory.createFromParameters('custom', proj4_params)
JAzimuthEquidistant.__init__(self, crs)
class EquidistantConic(JEquidistantConic):
"""
An Equidistant Conic projection.
This projection is conic and equidistant, and the scale is true along all
meridians and along one or two specified standard parallels.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0,
standard_parallels=(20.0, 50.0)):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
central_latitude: optional
The true latitude of the planar origin in degrees. Defaults to 0.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
standard_parallels: optional
The one or two latitudes of correct scale. Defaults to (20, 50).
"""
proj4_params = ['+proj=' + 'eqdc',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
if standard_parallels is not None:
try:
proj4_params.append('+lat_1=', str(standard_parallels[0]))
try:
proj4_params.append('+lat_2=' + str(standard_parallels[1]))
except IndexError:
pass
except TypeError:
proj4_params.append('+lat_1=' + str(standard_parallels))
crs = crs_factory.createFromParameters('custom', proj4_params)
JEquidistantConic.__init__(self, crs)
class Geostationary(GeostationarySatellite):
"""
A view appropriate for satellites in Geostationary Earth orbit.
Perspective view looking directly down from above a point on the equator.
In this projection, the projected coordinates are scanning angles measured
from the satellite looking directly downward, multiplied by the height of
the satellite.
"""
def __init__(self, central_longitude=0.0, satellite_height=35785831,
false_easting=0, false_northing=0, semi_major_axis=None,
semi_minor_axis=None):
"""
Parameters
----------
central_longitude: float, optional
The central longitude. Defaults to 0.
satellite_height: float, optional
The height of the satellite. Defaults to 35785831 metres
(true geostationary orbit).
false_easting:
X offset from planar origin in metres. Defaults to 0.
false_northing:
Y offset from planar origin in metres. Defaults to 0.
semi_major_axis:
Semi-major axis of the ellipsoid, `a`.
semi_minor_axis:
Semi-minor axis of the ellipsoid, `b`.
"""
proj4_params = ['+proj=' + 'geos',
'+lon_0=' + str(central_longitude),
'+h=' + str(satellite_height),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
if semi_major_axis is not None:
proj4_params.append('+a=' + str(semi_major_axis))
if semi_minor_axis is not None:
proj4_params.append('+b=' + str(semi_minor_axis))
crs = crs_factory.createFromParameters('custom', proj4_params)
GeostationarySatellite.__init__(self, crs)
class Hammer(JHammer):
"""
A Hammer projection.
This projection is a modified `.LambertAzimuthalEqualArea` projection,
similar to `.Aitoff`, and intended to reduce distortion in the outer
meridians compared to `.Mollweide`. There are no standard lines and only
the central point is free of distortion.
"""
_handles_ellipses = False
def __init__(self, central_longitude=0, false_easting=None,
false_northing=None):
"""
Parameters
----------
central_longitude: float, optional
The central longitude. Defaults to 0.
false_easting: float, optional
X offset from planar origin in metres. Defaults to 0.
false_northing: float, optional
Y offset from planar origin in metres. Defaults to 0.
.. note::
This projection does not handle elliptical globes.
"""
proj4_params = ['+proj=' + 'hammer',
'+lon_0=' + str(central_longitude)]
if false_easting is not None:
proj4_params.append('+x_0=' + str(false_easting))
if false_northing is not None:
proj4_params.append('+y_0=' + str(false_northing))
crs = crs_factory.createFromParameters('custom', proj4_params)
JHammer.__init__(self, crs)
class LambertConformal(LambertConformalConic):
"""
A Lambert Conformal conic projection.
"""
def __init__(self, central_longitude=-96.0, central_latitude=39.0,
false_easting=0.0, false_northing=0.0,
standard_parallels=(33, 45), cutoff=-30):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to -96.
central_latitude: optional
The central latitude. Defaults to 39.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
standard_parallels: optional
Standard parallel latitude(s). Defaults to (33, 45).
cutoff: optional
Latitude of map cutoff.
The map extends to infinity opposite the central pole
so we must cut off the map drawing before then.
A value of 0 will draw half the globe. Defaults to -30.
"""
proj4_params = ['+proj=' + 'lcc',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
n_parallels = len(standard_parallels)
if not 1 <= n_parallels <= 2:
raise ValueError('1 or 2 standard parallels must be specified. '
'Got {} ({})'.format(n_parallels, standard_parallels))
proj4_params.append('+lat_1=' + str(standard_parallels[0]))
if n_parallels == 2:
proj4_params.append('+lat_2=' + str(standard_parallels[1]))
crs = crs_factory.createFromParameters('custom', proj4_params)
LambertConformalConic.__init__(self, crs, cutoff)
class LambertAzimuthalEqualArea(JLambertAzimuthalEqualArea):
"""
A Lambert Azimuthal Equal-Area projection.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
central_latitude: optional
The central latitude. Defaults to 0.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
"""
proj4_params = ['+proj=' + 'laea',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
crs = crs_factory.createFromParameters('custom', proj4_params)
JLambertAzimuthalEqualArea.__init__(self, crs)
class LambertEqualArea(LambertEqualAreaConic):
"""
A Lambert Equal-Area Conic projection.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
central_latitude: optional
The central latitude. Defaults to 0.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
"""
proj4_params = ['+proj=' + 'leac',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
crs = crs_factory.createFromParameters('custom', proj4_params)
LambertEqualAreaConic.__init__(self, crs)
class Mollweide(JMollweide):
"""
A Mollweide projection.
This projection is pseudocylindrical, and equal area. Parallels are
unequally-spaced straight lines, while meridians are elliptical arcs up to
semicircles on the edges. Poles are points.
It is commonly used for world maps, or interrupted with several central
meridians.
"""
_handles_ellipses = False
def __init__(self, central_longitude=0,
false_easting=None, false_northing=None):
"""
Parameters
----------
central_longitude: float, optional
The central longitude. Defaults to 0.
false_easting: float, optional
X offset from planar origin in metres. Defaults to 0.
false_northing: float, optional
Y offset from planar origin in metres. Defaults to 0.
.. note::
This projection does not handle elliptical globes.
"""
proj4_params = ['+proj=' + 'moll',
'+lon_0=' + str(central_longitude)]
if false_easting is not None:
proj4_params.append('+x_0=' + str(false_easting))
if false_northing is not None:
proj4_params.append('+y_0=' + str(false_northing))
crs = crs_factory.createFromParameters('custom', proj4_params)
JMollweide.__init__(self, crs)
class Orthographic(OrthographicAzimuthal):
"""
Am orthographic azimuthal projection.
"""
_handles_ellipses = False
def __init__(self, central_longitude=0.0, central_latitude=0.0):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
central_latitude: optional
The central latitude. Defaults to 0.
"""
proj4_params = ['+proj=' + 'ortho',
'+lon_0=' + str(central_longitude),
'+lat_0=' + str(central_latitude)]
crs = crs_factory.createFromParameters('custom', proj4_params)
OrthographicAzimuthal.__init__(self, crs)
class Robinson(JRobinson):
"""
A Robinson projection.
This projection is pseudocylindrical, and a compromise that is neither
equal-area nor conformal. Parallels are unequally-spaced straight lines,
and meridians are curved lines of no particular form.
It is commonly used for "visually-appealing" world maps.
"""
_handles_ellipses = False
def __init__(self, central_longitude=0,
false_easting=None, false_northing=None):
"""
Parameters
----------
central_longitude: float, optional
The central longitude. Defaults to 0.
false_easting: float, optional
X offset from planar origin in metres. Defaults to 0.
false_northing: float, optional
Y offset from planar origin in metres. Defaults to 0.
.. note::
This projection does not handle elliptical globes.
"""
proj4_params = ['+proj=' + 'robin',
'+lon_0=' + str(central_longitude)]
if false_easting is not None:
proj4_params.append('+x_0=' + str(false_easting))
if false_northing is not None:
proj4_params.append('+y_0=' + str(false_northing))
crs = crs_factory.createFromParameters('custom', proj4_params)
JRobinson.__init__(self, crs)
class Sinusoidal(JSinusoidal):
"""
A Sinusoidal projection.
This projection is equal-area.
"""
def __init__(self, central_longitude=0.0, false_easting=0.0,
false_northing=0.0):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
"""
proj4_params = ['+proj=' + 'sinu',
'+lon_0=' + str(central_longitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
crs = crs_factory.createFromParameters('custom', proj4_params)
JSinusoidal.__init__(self, crs)
class Stereographic(StereographicAzimuthal):
"""
A stereographic azimuthal projection.
"""
_wrappable = True
def __init__(self, central_latitude=0.0, central_longitude=0.0,
false_easting=0.0, false_northing=0.0,
true_scale_latitude=None, scale_factor=None, cutoff=0):
proj4_params = ['+proj=' + 'stere',
'+lat_0=' + str(central_latitude),
'+lon_0=' + str(central_longitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
if true_scale_latitude is not None:
if central_latitude not in (-90., 90.):
warnings.warn('"true_scale_latitude" parameter is only used '
'for polar stereographic projections. Consider '
'the use of "scale_factor" instead.',
stacklevel=2)
proj4_params.append('+lat_ts=' + str(true_scale_latitude))
if scale_factor is not None:
if true_scale_latitude is not None:
raise ValueError('It does not make sense to provide both '
'"scale_factor" and "true_scale_latitude". '
'Ignoring "scale_factor".')
else:
proj4_params.append('+k_0=' + str(scale_factor))
crs = crs_factory.createFromParameters('custom', proj4_params)
StereographicAzimuthal.__init__(self, crs, cutoff)
class NorthPolarStereo(Stereographic):
def __init__(self, central_longitude=0.0, true_scale_latitude=None, cutoff=0):
Stereographic.__init__(self,
central_latitude=90,
central_longitude=central_longitude,
true_scale_latitude=true_scale_latitude, # None is +90
cutoff=cutoff)
class SouthPolarStereo(Stereographic):
def __init__(self, central_longitude=0.0, true_scale_latitude=None, cutoff=0):
Stereographic.__init__(self,
central_latitude=-90,
central_longitude=central_longitude,
true_scale_latitude=true_scale_latitude, # None is -90
cutoff=cutoff)
class Wagner3(JWagner3):
"""
A Wagner 3 projection.
"""
def __init__(self, central_longitude=0.0, latitude_true_scale=None,
false_easting=0.0, false_northing=0.0):
"""
Parameters
----------
central_longitude: optional
The true longitude of the central meridian in degrees.
Defaults to 0.
latitude_true_scale: optional
The latitude where the scale is 1. Defaults to 0 degrees.
false_easting: optional
X offset from the planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from the planar origin in metres. Defaults to 0.
"""
proj4_params = ['+proj=wag3',
'+lon_0=' + str(central_longitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
# If it's None, we don't pass it to Proj4, in which case its default
# of 0.0 will be used.
if latitude_true_scale is not None:
proj4_params.append('+lat_ts=' + str(latitude_true_scale))
crs = crs_factory.createFromParameters('custom', proj4_params)
JWagner3.__init__(self, crs)
class Airy(JAiry):
"""
An Airy projection.
"""
def __init__(self, central_longitude=0.0, central_latitude=0.0,
false_easting=0.0, false_northing=0.0):
"""
Parameters
----------
central_longitude: optional
The central longitude. Defaults to 0.
central_latitude: optional
The central latitude. Defaults to 0.
false_easting: optional
X offset from planar origin in metres. Defaults to 0.
false_northing: optional
Y offset from planar origin in metres. Defaults to 0.
"""
proj4_params = ['+proj=' + 'airy',
'+lat_0=' + str(central_latitude),
'+lon_0=' + str(central_longitude),
'+x_0=' + str(false_easting),
'+y_0=' + str(false_northing)]
crs = crs_factory.createFromParameters('custom', proj4_params)
JAiry.__init__(self, crs)
class Aitoff(JAitoff):
"""
An Aitoff projection.
This projection is a modified azimuthal equidistant projection, balancing
shape and scale distortion. There are no standard lines and only the
central point is free of distortion.
"""
_handles_ellipses = False
def __init__(self, central_longitude=0, false_easting=None,
false_northing=None):
"""
Parameters
----------
central_longitude: float, optional
The central longitude. Defaults to 0.
false_easting: float, optional
X offset from planar origin in metres. Defaults to 0.
false_northing: float, optional
Y offset from planar origin in metres. Defaults to 0.
.. note::
This projection does not handle elliptical globes.
"""
proj4_params = ['+proj=' + 'aitoff',
'+lon_0=' + str(central_longitude)]
if false_easting is not None:
proj4_params.append('+x_0=' + str(false_easting))
if false_northing is not None:
proj4_params.append('+y_0=' + str(false_northing))
crs = crs_factory.createFromParameters('custom', proj4_params)
JAitoff.__init__(self, crs)
class August(JAugust):
"""
An August projection.
"""
_handles_ellipses = False
def __init__(self, central_longitude=0, false_easting=None,
false_northing=None):
"""
Parameters
----------
central_longitude: float, optional
The central longitude. Defaults to 0.
false_easting: float, optional
X offset from planar origin in metres. Defaults to 0.
false_northing: float, optional
Y offset from planar origin in metres. Defaults to 0.
.. note::
This projection does not handle elliptical globes.
"""
proj4_params = ['+proj=' + 'august',
'+lon_0=' + str(central_longitude)]
if false_easting is not None:
proj4_params.append('+x_0=' + str(false_easting))
if false_northing is not None:
proj4_params.append('+y_0=' + str(false_northing))
crs = crs_factory.createFromParameters('custom', proj4_params)
JAugust.__init__(self, crs)