From 0c969bba7f3b8a4c9f8c8c1b4006d4c72642e8db Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Mon, 18 Nov 2019 06:30:47 +1100 Subject: [PATCH 01/30] Initial commit for pygmt/projection.py; Contains a generic design/layout for a sample of the projections supported by GMT. --- pygmt/projection.py | 315 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 pygmt/projection.py diff --git a/pygmt/projection.py b/pygmt/projection.py new file mode 100644 index 00000000000..85e269c5db2 --- /dev/null +++ b/pygmt/projection.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python + +""" +Contains the projections supported by GMT, and the necessary mechanisms +to create a projection and output a valid GMT projection string. + +>>> from pygmt import projection +>>> proj = projection.LambertAzimuthalEqualArea(lon0=30, lat0=-20, horizon=60, width="8i") +>>> proj +LambertAzimuthalEqualArea(lon0=30, lat0=-20, horizon=60, width='8i') +>>> print(proj) +A30/-20/60/8i +""" + +from enum import Enum +import attr + + +class Supported(Enum): + + """ + The supported projections and their GMT code. + """ + + UNDEFINED = "" + LAMBERT_AZIMUTH_EQUAL_AREA = "A" # DONE + ALBERS_CONIC_EQUAL_AREA = "B" # DONE + CASSINI_CYLINDRICAL = "C" # DONE + CYLINDRICAL_STEROGRAPHIC = "JCyl_stere/" # includes `/` according to https://docs.generic-mapping-tools.org/latest/proj_codes.html # DONE + EQUIDISTANT_CONIC = "JD" # DONE + AZIMUTHAL_EQUIDISTANT = "E" # DONE + AZIMUTHAL_GNOMIC = "F" # DONE + AZIMUTHAL_ORTHOGRAPHIC = "G" # DONE + GENERAL_PERSPECTIVE = "G" # DONE + HAMMER_EQUAL_AREA = "H" + SINUSOIDAL_EQUAL_AREA = "I" + MILLER_CYLINDRICAL = "J" + ECKERT_IV_EQUAL_AREA = "Kf" + ECKERT_VI_EQUAL_AREA = "Ks" + LAMBERT_CONIC_CONFORMAL = "L" + MERCATOR_CYLINDRICAL = "M" # DONE + ROBINSON = "N" + OBLIQUE_MERCATOR_1 = "Oa" + OBLIQUE_MERCATOR_2 = "Ob" + OBLIQUE_MERCATOR_3 = "Oc" + POLAR = "P" + POLYCONIC = "Poly" + EQUIDISTANT_CYLINDRICAL = "Q" + WINKEL_TRIPEL = "R" + GENERAL_STEREOGRAPHIC = "S" # DONE + TRANSVERSE_MERCATOR = "T" + UNIVERSAL_TRANSVERSE_MERCATOR = "U" + VAN_DER_GRINTEN = "V" + MOLLWEIDE = "W" + LINEAR = "X" + CYLINDRICAL_EQUAL_AREA = "Y" # DONE + + +@attr.s() +class _Projection: + + """ + Base class for all projections. + """ + + # private; we don't want the user to care or know about + _fmt: str = attr.ib(init=False, repr=False, default="{_code}") + _code: str = attr.ib(init=False, repr=False, + default=Supported.UNDEFINED.value) + + def __str__(self): + exclude = attr.fields(self.__class__)._fmt + kwargs = attr.asdict(self, filter=attr.filters.exclude(exclude)) + return self._fmt.format(**kwargs) + + +@attr.s(kw_only=True) +class _Azimuthal(_Projection): + + """ + Base class for azimuthal projections. + """ + + lon0: float = attr.ib() + lat0: float = attr.ib() + horizon: float = attr.ib(default=90) + width: str = attr.ib() + + # private; we don't want the user to care or know about + _fmt: str = attr.ib(init=False, repr=False, + default="{_code}{lon0}/{lat0}/{horizon}/{width}") + _code: str = attr.ib(init=False, repr=False, + default=Supported.UNDEFINED.value) + + @horizon.validator + def check_horizon(self, attribute, value): + """ + Validate the horizon attribute. + """ + if value > 180: + raise ValueError("horizon must be less than or equal to 180") + + +@attr.s(kw_only=True) +class _Cylindrical(_Projection): + + """ + Base class for cylindrical projections. + """ + + lon0: float = attr.ib() + lat0: float = attr.ib() + width: str = attr.ib() + + # private; we don't want the user to care or know about + _fmt: str = attr.ib(init=False, repr=False, + default="{_code}{lon0}/{lat0}/{wdith}") + _code: str = attr.ib(init=False, repr=False, + default=Supported.UNDEFINED.value) + +@attr.s(kw_only=True) +class _Conic: + + """ + Base class for conic projections. + """ + + lon0: float = attr.ib() + lat0: float = attr.ib() + lat1: float = attr.ib() + lat2: float = attr.ib() + width: float = attr.ib() + + # private; we don't want the user to care or know about + _fmt: str = attr.ib(init=False, repr=False, + default="{_code}{lon0}/{lat0}/{lat1}/{lat2}/{width}") + + +@attr.s(frozen=True) +class LambertAzimuthalEqualArea(_Azimuthal): + + """ + Definition for the lambert azimuthal equal area projection. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.LAMBERT_AZIMUTH_EQUAL_AREA.value) + + +@attr.s(frozen=True) +class AzimuthalEquidistant(_Azimuthal): + + """ + Definition for the azimuthal equidistant projection. + """ + + horizon: float = attr.ib(default=180, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.AZIMUTHAL_EQUIDISTANT.value) + + +@attr.s(frozen=True) +class AzimuthalGnomic(_Azimuthal): + + """ + Definition for the azimuthal gnomic projection. + """ + + horizon: float = attr.ib(default=60, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.AZIMUTHAL_EQUIDISTANT.value) + + @horizon.validator + def check_horizon(self, attribute, value): + """ + Validate the horizon attribute. + """ + if value >= 90: + raise ValueError("horizon must be less than 90") + + +@attr.s(frozen=True) +class AzimuthalOrthographic(_Azimuthal): + + """ + Definition for the azimuthal orthographic projection. + """ + + horizon: float = attr.ib(default=60, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.AZIMUTHAL_EQUIDISTANT.value) + + @horizon.validator + def check_horizon(self, attribute, value): + """ + Validate the horizon attribute. + """ + if value > 90: + raise ValueError("horizon must be less than or equal to 90") + + +@attr.s(frozen=True, kw_only=True) +class GeneralPerspective(_Projection): + + """ + Definition for the azimuthal general perspective projection. + """ + + lon0: float = attr.ib() + lat0: float = attr.ib() + altitude: float = attr.ib() + azimuth: float = attr.ib() + tilt: float = attr.ib() + twist: float = attr.ib() + Width: float = attr.ib() + Height: float = attr.ib() + width: float = attr.ib() + + # private; we don't want the user to care or know about + _fmt: str = attr.ib(init=False, repr=False, + default=("{_code}{lon0}/{lat0}/{altitude}/{azimuth}/" + "{tilt}/{twist}/{Width}/{Height}/{width}")) + _code: str = attr.ib(init=False, repr=False, + default=Supported.GENERAL_PERSPECTIVE.value) + + +@attr.s(frozen=True) +class GeneralSterographic(_Azimuthal): + + """ + Definition for the azimuthal general sterographic projection. + """ + + horizon: float = attr.ib(default=90, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.GENERAL_STEREOGRAPHIC.value) + + @horizon.validator + def check_horizon(self, attribute, value): + """ + Validate the horizon attribute. + """ + if value >= 180: + raise ValueError("horizon must be less than 180") + + +@attr.s(frozen=True, kw_only=True) +class AlbersConicEqualArea(_Conic): + + """ + Definition for the albers conic equal area projection. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.ALBERS_CONIC_EQUAL_AREA.value) + + +@attr.s(frozen=True, kw_only=True) +class EquidistantConic(_Conic): + + """ + Definition for the equidistant conic projection. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.EQUIDISTANT_CONIC) + + +@attr.s(frozen=True) +class CassiniCylindrical(_Cylindrical): + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.CASSINI_CYLINDRICAL.value) + + +@attr.s(frozen=True) +class MercatorCylindrical(_Cylindrical): + + lon0: float = attr.ib(default=180, kw_only=True) + lat0: float = attr.ib(default=0, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.MERCATOR_CYLINDRICAL.value) + + +@attr.s(frozen=True) +class CylindricalStereographic(_Cylindrical): + + lon0: float = attr.ib(default=180, kw_only=True) + lat0: float = attr.ib(default=0, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.CYLINDRICAL_STEROGRAPHIC.value) + + +@attr.s(frozen=True) +class CylindricalEqualArea(_Cylindrical): + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, + default=Supported.CYLINDRICAL_EQUAL_AREA.value) From 16819cce8440483204a5f175b34f59734995ccff Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Mon, 18 Nov 2019 15:58:55 +1100 Subject: [PATCH 02/30] Updated docstrings for class projection definitions. --- pygmt/projection.py | 213 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 200 insertions(+), 13 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 85e269c5db2..ffbab954897 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -79,6 +79,17 @@ class _Azimuthal(_Projection): """ Base class for azimuthal projections. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + horizon : float + The max distance to the projection centre in degrees. Default is 90. + width : str + The figure width. For example ``8i`` is 8 inches. """ lon0: float = attr.ib() @@ -106,6 +117,15 @@ class _Cylindrical(_Projection): """ Base class for cylindrical projections. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + width : str + The figure width. For example ``8i`` is 8 inches. """ lon0: float = attr.ib() @@ -118,11 +138,25 @@ class _Cylindrical(_Projection): _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) + @attr.s(kw_only=True) class _Conic: """ Base class for conic projections. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + lat1 : float + The first standard parallel. + lat2 : float + The second standard parallel. + width : str + The figure width. For example ``8i`` is 8 inches. """ lon0: float = attr.ib() @@ -140,7 +174,18 @@ class _Conic: class LambertAzimuthalEqualArea(_Azimuthal): """ - Definition for the lambert azimuthal equal area projection. + Class definition for the lambert azimuthal equal area projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + horizon : float + The max distance to the projection centre in degrees. Default is 90. + width : str + The figure width. For example ``8i`` is 8 inches. """ # private; we don't want the user to care or know about @@ -152,7 +197,18 @@ class LambertAzimuthalEqualArea(_Azimuthal): class AzimuthalEquidistant(_Azimuthal): """ - Definition for the azimuthal equidistant projection. + Class definition for the azimuthal equidistant projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + horizon : float + The max distance to the projection centre in degrees. Default is 180. + width : str + The figure width. For example ``8i`` is 8 inches. """ horizon: float = attr.ib(default=180, kw_only=True) @@ -166,14 +222,25 @@ class AzimuthalEquidistant(_Azimuthal): class AzimuthalGnomic(_Azimuthal): """ - Definition for the azimuthal gnomic projection. + Class definition for the azimuthal gnomic projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + horizon : float + The max distance to the projection centre in degrees. Default is 60. + width : str + The figure width. For example ``8i`` is 8 inches. """ horizon: float = attr.ib(default=60, kw_only=True) # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, - default=Supported.AZIMUTHAL_EQUIDISTANT.value) + default=Supported.AZIMUTHAL_GNOMIC.value) @horizon.validator def check_horizon(self, attribute, value): @@ -188,14 +255,25 @@ def check_horizon(self, attribute, value): class AzimuthalOrthographic(_Azimuthal): """ - Definition for the azimuthal orthographic projection. + Class definition for the azimuthal orthographic projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + horizon : float + The max distance to the projection centre in degrees. Default is 90. + width : str + The figure width. For example ``8i`` is 8 inches. """ - horizon: float = attr.ib(default=60, kw_only=True) + horizon: float = attr.ib(default=90) # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, - default=Supported.AZIMUTHAL_EQUIDISTANT.value) + default=Supported.AZIMUTHAL_ORTHOGRAPHIC.value) @horizon.validator def check_horizon(self, attribute, value): @@ -210,7 +288,28 @@ def check_horizon(self, attribute, value): class GeneralPerspective(_Projection): """ - Definition for the azimuthal general perspective projection. + Class definition for the azimuthal general perspective projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre (in degrees). + lat0 : float + The latitude of the projection centre (in degrees). + altitude : float + The height in km of the viewpoint above local sea level. + azimuth : float + The direction (in degrees) in which you are looking is specified, measured clockwise from north. + tilt : float + The viewing angle relative to zenith (in degrees). + twist : float + The clockwise rotation of the image (in degrees). + viewport_width : float + The width of the viewing angle (in degrees). + viewport_height : float + The height of the viewing angle (in degrees). + width : str + The figure width. For example ``8i`` is 8 inches. """ lon0: float = attr.ib() @@ -225,8 +324,7 @@ class GeneralPerspective(_Projection): # private; we don't want the user to care or know about _fmt: str = attr.ib(init=False, repr=False, - default=("{_code}{lon0}/{lat0}/{altitude}/{azimuth}/" - "{tilt}/{twist}/{Width}/{Height}/{width}")) + default="{_code}{lon0}/{lat0}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}") _code: str = attr.ib(init=False, repr=False, default=Supported.GENERAL_PERSPECTIVE.value) @@ -235,7 +333,18 @@ class GeneralPerspective(_Projection): class GeneralSterographic(_Azimuthal): """ - Definition for the azimuthal general sterographic projection. + Class definition for the azimuthal general sterographic projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + horizon : float + The max distance to the projection centre in degrees. Default is 90. + width : str + The figure width. For example ``8i`` is 8 inches. """ horizon: float = attr.ib(default=90, kw_only=True) @@ -257,7 +366,20 @@ def check_horizon(self, attribute, value): class AlbersConicEqualArea(_Conic): """ - Definition for the albers conic equal area projection. + Class definition for the albers conic equal area projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + lat1 : float + The first standard parallel. + lat2 : float + The second standard parallel. + width : str + The figure width. For example ``8i`` is 8 inches. """ # private; we don't want the user to care or know about @@ -269,7 +391,20 @@ class AlbersConicEqualArea(_Conic): class EquidistantConic(_Conic): """ - Definition for the equidistant conic projection. + Class definition for the equidistant conic projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + lat1 : float + The first standard parallel. + lat2 : float + The second standard parallel. + width : str + The figure width. For example ``8i`` is 8 inches. """ # private; we don't want the user to care or know about @@ -280,6 +415,19 @@ class EquidistantConic(_Conic): @attr.s(frozen=True) class CassiniCylindrical(_Cylindrical): + """ + Class definition for the cassini cylindrical projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + width : str + The figure width. For example ``8i`` is 8 inches. + """ + # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default=Supported.CASSINI_CYLINDRICAL.value) @@ -288,6 +436,19 @@ class CassiniCylindrical(_Cylindrical): @attr.s(frozen=True) class MercatorCylindrical(_Cylindrical): + """ + Class definition for the cassini cylindrical projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. Default is 180. + lat0 : float + The latitude of the projection centre. Default is 0. + width : str + The figure width. For example ``8i`` is 8 inches. + """ + lon0: float = attr.ib(default=180, kw_only=True) lat0: float = attr.ib(default=0, kw_only=True) @@ -299,6 +460,19 @@ class MercatorCylindrical(_Cylindrical): @attr.s(frozen=True) class CylindricalStereographic(_Cylindrical): + """ + Class definition for the cassini cylindrical projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. Default is 180. + lat0 : float + The latitude of the projection centre. Default is 0. + width : str + The figure width. For example ``8i`` is 8 inches. + """ + lon0: float = attr.ib(default=180, kw_only=True) lat0: float = attr.ib(default=0, kw_only=True) @@ -310,6 +484,19 @@ class CylindricalStereographic(_Cylindrical): @attr.s(frozen=True) class CylindricalEqualArea(_Cylindrical): + """ + Class definition for the cassini cylindrical projection. + + Parameters + ---------- + lon0 : float + The longitude of the projection centre. + lat0 : float + The latitude of the projection centre. + width : str + The figure width. For example ``8i`` is 8 inches. + """ + # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default=Supported.CYLINDRICAL_EQUAL_AREA.value) From 979d05710ff1922cd019409cdc4c83ab9db18ef3 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Mon, 18 Nov 2019 21:26:29 +1100 Subject: [PATCH 03/30] Initial run of Black to reformat code. --- pygmt/projection.py | 87 +++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index ffbab954897..b2b1c5c1c77 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -65,8 +65,7 @@ class _Projection: # private; we don't want the user to care or know about _fmt: str = attr.ib(init=False, repr=False, default="{_code}") - _code: str = attr.ib(init=False, repr=False, - default=Supported.UNDEFINED.value) + _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) def __str__(self): exclude = attr.fields(self.__class__)._fmt @@ -98,10 +97,10 @@ class _Azimuthal(_Projection): width: str = attr.ib() # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, - default="{_code}{lon0}/{lat0}/{horizon}/{width}") - _code: str = attr.ib(init=False, repr=False, - default=Supported.UNDEFINED.value) + _fmt: str = attr.ib( + init=False, repr=False, default="{_code}{lon0}/{lat0}/{horizon}/{width}" + ) + _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @horizon.validator def check_horizon(self, attribute, value): @@ -133,10 +132,8 @@ class _Cylindrical(_Projection): width: str = attr.ib() # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, - default="{_code}{lon0}/{lat0}/{wdith}") - _code: str = attr.ib(init=False, repr=False, - default=Supported.UNDEFINED.value) + _fmt: str = attr.ib(init=False, repr=False, default="{_code}{lon0}/{lat0}/{wdith}") + _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @attr.s(kw_only=True) @@ -166,8 +163,9 @@ class _Conic: width: float = attr.ib() # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, - default="{_code}{lon0}/{lat0}/{lat1}/{lat2}/{width}") + _fmt: str = attr.ib( + init=False, repr=False, default="{_code}{lon0}/{lat0}/{lat1}/{lat2}/{width}" + ) @attr.s(frozen=True) @@ -189,8 +187,9 @@ class LambertAzimuthalEqualArea(_Azimuthal): """ # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.LAMBERT_AZIMUTH_EQUAL_AREA.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.LAMBERT_AZIMUTH_EQUAL_AREA.value + ) @attr.s(frozen=True) @@ -214,8 +213,9 @@ class AzimuthalEquidistant(_Azimuthal): horizon: float = attr.ib(default=180, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.AZIMUTHAL_EQUIDISTANT.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.AZIMUTHAL_EQUIDISTANT.value + ) @attr.s(frozen=True) @@ -239,8 +239,9 @@ class AzimuthalGnomic(_Azimuthal): horizon: float = attr.ib(default=60, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.AZIMUTHAL_GNOMIC.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.AZIMUTHAL_GNOMIC.value + ) @horizon.validator def check_horizon(self, attribute, value): @@ -272,8 +273,9 @@ class AzimuthalOrthographic(_Azimuthal): horizon: float = attr.ib(default=90) # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.AZIMUTHAL_ORTHOGRAPHIC.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.AZIMUTHAL_ORTHOGRAPHIC.value + ) @horizon.validator def check_horizon(self, attribute, value): @@ -323,10 +325,14 @@ class GeneralPerspective(_Projection): width: float = attr.ib() # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, - default="{_code}{lon0}/{lat0}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}") - _code: str = attr.ib(init=False, repr=False, - default=Supported.GENERAL_PERSPECTIVE.value) + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{lon0}/{lat0}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}", + ) + _code: str = attr.ib( + init=False, repr=False, default=Supported.GENERAL_PERSPECTIVE.value + ) @attr.s(frozen=True) @@ -350,8 +356,9 @@ class GeneralSterographic(_Azimuthal): horizon: float = attr.ib(default=90, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.GENERAL_STEREOGRAPHIC.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.GENERAL_STEREOGRAPHIC.value + ) @horizon.validator def check_horizon(self, attribute, value): @@ -383,8 +390,9 @@ class AlbersConicEqualArea(_Conic): """ # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.ALBERS_CONIC_EQUAL_AREA.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.ALBERS_CONIC_EQUAL_AREA.value + ) @attr.s(frozen=True, kw_only=True) @@ -408,8 +416,7 @@ class EquidistantConic(_Conic): """ # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.EQUIDISTANT_CONIC) + _code: str = attr.ib(init=False, repr=False, default=Supported.EQUIDISTANT_CONIC) @attr.s(frozen=True) @@ -429,8 +436,9 @@ class CassiniCylindrical(_Cylindrical): """ # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.CASSINI_CYLINDRICAL.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.CASSINI_CYLINDRICAL.value + ) @attr.s(frozen=True) @@ -453,8 +461,9 @@ class MercatorCylindrical(_Cylindrical): lat0: float = attr.ib(default=0, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.MERCATOR_CYLINDRICAL.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.MERCATOR_CYLINDRICAL.value + ) @attr.s(frozen=True) @@ -477,8 +486,9 @@ class CylindricalStereographic(_Cylindrical): lat0: float = attr.ib(default=0, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.CYLINDRICAL_STEROGRAPHIC.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.CYLINDRICAL_STEROGRAPHIC.value + ) @attr.s(frozen=True) @@ -498,5 +508,6 @@ class CylindricalEqualArea(_Cylindrical): """ # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, - default=Supported.CYLINDRICAL_EQUAL_AREA.value) + _code: str = attr.ib( + init=False, repr=False, default=Supported.CYLINDRICAL_EQUAL_AREA.value + ) From c1161a3c99610c174c52e75265b815c42115f7b4 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 19 Nov 2019 20:48:04 +1100 Subject: [PATCH 04/30] Renamed lon0 to central_longitude and lat0 to central_latitude. --- pygmt/projection.py | 96 ++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index b2b1c5c1c77..249542d8945 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -5,9 +5,9 @@ to create a projection and output a valid GMT projection string. >>> from pygmt import projection ->>> proj = projection.LambertAzimuthalEqualArea(lon0=30, lat0=-20, horizon=60, width="8i") +>>> proj = projection.LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width="8i") >>> proj -LambertAzimuthalEqualArea(lon0=30, lat0=-20, horizon=60, width='8i') +LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width='8i') >>> print(proj) A30/-20/60/8i """ @@ -81,9 +81,9 @@ class _Azimuthal(_Projection): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. @@ -91,14 +91,14 @@ class _Azimuthal(_Projection): The figure width. For example ``8i`` is 8 inches. """ - lon0: float = attr.ib() - lat0: float = attr.ib() + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() horizon: float = attr.ib(default=90) width: str = attr.ib() # private; we don't want the user to care or know about _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{lon0}/{lat0}/{horizon}/{width}" + init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}" ) _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @@ -119,20 +119,20 @@ class _Cylindrical(_Projection): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. width : str The figure width. For example ``8i`` is 8 inches. """ - lon0: float = attr.ib() - lat0: float = attr.ib() + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() width: str = attr.ib() # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, default="{_code}{lon0}/{lat0}/{wdith}") + _fmt: str = attr.ib(init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{wdith}") _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @@ -144,9 +144,9 @@ class _Conic: Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. lat1 : float The first standard parallel. @@ -156,15 +156,15 @@ class _Conic: The figure width. For example ``8i`` is 8 inches. """ - lon0: float = attr.ib() - lat0: float = attr.ib() + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() lat1: float = attr.ib() lat2: float = attr.ib() width: float = attr.ib() # private; we don't want the user to care or know about _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{lon0}/{lat0}/{lat1}/{lat2}/{width}" + init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{lat1}/{lat2}/{width}" ) @@ -176,9 +176,9 @@ class LambertAzimuthalEqualArea(_Azimuthal): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. @@ -200,9 +200,9 @@ class AzimuthalEquidistant(_Azimuthal): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 180. @@ -226,9 +226,9 @@ class AzimuthalGnomic(_Azimuthal): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 60. @@ -260,9 +260,9 @@ class AzimuthalOrthographic(_Azimuthal): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. @@ -294,9 +294,9 @@ class GeneralPerspective(_Projection): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre (in degrees). - lat0 : float + central_latitude : float The latitude of the projection centre (in degrees). altitude : float The height in km of the viewpoint above local sea level. @@ -314,8 +314,8 @@ class GeneralPerspective(_Projection): The figure width. For example ``8i`` is 8 inches. """ - lon0: float = attr.ib() - lat0: float = attr.ib() + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() altitude: float = attr.ib() azimuth: float = attr.ib() tilt: float = attr.ib() @@ -328,7 +328,7 @@ class GeneralPerspective(_Projection): _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{lon0}/{lat0}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}", + default="{_code}{central_longitude}/{central_latitude}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}", ) _code: str = attr.ib( init=False, repr=False, default=Supported.GENERAL_PERSPECTIVE.value @@ -343,9 +343,9 @@ class GeneralSterographic(_Azimuthal): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. @@ -377,9 +377,9 @@ class AlbersConicEqualArea(_Conic): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. lat1 : float The first standard parallel. @@ -403,9 +403,9 @@ class EquidistantConic(_Conic): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. lat1 : float The first standard parallel. @@ -427,9 +427,9 @@ class CassiniCylindrical(_Cylindrical): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. width : str The figure width. For example ``8i`` is 8 inches. @@ -449,16 +449,16 @@ class MercatorCylindrical(_Cylindrical): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. Default is 180. - lat0 : float + central_latitude : float The latitude of the projection centre. Default is 0. width : str The figure width. For example ``8i`` is 8 inches. """ - lon0: float = attr.ib(default=180, kw_only=True) - lat0: float = attr.ib(default=0, kw_only=True) + central_longitude: float = attr.ib(default=180, kw_only=True) + central_latitude: float = attr.ib(default=0, kw_only=True) # private; we don't want the user to care or know about _code: str = attr.ib( @@ -474,16 +474,16 @@ class CylindricalStereographic(_Cylindrical): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. Default is 180. - lat0 : float + central_latitude : float The latitude of the projection centre. Default is 0. width : str The figure width. For example ``8i`` is 8 inches. """ - lon0: float = attr.ib(default=180, kw_only=True) - lat0: float = attr.ib(default=0, kw_only=True) + central_longitude: float = attr.ib(default=180, kw_only=True) + central_latitude: float = attr.ib(default=0, kw_only=True) # private; we don't want the user to care or know about _code: str = attr.ib( @@ -499,9 +499,9 @@ class CylindricalEqualArea(_Cylindrical): Parameters ---------- - lon0 : float + central_longitude : float The longitude of the projection centre. - lat0 : float + central_latitude : float The latitude of the projection centre. width : str The figure width. For example ``8i`` is 8 inches. From 29e263d025056d61708c8ac0e408aca94fa78e69 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 19 Nov 2019 21:42:50 +1100 Subject: [PATCH 05/30] Added the attribute to give meaning to the width argument (inches or centimetres). --- pygmt/projection.py | 121 +++++++++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 36 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 249542d8945..57942b435f8 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -87,18 +87,22 @@ class _Azimuthal(_Projection): The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() horizon: float = attr.ib(default=90) - width: str = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default='i') # private; we don't want the user to care or know about _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}" + init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}{unit}" ) _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @@ -123,16 +127,20 @@ class _Cylindrical(_Projection): The longitude of the projection centre. central_latitude : float The latitude of the projection centre. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() - width: str = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default='i') # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{wdith}") + _fmt: str = attr.ib(init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{wdith}{unit}") _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @@ -152,8 +160,11 @@ class _Conic: The first standard parallel. lat2 : float The second standard parallel. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ central_longitude: float = attr.ib() @@ -161,10 +172,11 @@ class _Conic: lat1: float = attr.ib() lat2: float = attr.ib() width: float = attr.ib() + unit: str = attr.ib(default='i') # private; we don't want the user to care or know about _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{lat1}/{lat2}/{width}" + init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{lat1}/{lat2}/{width}{unit}" ) @@ -182,8 +194,11 @@ class LambertAzimuthalEqualArea(_Azimuthal): The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ # private; we don't want the user to care or know about @@ -206,8 +221,11 @@ class AzimuthalEquidistant(_Azimuthal): The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 180. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ horizon: float = attr.ib(default=180, kw_only=True) @@ -232,8 +250,11 @@ class AzimuthalGnomic(_Azimuthal): The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 60. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ horizon: float = attr.ib(default=60, kw_only=True) @@ -266,8 +287,11 @@ class AzimuthalOrthographic(_Azimuthal): The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ horizon: float = attr.ib(default=90) @@ -310,8 +334,11 @@ class GeneralPerspective(_Projection): The width of the viewing angle (in degrees). viewport_height : float The height of the viewing angle (in degrees). - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ central_longitude: float = attr.ib() @@ -323,12 +350,13 @@ class GeneralPerspective(_Projection): Width: float = attr.ib() Height: float = attr.ib() width: float = attr.ib() + unit: str = attr.ib(default='i') # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_longitude}/{central_latitude}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}", + default="{_code}{central_longitude}/{central_latitude}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}{unit}", ) _code: str = attr.ib( init=False, repr=False, default=Supported.GENERAL_PERSPECTIVE.value @@ -349,8 +377,11 @@ class GeneralSterographic(_Azimuthal): The latitude of the projection centre. horizon : float The max distance to the projection centre in degrees. Default is 90. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ horizon: float = attr.ib(default=90, kw_only=True) @@ -385,8 +416,11 @@ class AlbersConicEqualArea(_Conic): The first standard parallel. lat2 : float The second standard parallel. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ # private; we don't want the user to care or know about @@ -411,8 +445,11 @@ class EquidistantConic(_Conic): The first standard parallel. lat2 : float The second standard parallel. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ # private; we don't want the user to care or know about @@ -431,8 +468,11 @@ class CassiniCylindrical(_Cylindrical): The longitude of the projection centre. central_latitude : float The latitude of the projection centre. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ # private; we don't want the user to care or know about @@ -453,8 +493,11 @@ class MercatorCylindrical(_Cylindrical): The longitude of the projection centre. Default is 180. central_latitude : float The latitude of the projection centre. Default is 0. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ central_longitude: float = attr.ib(default=180, kw_only=True) @@ -478,8 +521,11 @@ class CylindricalStereographic(_Cylindrical): The longitude of the projection centre. Default is 180. central_latitude : float The latitude of the projection centre. Default is 0. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ central_longitude: float = attr.ib(default=180, kw_only=True) @@ -503,8 +549,11 @@ class CylindricalEqualArea(_Cylindrical): The longitude of the projection centre. central_latitude : float The latitude of the projection centre. - width : str - The figure width. For example ``8i`` is 8 inches. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + Default is ``i``. """ # private; we don't want the user to care or know about From 129ae997c87872435bc8910db98eaffb638e3362 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 19 Nov 2019 21:50:07 +1100 Subject: [PATCH 06/30] Updated example, and cleaned up docstring for unit description. --- pygmt/projection.py | 56 ++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 57942b435f8..b1bd3ec59a3 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -5,9 +5,9 @@ to create a projection and output a valid GMT projection string. >>> from pygmt import projection ->>> proj = projection.LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width="8i") +>>> proj = projection.LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit="i") >>> proj -LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width='8i') +LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit='i', center=[30, -20]) >>> print(proj) A30/-20/60/8i """ @@ -90,7 +90,7 @@ class _Azimuthal(_Projection): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -98,11 +98,13 @@ class _Azimuthal(_Projection): central_latitude: float = attr.ib() horizon: float = attr.ib(default=90) width: float = attr.ib() - unit: str = attr.ib(default='i') + unit: str = attr.ib(default="i") # private; we don't want the user to care or know about _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}{unit}" + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @@ -130,17 +132,21 @@ class _Cylindrical(_Projection): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default='i') + unit: str = attr.ib(default="i") # private; we don't want the user to care or know about - _fmt: str = attr.ib(init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{wdith}{unit}") + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{wdith}{unit}", + ) _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) @@ -163,7 +169,7 @@ class _Conic: width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -172,11 +178,13 @@ class _Conic: lat1: float = attr.ib() lat2: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default='i') + unit: str = attr.ib(default="i") # private; we don't want the user to care or know about _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{central_longitude}/{central_latitude}/{lat1}/{lat2}/{width}{unit}" + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{lat1}/{lat2}/{width}{unit}", ) @@ -197,7 +205,7 @@ class LambertAzimuthalEqualArea(_Azimuthal): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -224,7 +232,7 @@ class AzimuthalEquidistant(_Azimuthal): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -253,7 +261,7 @@ class AzimuthalGnomic(_Azimuthal): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -290,7 +298,7 @@ class AzimuthalOrthographic(_Azimuthal): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -337,7 +345,7 @@ class GeneralPerspective(_Projection): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -350,7 +358,7 @@ class GeneralPerspective(_Projection): Width: float = attr.ib() Height: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default='i') + unit: str = attr.ib(default="i") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -380,7 +388,7 @@ class GeneralSterographic(_Azimuthal): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -419,7 +427,7 @@ class AlbersConicEqualArea(_Conic): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -448,7 +456,7 @@ class EquidistantConic(_Conic): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -471,7 +479,7 @@ class CassiniCylindrical(_Cylindrical): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -496,7 +504,7 @@ class MercatorCylindrical(_Cylindrical): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -524,7 +532,7 @@ class CylindricalStereographic(_Cylindrical): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ @@ -552,7 +560,7 @@ class CylindricalEqualArea(_Cylindrical): width : float The figure width. unit : str - The unit for the figure width in ``i`` for inch, ``c`` for centimetre and ``p`` for point. + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``i``. """ From adef7ad3eb7b61fb1d55155c9ec0b98c1244551a Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 14:48:15 +1100 Subject: [PATCH 07/30] Removed center attribute from printed example. --- pygmt/projection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index b1bd3ec59a3..c81ec9b3d2e 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -7,7 +7,7 @@ >>> from pygmt import projection >>> proj = projection.LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit="i") >>> proj -LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit='i', center=[30, -20]) +LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit='i') >>> print(proj) A30/-20/60/8i """ From 97c836b2c031328e490630cb151f1b179248a292 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 15:14:49 +1100 Subject: [PATCH 08/30] Specifying the projection code directly in the attrib creation, rather than use the Enum which will be removed once all projections are defined. --- pygmt/projection.py | 55 +++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index c81ec9b3d2e..0a01e0c2adf 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -16,6 +16,9 @@ import attr +UNDEFINED = "" + + class Supported(Enum): """ @@ -65,7 +68,7 @@ class _Projection: # private; we don't want the user to care or know about _fmt: str = attr.ib(init=False, repr=False, default="{_code}") - _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) + _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) def __str__(self): exclude = attr.fields(self.__class__)._fmt @@ -106,7 +109,7 @@ class _Azimuthal(_Projection): repr=False, default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}{unit}", ) - _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) + _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) @horizon.validator def check_horizon(self, attribute, value): @@ -147,7 +150,7 @@ class _Cylindrical(_Projection): repr=False, default="{_code}{central_longitude}/{central_latitude}/{wdith}{unit}", ) - _code: str = attr.ib(init=False, repr=False, default=Supported.UNDEFINED.value) + _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) @attr.s(kw_only=True) @@ -210,9 +213,7 @@ class LambertAzimuthalEqualArea(_Azimuthal): """ # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.LAMBERT_AZIMUTH_EQUAL_AREA.value - ) + _code: str = attr.ib(init=False, repr=False, default="A") @attr.s(frozen=True) @@ -239,9 +240,7 @@ class AzimuthalEquidistant(_Azimuthal): horizon: float = attr.ib(default=180, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.AZIMUTHAL_EQUIDISTANT.value - ) + _code: str = attr.ib(init=False, repr=False, default="E") @attr.s(frozen=True) @@ -268,9 +267,7 @@ class AzimuthalGnomic(_Azimuthal): horizon: float = attr.ib(default=60, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.AZIMUTHAL_GNOMIC.value - ) + _code: str = attr.ib(init=False, repr=False, default="F") @horizon.validator def check_horizon(self, attribute, value): @@ -305,9 +302,7 @@ class AzimuthalOrthographic(_Azimuthal): horizon: float = attr.ib(default=90) # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.AZIMUTHAL_ORTHOGRAPHIC.value - ) + _code: str = attr.ib(init=False, repr=False, default="G") @horizon.validator def check_horizon(self, attribute, value): @@ -366,9 +361,7 @@ class GeneralPerspective(_Projection): repr=False, default="{_code}{central_longitude}/{central_latitude}/{altitude}/{azimuth}/{tilt}/{twist}/{viewport_width}/{viewport_height}/{width}{unit}", ) - _code: str = attr.ib( - init=False, repr=False, default=Supported.GENERAL_PERSPECTIVE.value - ) + _code: str = attr.ib(init=False, repr=False, default="G") @attr.s(frozen=True) @@ -395,9 +388,7 @@ class GeneralSterographic(_Azimuthal): horizon: float = attr.ib(default=90, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.GENERAL_STEREOGRAPHIC.value - ) + _code: str = attr.ib(init=False, repr=False, default="S") @horizon.validator def check_horizon(self, attribute, value): @@ -432,9 +423,7 @@ class AlbersConicEqualArea(_Conic): """ # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.ALBERS_CONIC_EQUAL_AREA.value - ) + _code: str = attr.ib(init=False, repr=False, default="B") @attr.s(frozen=True, kw_only=True) @@ -461,7 +450,7 @@ class EquidistantConic(_Conic): """ # private; we don't want the user to care or know about - _code: str = attr.ib(init=False, repr=False, default=Supported.EQUIDISTANT_CONIC) + _code: str = attr.ib(init=False, repr=False, default="JD") @attr.s(frozen=True) @@ -484,9 +473,7 @@ class CassiniCylindrical(_Cylindrical): """ # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.CASSINI_CYLINDRICAL.value - ) + _code: str = attr.ib(init=False, repr=False, default="C") @attr.s(frozen=True) @@ -512,9 +499,7 @@ class MercatorCylindrical(_Cylindrical): central_latitude: float = attr.ib(default=0, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.MERCATOR_CYLINDRICAL.value - ) + _code: str = attr.ib(init=False, repr=False, default="M") @attr.s(frozen=True) @@ -540,9 +525,7 @@ class CylindricalStereographic(_Cylindrical): central_latitude: float = attr.ib(default=0, kw_only=True) # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.CYLINDRICAL_STEROGRAPHIC.value - ) + _code: str = attr.ib(init=False, repr=False, default="JCyl_stere/") @attr.s(frozen=True) @@ -565,6 +548,4 @@ class CylindricalEqualArea(_Cylindrical): """ # private; we don't want the user to care or know about - _code: str = attr.ib( - init=False, repr=False, default=Supported.CYLINDRICAL_EQUAL_AREA.value - ) + _code: str = attr.ib(init=False, repr=False, default="Y") From a414f7ed0682c36d23fe2718949384e59f81e8dc Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 18:24:48 +1100 Subject: [PATCH 09/30] Added the miscellaneous projections group; Mollweide, Sinusoidal, Eckert IV, Eckert VI, Van der Grinten, Winkel Tripel, Hammer, Robinson. --- pygmt/projection.py | 215 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 206 insertions(+), 9 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 0a01e0c2adf..9fac642f529 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -35,26 +35,26 @@ class Supported(Enum): AZIMUTHAL_GNOMIC = "F" # DONE AZIMUTHAL_ORTHOGRAPHIC = "G" # DONE GENERAL_PERSPECTIVE = "G" # DONE - HAMMER_EQUAL_AREA = "H" - SINUSOIDAL_EQUAL_AREA = "I" + HAMMER_EQUAL_AREA = "H" # DONE + SINUSOIDAL_EQUAL_AREA = "I" # DONE MILLER_CYLINDRICAL = "J" - ECKERT_IV_EQUAL_AREA = "Kf" - ECKERT_VI_EQUAL_AREA = "Ks" + ECKERT_IV_EQUAL_AREA = "Kf" # DONE + ECKERT_VI_EQUAL_AREA = "Ks" # DONE LAMBERT_CONIC_CONFORMAL = "L" MERCATOR_CYLINDRICAL = "M" # DONE - ROBINSON = "N" + ROBINSON = "N" # DONE OBLIQUE_MERCATOR_1 = "Oa" OBLIQUE_MERCATOR_2 = "Ob" OBLIQUE_MERCATOR_3 = "Oc" POLAR = "P" POLYCONIC = "Poly" EQUIDISTANT_CYLINDRICAL = "Q" - WINKEL_TRIPEL = "R" + WINKEL_TRIPEL = "R" # DONE GENERAL_STEREOGRAPHIC = "S" # DONE TRANSVERSE_MERCATOR = "T" UNIVERSAL_TRANSVERSE_MERCATOR = "U" - VAN_DER_GRINTEN = "V" - MOLLWEIDE = "W" + VAN_DER_GRINTEN = "V" # DONE + MOLLWEIDE = "W" # DONE LINEAR = "X" CYLINDRICAL_EQUAL_AREA = "Y" # DONE @@ -154,7 +154,7 @@ class _Cylindrical(_Projection): @attr.s(kw_only=True) -class _Conic: +class _Conic(_Projection): """ Base class for conic projections. @@ -191,6 +191,35 @@ class _Conic: ) +@attr.s(kw_only=True) +class _Miscellaneous(_Projection): + + """ + Base class for miscellaneous projections. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + central_meridian: float = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="i") + + # private; we don't want the user to care or know about + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_meridian}/{width}{unit}", + ) + + @attr.s(frozen=True) class LambertAzimuthalEqualArea(_Azimuthal): @@ -549,3 +578,171 @@ class CylindricalEqualArea(_Cylindrical): # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="Y") + + +@attr.s(frozen=True) +class HammerEqualArea(_Miscellaneous): + + """ + Class definition for the hammer equal area projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="H") + + +@attr.s(frozen=True) +class SinusoidalEqualArea(_Miscellaneous): + + """ + Class definition for the sinusoidal equal area projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="I") + + +@attr.s(frozen=True) +class EckertIVEqualArea(_Miscellaneous): + + """ + Class definition for the eckert IV equal area projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="Kf") + + +@attr.s(frozen=True) +class EckertVIEqualArea(_Miscellaneous): + + """ + Class definition for the eckert VI equal area projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="Ks") + + +@attr.s(frozen=True) +class Robinson(_Miscellaneous): + + """ + Class definition for the robinson projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="N") + + +@attr.s(frozen=True) +class WinkelTripel(_Miscellaneous): + + """ + Class definition for the winkel tripel projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="R") + + +@attr.s(frozen=True) +class Mollweide(_Miscellaneous): + + """ + Class definition for the mollweide projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="W") + + +@attr.s(frozen=True) +class VanDerGrinten(_Miscellaneous): + + """ + Class definition for the van der grinten projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="V") From c7ee1a24f6f140696cbb38bc1a4d599c059baba8 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 18:30:10 +1100 Subject: [PATCH 10/30] Capitalised projection names where required, eg when named after the inventer. --- pygmt/projection.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 9fac642f529..4c7e51e2255 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -224,7 +224,7 @@ class _Miscellaneous(_Projection): class LambertAzimuthalEqualArea(_Azimuthal): """ - Class definition for the lambert azimuthal equal area projection. + Class definition for the Lambert azimuthal equal area projection. Parameters ---------- @@ -432,7 +432,7 @@ def check_horizon(self, attribute, value): class AlbersConicEqualArea(_Conic): """ - Class definition for the albers conic equal area projection. + Class definition for the Albers conic equal area projection. Parameters ---------- @@ -486,7 +486,7 @@ class EquidistantConic(_Conic): class CassiniCylindrical(_Cylindrical): """ - Class definition for the cassini cylindrical projection. + Class definition for the Cassini cylindrical projection. Parameters ---------- @@ -509,7 +509,7 @@ class CassiniCylindrical(_Cylindrical): class MercatorCylindrical(_Cylindrical): """ - Class definition for the cassini cylindrical projection. + Class definition for the Mercator cylindrical projection. Parameters ---------- @@ -535,7 +535,7 @@ class MercatorCylindrical(_Cylindrical): class CylindricalStereographic(_Cylindrical): """ - Class definition for the cassini cylindrical projection. + Class definition for the cylindrical stereographic projection. Parameters ---------- @@ -561,7 +561,7 @@ class CylindricalStereographic(_Cylindrical): class CylindricalEqualArea(_Cylindrical): """ - Class definition for the cassini cylindrical projection. + Class definition for the cylindrical equal area projection. Parameters ---------- @@ -584,7 +584,7 @@ class CylindricalEqualArea(_Cylindrical): class HammerEqualArea(_Miscellaneous): """ - Class definition for the hammer equal area projection. + Class definition for the Hammer equal area projection. Parameters ---------- @@ -626,7 +626,7 @@ class SinusoidalEqualArea(_Miscellaneous): class EckertIVEqualArea(_Miscellaneous): """ - Class definition for the eckert IV equal area projection. + Class definition for the Eckert IV equal area projection. Parameters ---------- @@ -647,7 +647,7 @@ class EckertIVEqualArea(_Miscellaneous): class EckertVIEqualArea(_Miscellaneous): """ - Class definition for the eckert VI equal area projection. + Class definition for the Eckert VI equal area projection. Parameters ---------- @@ -668,7 +668,7 @@ class EckertVIEqualArea(_Miscellaneous): class Robinson(_Miscellaneous): """ - Class definition for the robinson projection. + Class definition for the Robinson projection. Parameters ---------- @@ -689,7 +689,7 @@ class Robinson(_Miscellaneous): class WinkelTripel(_Miscellaneous): """ - Class definition for the winkel tripel projection. + Class definition for the Winkel tripel projection. Parameters ---------- @@ -710,7 +710,7 @@ class WinkelTripel(_Miscellaneous): class Mollweide(_Miscellaneous): """ - Class definition for the mollweide projection. + Class definition for the Mollweide projection. Parameters ---------- @@ -731,7 +731,7 @@ class Mollweide(_Miscellaneous): class VanDerGrinten(_Miscellaneous): """ - Class definition for the van der grinten projection. + Class definition for the Van der Grinten projection. Parameters ---------- From db7aca692f5ff3a51bd1dd95dde0966f20fd1cde Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 18:42:26 +1100 Subject: [PATCH 11/30] Added the Polyconic projection. --- pygmt/projection.py | 64 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 4c7e51e2255..2065765318c 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -40,14 +40,14 @@ class Supported(Enum): MILLER_CYLINDRICAL = "J" ECKERT_IV_EQUAL_AREA = "Kf" # DONE ECKERT_VI_EQUAL_AREA = "Ks" # DONE - LAMBERT_CONIC_CONFORMAL = "L" + LAMBERT_CONIC_CONFORMAL = "L" # DONE MERCATOR_CYLINDRICAL = "M" # DONE ROBINSON = "N" # DONE OBLIQUE_MERCATOR_1 = "Oa" OBLIQUE_MERCATOR_2 = "Ob" OBLIQUE_MERCATOR_3 = "Oc" POLAR = "P" - POLYCONIC = "Poly" + POLYCONIC = "Poly" # DONE EQUIDISTANT_CYLINDRICAL = "Q" WINKEL_TRIPEL = "R" # DONE GENERAL_STEREOGRAPHIC = "S" # DONE @@ -746,3 +746,63 @@ class VanDerGrinten(_Miscellaneous): # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="V") + + +@attr.s(frozen=True, kw_only=True) +class LambertConicConformal(_Conic): + + """ + Class definition for the Lambert conic conformal projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + lat1 : float + The first standard parallel. + lat2 : float + The second standard parallel. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="L") + + +@attr.s(frozen=True, kw_only=True) +class Polyconic(_Projection): + + """ + Class definition for the (American) polyconic projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="i") + + # private; we don't want the user to care or know about + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{width}{unit}", + ) + _code: str = attr.ib(init=False, repr=False, default="Poly") From 825cd668e2f13fe071767ebb0f919aa10f5e5eb5 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 19:52:28 +1100 Subject: [PATCH 12/30] Added the Miller and oblique 1, 2, 3 projections. --- pygmt/projection.py | 144 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 5 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 2065765318c..d25bf2ec901 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -37,15 +37,15 @@ class Supported(Enum): GENERAL_PERSPECTIVE = "G" # DONE HAMMER_EQUAL_AREA = "H" # DONE SINUSOIDAL_EQUAL_AREA = "I" # DONE - MILLER_CYLINDRICAL = "J" + MILLER_CYLINDRICAL = "J" # DONE ECKERT_IV_EQUAL_AREA = "Kf" # DONE ECKERT_VI_EQUAL_AREA = "Ks" # DONE LAMBERT_CONIC_CONFORMAL = "L" # DONE MERCATOR_CYLINDRICAL = "M" # DONE ROBINSON = "N" # DONE - OBLIQUE_MERCATOR_1 = "Oa" - OBLIQUE_MERCATOR_2 = "Ob" - OBLIQUE_MERCATOR_3 = "Oc" + OBLIQUE_MERCATOR_1 = "Oa" # DONE + OBLIQUE_MERCATOR_2 = "Ob" # DONE + OBLIQUE_MERCATOR_3 = "Oc" # DONE POLAR = "P" POLYCONIC = "Poly" # DONE EQUIDISTANT_CYLINDRICAL = "Q" @@ -748,7 +748,7 @@ class VanDerGrinten(_Miscellaneous): _code: str = attr.ib(init=False, repr=False, default="V") -@attr.s(frozen=True, kw_only=True) +@attr.s(frozen=True) class LambertConicConformal(_Conic): """ @@ -806,3 +806,137 @@ class Polyconic(_Projection): default="{_code}{central_longitude}/{central_latitude}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default="Poly") + + +@attr.s(frozen=True) +class Miller(_Miscellaneous): + + """ + Class definition for the Miller cylindrical projection. + + Parameters + ---------- + central_meridian : float + The central meridian/longitude to use as the centre of the map. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="J") + + +@attr.s(frozen=True, kw_only=True) +class ObliqueMercator1(_Projection): + + """ + Class definition for the oblique Mercator 1 projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + azimuth : float + Azimuth of the oblique equator. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="i") + + # private; we don't want the user to care or know about + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{azimuth}/{width}{unit}", + ) + _code: str = attr.ib(init=False, repr=False, default="Oa") + + +@attr.s(frozen=True, kw_only=True) +class ObliqueMercator2(_Projection): + + """ + Class definition for the oblique Mercator 2 projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + oblique_longitude : float + The longitude of the second point on an oblique equator. + oblique_latitude : float + The latitude of the second point on an oblique equator. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() + oblique_longitude: float = attr.ib() + oblique_latitude: float = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="i") + + # private; we don't want the user to care or know about + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{oblique_longitude}/{oblique_latitude}/{width}{unit}", + ) + _code: str = attr.ib(init=False, repr=False, default="Ob") + + +@attr.s(frozen=True, kw_only=True) +class ObliqueMercator3(_Projection): + + """ + Class definition for the oblique Mercator 3 projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + pole_longitude : float + The longitude of the projection pole. + pole_latitude : float + The latitude of the projection pole. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() + pole_longitude: float = attr.ib() + pole_latitude: float = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="i") + + # private; we don't want the user to care or know about + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_longitude}/{central_latitude}/{pole_longitude}/{pole_latitude}/{width}{unit}", + ) + _code: str = attr.ib(init=False, repr=False, default="Oc") From acaca7d5c8033f0a50ec6952d2f58d577afd9663 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 20:08:47 +1100 Subject: [PATCH 13/30] Added the Transverse Mercator and Universal Transverse Mercator Projections. --- pygmt/projection.py | 58 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index d25bf2ec901..b8720bdafb2 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -51,8 +51,8 @@ class Supported(Enum): EQUIDISTANT_CYLINDRICAL = "Q" WINKEL_TRIPEL = "R" # DONE GENERAL_STEREOGRAPHIC = "S" # DONE - TRANSVERSE_MERCATOR = "T" - UNIVERSAL_TRANSVERSE_MERCATOR = "U" + TRANSVERSE_MERCATOR = "T" # DONE + UNIVERSAL_TRANSVERSE_MERCATOR = "U" # DONE VAN_DER_GRINTEN = "V" # DONE MOLLWEIDE = "W" # DONE LINEAR = "X" @@ -940,3 +940,57 @@ class ObliqueMercator3(_Projection): default="{_code}{central_longitude}/{central_latitude}/{pole_longitude}/{pole_latitude}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default="Oc") + + +@attr.s(frozen=True) +class TransverseMercator(_Cylindrical): + + """ + Class definition for the Transverse Mercator projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="T") + + +@attr.s(frozen=True, kw_only=True) +class UniversalTransverseMercator(_Projection): + + """ + Class definition for the Universal Transverse Mercator projection. + + Parameters + ---------- + zone : str + The UTM zone {A, B, Y, Z, 1-60}. Use negative values for numerical + zones in the southern hemisphere, or append the latitude modifiers + {C-N, P-X} to specify and exact UTM grid zone. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + zone: str = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="i") + + # private; we don't want the user to care or know about + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{zone}/{width}{unit}", + ) + _code: str = attr.ib(init=False, repr=False, default="U") From 919102dd7ee7c876e6e56ae785653953f757892c Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 20:20:34 +1100 Subject: [PATCH 14/30] Added the equidistant cylindrical projection. --- pygmt/projection.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index b8720bdafb2..04fe243d5ba 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -48,7 +48,7 @@ class Supported(Enum): OBLIQUE_MERCATOR_3 = "Oc" # DONE POLAR = "P" POLYCONIC = "Poly" # DONE - EQUIDISTANT_CYLINDRICAL = "Q" + EQUIDISTANT_CYLINDRICAL = "Q" # DONE WINKEL_TRIPEL = "R" # DONE GENERAL_STEREOGRAPHIC = "S" # DONE TRANSVERSE_MERCATOR = "T" # DONE @@ -994,3 +994,29 @@ class UniversalTransverseMercator(_Projection): default="{_code}{zone}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default="U") + + +@attr.s(frozen=True) +class EquidistantCylindrical(_Cylindrical): + + """ + Class definition for the equidistant cylindrical projection. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. Default is 180. + central_latitude : float + The latitude of the projection centre. Default is 0. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``i``. + """ + + central_longitude: float = attr.ib(default=180, kw_only=True) + central_latitude: float = attr.ib(default=0, kw_only=True) + + # private; we don't want the user to care or know about + _code: str = attr.ib(init=False, repr=False, default="Q") From f5897a8fbff8a518a3b5c8136dbe278b97ccfe14 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sat, 7 Dec 2019 21:15:22 +1100 Subject: [PATCH 15/30] Fixed as per @leouieda suggestions. --- pygmt/projection.py | 47 ++------------------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 04fe243d5ba..bd976f720d5 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -1,15 +1,6 @@ -#!/usr/bin/env python - """ Contains the projections supported by GMT, and the necessary mechanisms to create a projection and output a valid GMT projection string. - ->>> from pygmt import projection ->>> proj = projection.LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit="i") ->>> proj -LambertAzimuthalEqualArea(central_longitude=30, central_latitude=-20, horizon=60, width=8, unit='i') ->>> print(proj) -A30/-20/60/8i """ from enum import Enum @@ -61,7 +52,6 @@ class Supported(Enum): @attr.s() class _Projection: - """ Base class for all projections. """ @@ -71,6 +61,7 @@ class _Projection: _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) def __str__(self): + "Convert to the GMT-style projection code." exclude = attr.fields(self.__class__)._fmt kwargs = attr.asdict(self, filter=attr.filters.exclude(exclude)) return self._fmt.format(**kwargs) @@ -78,7 +69,6 @@ def __str__(self): @attr.s(kw_only=True) class _Azimuthal(_Projection): - """ Base class for azimuthal projections. @@ -109,7 +99,6 @@ class _Azimuthal(_Projection): repr=False, default="{_code}{central_longitude}/{central_latitude}/{horizon}/{width}{unit}", ) - _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) @horizon.validator def check_horizon(self, attribute, value): @@ -122,7 +111,6 @@ def check_horizon(self, attribute, value): @attr.s(kw_only=True) class _Cylindrical(_Projection): - """ Base class for cylindrical projections. @@ -148,14 +136,13 @@ class _Cylindrical(_Projection): _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_longitude}/{central_latitude}/{wdith}{unit}", + default="{_code}{central_longitude}/{central_latitude}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) @attr.s(kw_only=True) class _Conic(_Projection): - """ Base class for conic projections. @@ -193,7 +180,6 @@ class _Conic(_Projection): @attr.s(kw_only=True) class _Miscellaneous(_Projection): - """ Base class for miscellaneous projections. @@ -222,7 +208,6 @@ class _Miscellaneous(_Projection): @attr.s(frozen=True) class LambertAzimuthalEqualArea(_Azimuthal): - """ Class definition for the Lambert azimuthal equal area projection. @@ -247,7 +232,6 @@ class LambertAzimuthalEqualArea(_Azimuthal): @attr.s(frozen=True) class AzimuthalEquidistant(_Azimuthal): - """ Class definition for the azimuthal equidistant projection. @@ -274,7 +258,6 @@ class AzimuthalEquidistant(_Azimuthal): @attr.s(frozen=True) class AzimuthalGnomic(_Azimuthal): - """ Class definition for the azimuthal gnomic projection. @@ -309,7 +292,6 @@ def check_horizon(self, attribute, value): @attr.s(frozen=True) class AzimuthalOrthographic(_Azimuthal): - """ Class definition for the azimuthal orthographic projection. @@ -344,7 +326,6 @@ def check_horizon(self, attribute, value): @attr.s(frozen=True, kw_only=True) class GeneralPerspective(_Projection): - """ Class definition for the azimuthal general perspective projection. @@ -395,7 +376,6 @@ class GeneralPerspective(_Projection): @attr.s(frozen=True) class GeneralSterographic(_Azimuthal): - """ Class definition for the azimuthal general sterographic projection. @@ -430,7 +410,6 @@ def check_horizon(self, attribute, value): @attr.s(frozen=True, kw_only=True) class AlbersConicEqualArea(_Conic): - """ Class definition for the Albers conic equal area projection. @@ -457,7 +436,6 @@ class AlbersConicEqualArea(_Conic): @attr.s(frozen=True, kw_only=True) class EquidistantConic(_Conic): - """ Class definition for the equidistant conic projection. @@ -484,7 +462,6 @@ class EquidistantConic(_Conic): @attr.s(frozen=True) class CassiniCylindrical(_Cylindrical): - """ Class definition for the Cassini cylindrical projection. @@ -507,7 +484,6 @@ class CassiniCylindrical(_Cylindrical): @attr.s(frozen=True) class MercatorCylindrical(_Cylindrical): - """ Class definition for the Mercator cylindrical projection. @@ -533,7 +509,6 @@ class MercatorCylindrical(_Cylindrical): @attr.s(frozen=True) class CylindricalStereographic(_Cylindrical): - """ Class definition for the cylindrical stereographic projection. @@ -559,7 +534,6 @@ class CylindricalStereographic(_Cylindrical): @attr.s(frozen=True) class CylindricalEqualArea(_Cylindrical): - """ Class definition for the cylindrical equal area projection. @@ -582,7 +556,6 @@ class CylindricalEqualArea(_Cylindrical): @attr.s(frozen=True) class HammerEqualArea(_Miscellaneous): - """ Class definition for the Hammer equal area projection. @@ -603,7 +576,6 @@ class HammerEqualArea(_Miscellaneous): @attr.s(frozen=True) class SinusoidalEqualArea(_Miscellaneous): - """ Class definition for the sinusoidal equal area projection. @@ -624,7 +596,6 @@ class SinusoidalEqualArea(_Miscellaneous): @attr.s(frozen=True) class EckertIVEqualArea(_Miscellaneous): - """ Class definition for the Eckert IV equal area projection. @@ -645,7 +616,6 @@ class EckertIVEqualArea(_Miscellaneous): @attr.s(frozen=True) class EckertVIEqualArea(_Miscellaneous): - """ Class definition for the Eckert VI equal area projection. @@ -666,7 +636,6 @@ class EckertVIEqualArea(_Miscellaneous): @attr.s(frozen=True) class Robinson(_Miscellaneous): - """ Class definition for the Robinson projection. @@ -687,7 +656,6 @@ class Robinson(_Miscellaneous): @attr.s(frozen=True) class WinkelTripel(_Miscellaneous): - """ Class definition for the Winkel tripel projection. @@ -708,7 +676,6 @@ class WinkelTripel(_Miscellaneous): @attr.s(frozen=True) class Mollweide(_Miscellaneous): - """ Class definition for the Mollweide projection. @@ -729,7 +696,6 @@ class Mollweide(_Miscellaneous): @attr.s(frozen=True) class VanDerGrinten(_Miscellaneous): - """ Class definition for the Van der Grinten projection. @@ -750,7 +716,6 @@ class VanDerGrinten(_Miscellaneous): @attr.s(frozen=True) class LambertConicConformal(_Conic): - """ Class definition for the Lambert conic conformal projection. @@ -777,7 +742,6 @@ class LambertConicConformal(_Conic): @attr.s(frozen=True, kw_only=True) class Polyconic(_Projection): - """ Class definition for the (American) polyconic projection. @@ -810,7 +774,6 @@ class Polyconic(_Projection): @attr.s(frozen=True) class Miller(_Miscellaneous): - """ Class definition for the Miller cylindrical projection. @@ -831,7 +794,6 @@ class Miller(_Miscellaneous): @attr.s(frozen=True, kw_only=True) class ObliqueMercator1(_Projection): - """ Class definition for the oblique Mercator 1 projection. @@ -866,7 +828,6 @@ class ObliqueMercator1(_Projection): @attr.s(frozen=True, kw_only=True) class ObliqueMercator2(_Projection): - """ Class definition for the oblique Mercator 2 projection. @@ -905,7 +866,6 @@ class ObliqueMercator2(_Projection): @attr.s(frozen=True, kw_only=True) class ObliqueMercator3(_Projection): - """ Class definition for the oblique Mercator 3 projection. @@ -944,7 +904,6 @@ class ObliqueMercator3(_Projection): @attr.s(frozen=True) class TransverseMercator(_Cylindrical): - """ Class definition for the Transverse Mercator projection. @@ -967,7 +926,6 @@ class TransverseMercator(_Cylindrical): @attr.s(frozen=True, kw_only=True) class UniversalTransverseMercator(_Projection): - """ Class definition for the Universal Transverse Mercator projection. @@ -998,7 +956,6 @@ class UniversalTransverseMercator(_Projection): @attr.s(frozen=True) class EquidistantCylindrical(_Cylindrical): - """ Class definition for the equidistant cylindrical projection. From 7abe416c28eecaf8c61060e6d7dff219586a6845 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sun, 8 Dec 2019 18:54:23 +1100 Subject: [PATCH 16/30] Missed one of the fixes as suggested by @leouieda --- pygmt/projection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index bd976f720d5..cf1fcfe1207 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -138,7 +138,6 @@ class _Cylindrical(_Projection): repr=False, default="{_code}{central_longitude}/{central_latitude}/{width}{unit}", ) - _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) @attr.s(kw_only=True) From 45abd597e5a6a5ab7740b3b1fe2c69b1e09f6426 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sun, 8 Dec 2019 20:29:22 +1100 Subject: [PATCH 17/30] Changed the default unit of inches to centimetres. --- pygmt/projection.py | 86 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index cf1fcfe1207..65177fe217a 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -84,14 +84,14 @@ class _Azimuthal(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() horizon: float = attr.ib(default=90) width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -124,13 +124,13 @@ class _Cylindrical(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -159,7 +159,7 @@ class _Conic(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() @@ -167,7 +167,7 @@ class _Conic(_Projection): lat1: float = attr.ib() lat2: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -190,12 +190,12 @@ class _Miscellaneous(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_meridian: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -222,7 +222,7 @@ class LambertAzimuthalEqualArea(_Azimuthal): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -246,7 +246,7 @@ class AzimuthalEquidistant(_Azimuthal): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ horizon: float = attr.ib(default=180, kw_only=True) @@ -272,7 +272,7 @@ class AzimuthalGnomic(_Azimuthal): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ horizon: float = attr.ib(default=60, kw_only=True) @@ -306,7 +306,7 @@ class AzimuthalOrthographic(_Azimuthal): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ horizon: float = attr.ib(default=90) @@ -350,7 +350,7 @@ class GeneralPerspective(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() @@ -362,7 +362,7 @@ class GeneralPerspective(_Projection): Width: float = attr.ib() Height: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -390,7 +390,7 @@ class GeneralSterographic(_Azimuthal): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ horizon: float = attr.ib(default=90, kw_only=True) @@ -426,7 +426,7 @@ class AlbersConicEqualArea(_Conic): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -452,7 +452,7 @@ class EquidistantConic(_Conic): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -474,7 +474,7 @@ class CassiniCylindrical(_Cylindrical): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -496,7 +496,7 @@ class MercatorCylindrical(_Cylindrical): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib(default=180, kw_only=True) @@ -521,7 +521,7 @@ class CylindricalStereographic(_Cylindrical): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib(default=180, kw_only=True) @@ -546,7 +546,7 @@ class CylindricalEqualArea(_Cylindrical): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -566,7 +566,7 @@ class HammerEqualArea(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -586,7 +586,7 @@ class SinusoidalEqualArea(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -606,7 +606,7 @@ class EckertIVEqualArea(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -626,7 +626,7 @@ class EckertVIEqualArea(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -646,7 +646,7 @@ class Robinson(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -666,7 +666,7 @@ class WinkelTripel(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -686,7 +686,7 @@ class Mollweide(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -706,7 +706,7 @@ class VanDerGrinten(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -732,7 +732,7 @@ class LambertConicConformal(_Conic): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -754,13 +754,13 @@ class Polyconic(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -784,7 +784,7 @@ class Miller(_Miscellaneous): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -808,13 +808,13 @@ class ObliqueMercator1(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -844,7 +844,7 @@ class ObliqueMercator2(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() @@ -852,7 +852,7 @@ class ObliqueMercator2(_Projection): oblique_longitude: float = attr.ib() oblique_latitude: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -882,7 +882,7 @@ class ObliqueMercator3(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib() @@ -890,7 +890,7 @@ class ObliqueMercator3(_Projection): pole_longitude: float = attr.ib() pole_latitude: float = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -916,7 +916,7 @@ class TransverseMercator(_Cylindrical): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ # private; we don't want the user to care or know about @@ -938,11 +938,11 @@ class UniversalTransverseMercator(_Projection): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ zone: str = attr.ib() width: float = attr.ib() - unit: str = attr.ib(default="i") + unit: str = attr.ib(default="c") # private; we don't want the user to care or know about _fmt: str = attr.ib( @@ -968,7 +968,7 @@ class EquidistantCylindrical(_Cylindrical): The figure width. unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. - Default is ``i``. + Default is ``c``. """ central_longitude: float = attr.ib(default=180, kw_only=True) From b9997c17a6d8a2651558724b31d4dceb9977aefb Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sun, 8 Dec 2019 20:32:24 +1100 Subject: [PATCH 18/30] Removed superfluous comments regarding the private variables. --- pygmt/projection.py | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 65177fe217a..a51136acc4c 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -56,7 +56,6 @@ class _Projection: Base class for all projections. """ - # private; we don't want the user to care or know about _fmt: str = attr.ib(init=False, repr=False, default="{_code}") _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) @@ -93,7 +92,6 @@ class _Azimuthal(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -132,7 +130,6 @@ class _Cylindrical(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -169,7 +166,6 @@ class _Conic(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -197,7 +193,6 @@ class _Miscellaneous(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -225,7 +220,6 @@ class LambertAzimuthalEqualArea(_Azimuthal): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="A") @@ -251,7 +245,6 @@ class AzimuthalEquidistant(_Azimuthal): horizon: float = attr.ib(default=180, kw_only=True) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="E") @@ -277,7 +270,6 @@ class AzimuthalGnomic(_Azimuthal): horizon: float = attr.ib(default=60, kw_only=True) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="F") @horizon.validator @@ -311,7 +303,6 @@ class AzimuthalOrthographic(_Azimuthal): horizon: float = attr.ib(default=90) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="G") @horizon.validator @@ -364,7 +355,6 @@ class GeneralPerspective(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -395,7 +385,6 @@ class GeneralSterographic(_Azimuthal): horizon: float = attr.ib(default=90, kw_only=True) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="S") @horizon.validator @@ -429,7 +418,6 @@ class AlbersConicEqualArea(_Conic): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="B") @@ -455,7 +443,6 @@ class EquidistantConic(_Conic): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="JD") @@ -477,7 +464,6 @@ class CassiniCylindrical(_Cylindrical): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="C") @@ -502,7 +488,6 @@ class MercatorCylindrical(_Cylindrical): central_longitude: float = attr.ib(default=180, kw_only=True) central_latitude: float = attr.ib(default=0, kw_only=True) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="M") @@ -527,7 +512,6 @@ class CylindricalStereographic(_Cylindrical): central_longitude: float = attr.ib(default=180, kw_only=True) central_latitude: float = attr.ib(default=0, kw_only=True) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="JCyl_stere/") @@ -549,7 +533,6 @@ class CylindricalEqualArea(_Cylindrical): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="Y") @@ -569,7 +552,6 @@ class HammerEqualArea(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="H") @@ -589,7 +571,6 @@ class SinusoidalEqualArea(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="I") @@ -609,7 +590,6 @@ class EckertIVEqualArea(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="Kf") @@ -629,7 +609,6 @@ class EckertVIEqualArea(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="Ks") @@ -649,7 +628,6 @@ class Robinson(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="N") @@ -669,7 +647,6 @@ class WinkelTripel(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="R") @@ -689,7 +666,6 @@ class Mollweide(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="W") @@ -709,7 +685,6 @@ class VanDerGrinten(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="V") @@ -735,7 +710,6 @@ class LambertConicConformal(_Conic): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="L") @@ -762,7 +736,6 @@ class Polyconic(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -787,7 +760,6 @@ class Miller(_Miscellaneous): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="J") @@ -816,7 +788,6 @@ class ObliqueMercator1(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -854,7 +825,6 @@ class ObliqueMercator2(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -892,7 +862,6 @@ class ObliqueMercator3(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -919,7 +888,6 @@ class TransverseMercator(_Cylindrical): Default is ``c``. """ - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="T") @@ -944,7 +912,6 @@ class UniversalTransverseMercator(_Projection): width: float = attr.ib() unit: str = attr.ib(default="c") - # private; we don't want the user to care or know about _fmt: str = attr.ib( init=False, repr=False, @@ -974,5 +941,4 @@ class EquidistantCylindrical(_Cylindrical): central_longitude: float = attr.ib(default=180, kw_only=True) central_latitude: float = attr.ib(default=0, kw_only=True) - # private; we don't want the user to care or know about _code: str = attr.ib(init=False, repr=False, default="Q") From f92c7d13d8cce07b4698718d4cafd1c5d5f7dfc9 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sun, 8 Dec 2019 20:35:02 +1100 Subject: [PATCH 19/30] Run Black formatting. --- pygmt/projection.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index a51136acc4c..5470da159ba 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -194,9 +194,7 @@ class _Miscellaneous(_Projection): unit: str = attr.ib(default="c") _fmt: str = attr.ib( - init=False, - repr=False, - default="{_code}{central_meridian}/{width}{unit}", + init=False, repr=False, default="{_code}{central_meridian}/{width}{unit}", ) @@ -908,14 +906,13 @@ class UniversalTransverseMercator(_Projection): The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``c``. """ + zone: str = attr.ib() width: float = attr.ib() unit: str = attr.ib(default="c") _fmt: str = attr.ib( - init=False, - repr=False, - default="{_code}{zone}/{width}{unit}", + init=False, repr=False, default="{_code}{zone}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default="U") From 4161f9eab4905020c866cfe11aac93dc2edc5931 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 14 Jan 2020 21:38:42 +1100 Subject: [PATCH 20/30] Update keyword args for the GeneralPerspective projection. --- pygmt/projection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 5470da159ba..25ea9c1cf18 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -348,8 +348,8 @@ class GeneralPerspective(_Projection): azimuth: float = attr.ib() tilt: float = attr.ib() twist: float = attr.ib() - Width: float = attr.ib() - Height: float = attr.ib() + viewport_width: float = attr.ib() + viewport_height: float = attr.ib() width: float = attr.ib() unit: str = attr.ib(default="c") From 968b2cd0d106a79d3c97d9c2c9bdf75421737bf6 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 14 Jan 2020 21:39:29 +1100 Subject: [PATCH 21/30] Initial unittests for the projection class configurations. --- pygmt/tests/test_projections.py | 199 ++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 pygmt/tests/test_projections.py diff --git a/pygmt/tests/test_projections.py b/pygmt/tests/test_projections.py new file mode 100644 index 00000000000..b674f7b4c5b --- /dev/null +++ b/pygmt/tests/test_projections.py @@ -0,0 +1,199 @@ +""" +Test the projection configuration classes. +""" +import pytest + +from .. import projection + + +class TestLambertAzimuthalEqualArea: + """ + Tests for the Lambert Azimuthal Equal Area projection. + """ + + prj = projection.LambertAzimuthalEqualArea( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_horizon(self): + "Test the default value for the horizon" + assert self.prj.horizon == 90 + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "A145/-35/90/12c" + + +class TestAzimuthalEquidistant: + """ + Tests for the Azimuth Equidistant projection. + """ + + prj = projection.AzimuthalEquidistant( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_horizon(self): + "Test the default value for the horizon" + assert self.prj.horizon == 180 + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "E145/-35/180/12c" + + +class TestAzimuthalGnomic: + """ + Tests for the Azimuth Gnomic projection. + """ + + prj = projection.AzimuthalGnomic( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_horizon(self): + "Test the default value for the horizon" + assert self.prj.horizon == 60 + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "F145/-35/60/12c" + + def test_horizon_upper_limit(self): + "Test that the horizon is < 90" + with pytest.raises(ValueError): + projection.AzimuthalGnomic( + central_longitude=145, central_latitude=-35, horizon=90, width=12 + ) + + +class TestAzimuthalOrthographic: + """ + Tests for the Azimuth Orthographic projection. + """ + + prj = projection.AzimuthalOrthographic( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_horizon(self): + "Test the default value for the horizon" + assert self.prj.horizon == 90 + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "G145/-35/90/12c" + + def test_horizon_upper_limit(self): + "Test that the horizon is < 90" + with pytest.raises(ValueError): + projection.AzimuthalOrthographic( + central_longitude=145, central_latitude=-35, horizon=90.0001, width=12 + ) + + +class TestGeneralPerspective: + """ + Tests for the General Perspective projection. + """ + + prj = projection.GeneralPerspective( + central_longitude=145, + central_latitude=-35, + width=12, + altitude=10, + azimuth=270, + tilt=10, + twist=5, + viewport_width=9, + viewport_height=7, + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "G145/-35/10/270/10/5/9/7/12c" + + +class TestGeneralSterographic: + """ + Tests for the General Sterographic projection. + """ + + prj = projection.GeneralSterographic( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_horizon(self): + "Test the default value for the horizon" + assert self.prj.horizon == 90 + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "S145/-35/90/12c" + + def test_horizon_upper_limit(self): + "Test that the horizon is < 180" + with pytest.raises(ValueError): + projection.GeneralSterographic( + central_longitude=145, central_latitude=-35, horizon=180, width=12 + ) + + +class TestAlbersConicEqualArea: + """ + Tests for the Albers Conic Equal Area projection. + """ + + prj = projection.AlbersConicEqualArea( + central_longitude=145, central_latitude=-35, lat1=-30, lat2=-40, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "B145/-35/-30/-40/12c" + + +class TestEquidistantConic: + """ + Tests for the Equidistant Conic projection. + """ + + prj = projection.EquidistantConic( + central_longitude=145, central_latitude=-35, lat1=-30, lat2=-40, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "JD145/-35/-30/-40/12c" From a77fd13a6b691ab4c5b666cee4e4051d020d6efa Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sun, 18 Dec 2022 17:46:48 +1100 Subject: [PATCH 22/30] Added Polar and Linear projections. General cleanup. --- pygmt/projection.py | 296 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 248 insertions(+), 48 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 25ea9c1cf18..7fb2d366a27 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -3,53 +3,10 @@ to create a projection and output a valid GMT projection string. """ -from enum import Enum +import numbers import attr -UNDEFINED = "" - - -class Supported(Enum): - - """ - The supported projections and their GMT code. - """ - - UNDEFINED = "" - LAMBERT_AZIMUTH_EQUAL_AREA = "A" # DONE - ALBERS_CONIC_EQUAL_AREA = "B" # DONE - CASSINI_CYLINDRICAL = "C" # DONE - CYLINDRICAL_STEROGRAPHIC = "JCyl_stere/" # includes `/` according to https://docs.generic-mapping-tools.org/latest/proj_codes.html # DONE - EQUIDISTANT_CONIC = "JD" # DONE - AZIMUTHAL_EQUIDISTANT = "E" # DONE - AZIMUTHAL_GNOMIC = "F" # DONE - AZIMUTHAL_ORTHOGRAPHIC = "G" # DONE - GENERAL_PERSPECTIVE = "G" # DONE - HAMMER_EQUAL_AREA = "H" # DONE - SINUSOIDAL_EQUAL_AREA = "I" # DONE - MILLER_CYLINDRICAL = "J" # DONE - ECKERT_IV_EQUAL_AREA = "Kf" # DONE - ECKERT_VI_EQUAL_AREA = "Ks" # DONE - LAMBERT_CONIC_CONFORMAL = "L" # DONE - MERCATOR_CYLINDRICAL = "M" # DONE - ROBINSON = "N" # DONE - OBLIQUE_MERCATOR_1 = "Oa" # DONE - OBLIQUE_MERCATOR_2 = "Ob" # DONE - OBLIQUE_MERCATOR_3 = "Oc" # DONE - POLAR = "P" - POLYCONIC = "Poly" # DONE - EQUIDISTANT_CYLINDRICAL = "Q" # DONE - WINKEL_TRIPEL = "R" # DONE - GENERAL_STEREOGRAPHIC = "S" # DONE - TRANSVERSE_MERCATOR = "T" # DONE - UNIVERSAL_TRANSVERSE_MERCATOR = "U" # DONE - VAN_DER_GRINTEN = "V" # DONE - MOLLWEIDE = "W" # DONE - LINEAR = "X" - CYLINDRICAL_EQUAL_AREA = "Y" # DONE - - @attr.s() class _Projection: """ @@ -57,13 +14,13 @@ class _Projection: """ _fmt: str = attr.ib(init=False, repr=False, default="{_code}") - _code: str = attr.ib(init=False, repr=False, default=UNDEFINED) + _code: str = attr.ib(init=False, repr=False, default="") def __str__(self): "Convert to the GMT-style projection code." exclude = attr.fields(self.__class__)._fmt kwargs = attr.asdict(self, filter=attr.filters.exclude(exclude)) - return self._fmt.format(**kwargs) + return f"-J{self._fmt.format(**kwargs)}" @attr.s(kw_only=True) @@ -441,7 +398,7 @@ class EquidistantConic(_Conic): Default is ``c``. """ - _code: str = attr.ib(init=False, repr=False, default="JD") + _code: str = attr.ib(init=False, repr=False, default="D") @attr.s(frozen=True) @@ -510,7 +467,7 @@ class CylindricalStereographic(_Cylindrical): central_longitude: float = attr.ib(default=180, kw_only=True) central_latitude: float = attr.ib(default=0, kw_only=True) - _code: str = attr.ib(init=False, repr=False, default="JCyl_stere/") + _code: str = attr.ib(init=False, repr=False, default="Cyl_stere/") @attr.s(frozen=True) @@ -939,3 +896,246 @@ class EquidistantCylindrical(_Cylindrical): central_latitude: float = attr.ib(default=0, kw_only=True) _code: str = attr.ib(init=False, repr=False, default="Q") + + +@attr.s(frozen=True, kw_only=True) +class Polar(_Projection): + """ + Class definition for the Polar projection (theta, radial or r). + + Parameters + ---------- + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``c``. + clockwise : bool + Set to True for azimuths clockwise from North instead of + counter clockwise from East (default). + flip : bool + Set to True to flip the radial direction to point inwards. + flip_options : str | int | float + The string ``e`` indicates that ``r`` represents elevations in degrees. + The string ``p`` will select current planetary radius as maximum radius north. + A numerical value can be used to specify a custom radius. + origin : float + Origin in degrees so the angular value is aligned with the + positive x-axis (or the azimuth to be aligned with the positive + y-axis if theta is clockwise from north). + Angular offset in degrees. Default is 0 (no offset). + offset : float + Radial offset to include in measurement units. Default is 0 (no offset). + depth : bool + To annotate depth rather than radius. Alternatively, if your ``r`` data + are actually depths, then you ca + depth_options : str | int | float + The string ``p`` indicates that your data are actually depths. + A numerical value ti get radial annotations ``r = radius - z`` instead. + + radial : str + Set to ``r`` if radial is elevations in degrees, or ``z`` if + annotations are depth. Default is '' (radius). + """ + + clockwise: bool = attr.ib(default=False, kw_only=True) + flip: bool = attr.ib(default=False, kw_only=True) + flip_options = attr.ib(default="", kw_only=True) + width: float = attr.ib() + unit: str = attr.ib(default="c") + origin: float = attr.ib(default=0, kw_only=True) + offset: float = attr.ib(default=0, kw_only=True) + depth = attr.ib(default=False, kw_only=True) + depth_options = attr.ib(default=False, kw_only=True) + + _code: str = attr.ib(init=False, repr=False, default="P") + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{width}{unit}{_clockwise}{_flip}{_offset}{_origin}{_depth}", + ) + + # the polar projection has a more complicated/specific setup with mixed type + # options. So private fields were necessary to do the post conversions. + _clockwise: str = attr.ib(init=False, repr=False, default="") + _flip: str = attr.ib(init=False, repr=False, default="") + _offset: str = attr.ib(init=False, repr=False, default="") + _origin: str = attr.ib(init=False, repr=False, default="") + _depth: str = attr.ib(init=False, repr=False, default="") + + @flip_options.validator + def check_flip_options(self, attribute, value): + """ + Validate the options that are passed through the flip_options field. + """ + msg = "flip_options must be 'e', 'p' or a number specifying the radius" + if isinstance(value, str): + if value not in ["e", "p", ""]: + raise ValueError(msg) + elif not isinstance(value, numbers.Number): + raise ValueError(msg) + + @depth_options.validator + def check_depth_options(self, attribute, value): + """ + Validate the options that are passed through the depth_options field. + """ + msg = "depth_options must be 'p' or a number specifying the radius" + if isinstance(value, str): + if value != "p": + raise ValueError(msg) + elif not isinstance(value, numbers.Number): + raise ValueError(msg) + + def __attrs_post_init__(self): + """ + For frozen instances, we have to set using the traditonal way + using object.__setattr__(self, key, value). + """ + # cw_value = "+a" if self.clockwise else "" + if self.clockwise: + object.__setattr__(self, "_clockwise", self.clockwise) + + if self.offset: + object.__setattr__(self, "_offset", f"+r{self.offset}") + + if self.origin: + object.__setattr__(self, "_origin", f"+t{self.origin}") + + # flip and depth have an options field + # two options; + # 1. override with an empty str, + # 2. raise an exception if the associated bool is not set to True + + if self.flip: + flip_str = "+f" + + if self.flip_options: + flip_str += f"{self.flip_options}" + else: + object.__setattr__(self, "_flip", "") # override + + if self.depth: + depth_str = "+z" + + if self.depth_options: + depth_str += f"{self.depth_options}" + + object.__setattr__(self, "_depth", depth_str) + else: + object.__setattr__(self, "_depth", "") # override + + +def _time_check(self, attribute, value): + """ + Validate the time field for the linear projection. + """ + msg = "time must be 't' 'T' (relative to TIME_EPOCH or absolute time)." + if isinstance(value, str): + if value not in ["t", "T", ""]: # empty str caters for default value + raise ValueError(msg) + else: + raise ValueError(msg) + + +@attr.s(frozen=True, kw_only=True) +class Linear(_Projection): + """ + Class definition for the linear coordinate transformations. + + Caters for regular floating point coordinates, geographic coordinates + and calendar time coordinates. + Additional scaling transformations include logarithmic and power. + """ + + width: float = attr.ib() + height: float = attr.ib(default=False, kw_only=True) + unit: str = attr.ib(default="c") + geographic: bool = attr.ib(default=False, kw_only=True) + log_x: bool = attr.ib(default=False, kw_only=True) + log_y: bool = attr.ib(default=False, kw_only=True) + power_x: float = attr.ib(default=False, kw_only=True) + power_y: float = attr.ib(default=False, kw_only=True) + time_x: str = attr.ib(default="", kw_only=True, validator=_time_check) + time_y: str = attr.ib(default="", kw_only=True, validator=_time_check) + + _code: str = attr.ib(init=False, repr=False, default="X") + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{width}{unit}{_logx}{_powx}{_timex}{_height}{_logy}{_powy}{_timey}{_geog}", + ) + + # these private fields act as an alias for the main fields so the proj str + # can be be generated from the aliases rather than the + # original fields due to the handling complexity of this proj type + _height: str = attr.ib(init=False, repr=False, default="") + _timex: str = attr.ib(init=False, repr=False, default="") + _timey: str = attr.ib(init=False, repr=False, default="") + _powx: str = attr.ib(init=False, repr=False, default="") + _powy: str = attr.ib(init=False, repr=False, default="") + _logx: str = attr.ib(init=False, repr=False, default="") + _logy: str = attr.ib(init=False, repr=False, default="") + _geog: str = attr.ib(init=False, repr=False, default="") + + def __attrs_post_init__(self): + """ + The linear projection has a lot of options that require more control + and checking after initialisation. + """ + if self.height: + object.__setattr__(self, "_height", f"/{self.height}{self.unit}") + + # docs mention d | g, but the examples showed no difference + if self.geographic: + object.__setattr__(self, "_geog", "d") + + # docs indicate mutual exclusivity for log, power, time for both + # x & y sections + # -JXwidth[l|pexp|T|t][/height[l|pexp|T|t]][d] + if any( + [ + self.log_x and self.power_x, + self.log_x and self.time_x, + self.power_x and self.time_x, + ] + ): + msg = "log_x, power_x and time_x are mutually exclusive" + raise ValueError(msg) + + if any( + [ + self.log_y and self.power_y, + self.log_y and self.time_y, + self.power_y and self.time_y, + ] + ): + msg = "log_y, power_y and time_y are mutually exclusive" + raise ValueError(msg) + + if self.log_y and not self.height: + msg = "height must be defined when applying log scaling" + raise ValueError(msg) + + if self.power_y and not self.height: + msg = "height must be defined when applying power scaling" + raise ValueError(msg) + + # Linear proj has a slightly more complicated str format to control; + if self.log_x: + object.__setattr__(self, "_logx", "l") + + if self.log_y: + object.__setattr__(self, "_logy", "l") + + if self.time_x: + object.__setattr__(self, "_timex", self.time_x) + + if self.time_y: + object.__setattr__(self, "_timey", self.time_y) + + if self.power_x: + object.__setattr__(self, "_powx", f"p{self.power_x}") + + if self.power_y: + object.__setattr__(self, "_powy", f"p{self.power_y}") From d030593ef06d3b6fcfab5d9ba7718077c5aaaea4 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Sun, 18 Dec 2022 17:51:54 +1100 Subject: [PATCH 23/30] Apply black formatting --- pygmt/projection.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 7fb2d366a27..93130711b76 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -151,7 +151,9 @@ class _Miscellaneous(_Projection): unit: str = attr.ib(default="c") _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{central_meridian}/{width}{unit}", + init=False, + repr=False, + default="{_code}{central_meridian}/{width}{unit}", ) @@ -869,7 +871,9 @@ class UniversalTransverseMercator(_Projection): unit: str = attr.ib(default="c") _fmt: str = attr.ib( - init=False, repr=False, default="{_code}{zone}/{width}{unit}", + init=False, + repr=False, + default="{_code}{zone}/{width}{unit}", ) _code: str = attr.ib(init=False, repr=False, default="U") From cd38f61e64819cfaf7290169cf0c5d69495b2b20 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Mon, 19 Dec 2022 20:51:51 +1100 Subject: [PATCH 24/30] Various reconfigs; some projs have updated, updated some that specified optional params. General cleanup. --- pygmt/projection.py | 115 +++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 33 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 93130711b76..1a88ac15c9c 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -4,6 +4,7 @@ """ import numbers +from typing import Union import attr @@ -20,7 +21,7 @@ def __str__(self): "Convert to the GMT-style projection code." exclude = attr.fields(self.__class__)._fmt kwargs = attr.asdict(self, filter=attr.filters.exclude(exclude)) - return f"-J{self._fmt.format(**kwargs)}" + return f"{self._fmt.format(**kwargs)}" @attr.s(kw_only=True) @@ -146,15 +147,25 @@ class _Miscellaneous(_Projection): Default is ``c``. """ - central_meridian: float = attr.ib() + central_meridian: Union[float, str] = attr.ib(default="") width: float = attr.ib() unit: str = attr.ib(default="c") _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_meridian}/{width}{unit}", + default="{_code}{_central_meridian}{width}{unit}", ) + _central_meridian: str = attr.ib(init=False, repr=False, default="") + + def __attrs_post_init__(self): + """Handling the default case; not supplying a central meridian.""" + if self.central_meridian: + cm_fmt = f"{self.central_meridian}/" + else: + cm_fmt = "" + + object.__setattr__(self, "_central_meridian", cm_fmt) @attr.s(frozen=True) @@ -688,17 +699,33 @@ class Polyconic(_Projection): Default is ``c``. """ - central_longitude: float = attr.ib() - central_latitude: float = attr.ib() + # whilst this proj is part of the conic family, the params are different: + # central lon/lat are optionals + # two standard parallels are not defined in the proj code string + central_longitude: float = attr.ib(default=None) + central_latitude: float = attr.ib(default=None) width: float = attr.ib() unit: str = attr.ib(default="c") _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_longitude}/{central_latitude}/{width}{unit}", + default="{_code}{_central_lon}{_central_lat}{width}{unit}", ) - _code: str = attr.ib(init=False, repr=False, default="Poly") + _code: str = attr.ib(init=False, repr=False, default="Poly/") + _central_lon = attr.ib(init=False, repr=False, default="") + _central_lat = attr.ib(init=False, repr=False, default="") + + def __attrs_post_init__(self): + """ + For frozen instances, we have to set using the traditonal way + using object.__setattr__(self, key, value). + """ + if self.central_longitude: + object.__setattr__(self, "_central_lon", f"{self.central_longitude}/") + + if self.central_latitude: + object.__setattr__(self, "_central_lat", f"{self.central_latitude}/") @attr.s(frozen=True) @@ -717,6 +744,8 @@ class Miller(_Miscellaneous): Default is ``c``. """ + # a cylindrical proj, but we're basing of miscellaneous as the + # standard parallel param isn't defined in the code string for Miller _code: str = attr.ib(init=False, repr=False, default="J") @@ -738,19 +767,42 @@ class ObliqueMercator1(_Projection): unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``c``. + allow_southern_hemisphere : bool + If set to True, then allow projection poles in the southern hemisphere. + Default is to map any such poles to their antipodes in the northern + hemisphere. + align_yaxis : bool + If set to True, then align the oblique with the y-axis. + Default is to align with the x-axis. """ central_longitude: float = attr.ib() central_latitude: float = attr.ib() + azimuth: float = attr.ib() width: float = attr.ib() unit: str = attr.ib(default="c") + allow_southern_hemisphere: bool = attr.ib(default=False) + align_yaxis: bool = attr.ib(default=False) _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_longitude}/{central_latitude}/{azimuth}/{width}{unit}", + default="{_code}{_sth_hem}{central_longitude}/{central_latitude}/{azimuth}/{width}{unit}{_align_y}", ) - _code: str = attr.ib(init=False, repr=False, default="Oa") + _code: str = attr.ib(init=False, repr=False, default="O") + _sth_hem: str = attr.ib(init=False, repr=False, default="") + _align_y: str = attr.ib(init=False, repr=False, default="") + + def __attrs_post_init__(self): + """ + For frozen instances, we have to set using the traditonal way + using object.__setattr__(self, key, value). + """ + if self.allow_southern_hemisphere: + object.__setattr__(self, "_sth_hem", "A") + + if self.align_yaxis: + object.__setattr__(self, "_align_y", "+v") @attr.s(frozen=True, kw_only=True) @@ -936,21 +988,17 @@ class Polar(_Projection): depth_options : str | int | float The string ``p`` indicates that your data are actually depths. A numerical value ti get radial annotations ``r = radius - z`` instead. - - radial : str - Set to ``r`` if radial is elevations in degrees, or ``z`` if - annotations are depth. Default is '' (radius). """ - clockwise: bool = attr.ib(default=False, kw_only=True) - flip: bool = attr.ib(default=False, kw_only=True) - flip_options = attr.ib(default="", kw_only=True) + clockwise: bool = attr.ib(default=False) + flip: bool = attr.ib(default=False) + flip_options = attr.ib(default="") width: float = attr.ib() unit: str = attr.ib(default="c") - origin: float = attr.ib(default=0, kw_only=True) - offset: float = attr.ib(default=0, kw_only=True) - depth = attr.ib(default=False, kw_only=True) - depth_options = attr.ib(default=False, kw_only=True) + origin: float = attr.ib(default=0) + offset: float = attr.ib(default=0) + depth: bool = attr.ib(default=False) + depth_options = attr.ib(default=False) _code: str = attr.ib(init=False, repr=False, default="P") _fmt: str = attr.ib( @@ -996,9 +1044,8 @@ def __attrs_post_init__(self): For frozen instances, we have to set using the traditonal way using object.__setattr__(self, key, value). """ - # cw_value = "+a" if self.clockwise else "" if self.clockwise: - object.__setattr__(self, "_clockwise", self.clockwise) + object.__setattr__(self, "_clockwise", "+a") if self.offset: object.__setattr__(self, "_offset", f"+r{self.offset}") @@ -1007,8 +1054,8 @@ def __attrs_post_init__(self): object.__setattr__(self, "_origin", f"+t{self.origin}") # flip and depth have an options field - # two options; - # 1. override with an empty str, + # two options if the user has provided options without depth=True; + # 1. override user input with an empty str, # 2. raise an exception if the associated bool is not set to True if self.flip: @@ -1016,6 +1063,8 @@ def __attrs_post_init__(self): if self.flip_options: flip_str += f"{self.flip_options}" + + object.__setattr__(self, "_flip", flip_str) else: object.__setattr__(self, "_flip", "") # override @@ -1034,7 +1083,7 @@ def _time_check(self, attribute, value): """ Validate the time field for the linear projection. """ - msg = "time must be 't' 'T' (relative to TIME_EPOCH or absolute time)." + msg = "time must be 't' or 'T' (relative to TIME_EPOCH or absolute time)." if isinstance(value, str): if value not in ["t", "T", ""]: # empty str caters for default value raise ValueError(msg) @@ -1053,15 +1102,15 @@ class Linear(_Projection): """ width: float = attr.ib() - height: float = attr.ib(default=False, kw_only=True) + height: float = attr.ib(default=False) unit: str = attr.ib(default="c") - geographic: bool = attr.ib(default=False, kw_only=True) - log_x: bool = attr.ib(default=False, kw_only=True) - log_y: bool = attr.ib(default=False, kw_only=True) - power_x: float = attr.ib(default=False, kw_only=True) - power_y: float = attr.ib(default=False, kw_only=True) - time_x: str = attr.ib(default="", kw_only=True, validator=_time_check) - time_y: str = attr.ib(default="", kw_only=True, validator=_time_check) + geographic: bool = attr.ib(default=False) + log_x: bool = attr.ib(default=False) + log_y: bool = attr.ib(default=False) + power_x: float = attr.ib(default=False) + power_y: float = attr.ib(default=False) + time_x: str = attr.ib(default="", validator=_time_check) + time_y: str = attr.ib(default="", validator=_time_check) _code: str = attr.ib(init=False, repr=False, default="X") _fmt: str = attr.ib( From 47931553a184d22be2683f348629c164f01ec67e Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Mon, 19 Dec 2022 20:52:28 +1100 Subject: [PATCH 25/30] Added a bunch more projections to the test suite. --- pygmt/tests/test_projections.py | 416 +++++++++++++++++++++++++++++++- 1 file changed, 415 insertions(+), 1 deletion(-) diff --git a/pygmt/tests/test_projections.py b/pygmt/tests/test_projections.py index b674f7b4c5b..9c3374df69e 100644 --- a/pygmt/tests/test_projections.py +++ b/pygmt/tests/test_projections.py @@ -196,4 +196,418 @@ def test_default_unit(self): def test_string_conversion(self): "Test the string representation of the projection class" - assert str(self.prj) == "JD145/-35/-30/-40/12c" + assert str(self.prj) == "D145/-35/-30/-40/12c" + + +class TestCassiniCylindrical: + """ + Tests for the Cassini Cylindrical projection. + """ + + prj = projection.CassiniCylindrical( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "C145/-35/12c" + + +class TestMercatorCylindrical: + """ + Tests for the Mercator Cylindrical projection. + """ + + prj = projection.MercatorCylindrical( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "M145/-35/12c" + + +class TestCylindricalStereographic: + """ + Tests for the Cylindrical Stereographic projection. + """ + + prj = projection.CylindricalStereographic( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "Cyl_stere/145/-35/12c" + + +class TestCylindricalEqualArea: + """ + Tests for the Cylindrical Equal Area projection. + """ + + prj = projection.CylindricalEqualArea( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "Y145/-35/12c" + + +class TestHammerEqualArea: + """ + Tests for the Hammer Equal Area projection. + """ + + prj1 = projection.HammerEqualArea(central_meridian=145, width=12) + prj2 = projection.HammerEqualArea(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "H145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "H12c" + + +class TestSinusoidalEqualArea: + """ + Tests for the Sinusoidal Equal Area projection. + """ + + prj1 = projection.SinusoidalEqualArea(central_meridian=145, width=12) + prj2 = projection.SinusoidalEqualArea(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "I145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "I12c" + + +class TestEckertIVEqualArea: + """ + Tests for the Eckert IV Equal Area projection. + """ + + prj1 = projection.EckertIVEqualArea(central_meridian=145, width=12) + prj2 = projection.EckertIVEqualArea(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "Kf145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "Kf12c" + + +class TestEckertVIEqualArea: + """ + Tests for the Eckert VI Equal Area projection. + """ + + prj1 = projection.EckertVIEqualArea(central_meridian=145, width=12) + prj2 = projection.EckertVIEqualArea(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "Ks145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "Ks12c" + + +class TestRobinson: + """ + Tests for the Robinson projection. + """ + + prj1 = projection.Robinson(central_meridian=145, width=12) + prj2 = projection.Robinson(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "N145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "N12c" + + +class TestWinkelTripel: + """ + Tests for the Winkel Tripel projection. + """ + + prj1 = projection.WinkelTripel(central_meridian=145, width=12) + prj2 = projection.WinkelTripel(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "R145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "R12c" + + +class TestMollweide: + """ + Tests for the Mollweide projection. + """ + + prj1 = projection.Mollweide(central_meridian=145, width=12) + prj2 = projection.Mollweide(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "W145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "W12c" + + +class TestVanDerGrinten: + """ + Tests for the Van Der Grinten projection. + """ + + prj1 = projection.VanDerGrinten(central_meridian=145, width=12) + prj2 = projection.VanDerGrinten(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "V145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "V12c" + + +class TestLambertConicConformal: + """ + Tests for the Lambert Conic Conformal projection. + """ + + prj = projection.LambertConicConformal( + central_longitude=145, central_latitude=-35, lat1=-30, lat2=-40, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj.unit == "c" + + def test_string_conversion(self): + "Test the string representation of the projection class" + assert str(self.prj) == "L145/-35/-30/-40/12c" + + +class TestPolyconic: + """ + Tests for the Polyconic projection. + """ + + prj1 = projection.Polyconic( + central_longitude=145, central_latitude=-35, width=12 + ) + prj2 = projection.Polyconic(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "Poly/145/-35/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "Poly/12c" + + +class TestMiller: + """ + Tests for the Miller projection. + """ + + prj1 = projection.Miller(central_meridian=145, width=12) + prj2 = projection.Miller(width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "J145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "J12c" + + +class TestPolar: + """ + Tests for the Polar projection. + """ + + prj1 = projection.Polar(width=10) + prj2 = projection.Polar(width=10, clockwise=True, origin=45, offset=10) + prj3 = projection.Polar(width=10, flip=True, flip_options=33) + prj4 = projection.Polar(width=10, depth=True, depth_options=33) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the polar projection class" + assert str(self.prj1) == "P10c" + + def test_string_conversion2(self): + "Test the string representation of the polar projection class" + assert str(self.prj1) == "P10c+a+r10+t45" + + def test_string_conversion3(self): + "Test the string representation of the polar projection class" + assert str(self.prj1) == "P10c+f33" + + def test_string_conversion4(self): + "Test the string representation of the polar projection class" + assert str(self.prj1) == "P10c+z33" + + def test_assert_depth_options(self): + "Test that a ValueError assertion is raised for the depth options" + with pytest.raises(ValueError): + projection.Polar(width=10, depth=True, depth_options="ep") + + def test_assert_flip_options(self): + "Test that a ValueError assertion is raised for the flip options" + with pytest.raises(ValueError): + projection.Polar(width=10, flip=True, flip_options="ep") + + +class TestLinear: + """ + Tests for the Linear projection. + """ + + prj1 = projection.Linear(width=10) + prj2 = projection.Linear(width=10, geographic=True) + prj3 = projection.Linear(width=10, log_x=True) + prj4 = projection.Linear(width=10, log_x=True, height=35, log_y=True) + prj5 = projection.Linear(width=10, power_x=2, height=35, power_y=3) + prj6 = projection.Linear(width=10, time_x="t", height=35, power_y=3) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the linear projection class" + assert str(self.prj1) == "X10c" + + def test_string_conversion2(self): + "Test the string representation of the linear projection class" + assert str(self.prj2) == "X10cd" + + def test_string_conversion3(self): + "Test the string representation of the linear projection class" + assert str(self.prj3) == "X10cl" + + def test_string_conversion4(self): + "Test the string representation of the linear projection class" + assert str(self.prj4) == "X10cl/35cl" + + def test_string_conversion5(self): + "Test the string representation of the linear projection class" + assert str(self.prj5) == "X10cp0.5/35cp3" + + def test_string_conversion6(self): + "Test the string representation of the linear projection class" + assert str(self.prj6) == "X10ct/35cp3" + + def test_assert_log_y(self): + "Test that setting log_y without setting height raises a ValueError" + with pytest.raises(ValueError, match=r"height .* log scaling"): + projection.Linear(width=10, log_x=True, log_y=True) + + def test_assert_power_y(self): + "Test that setting power_y without setting height raises a ValueError" + with pytest.raises(ValueError, match=r"height .* power scaling"): + projection.Linear(width=10, power_y=True) + + def test_assert_time_code(self): + "Test that setting an incorrect time code raises a ValueError" + with pytest.raises(ValueError): + projection.Linear(width=10, time_x="s") + + def test_assert_log_power(self): + "Test that setting both log_x and power_x keywords raises a ValueError" + with pytest.raises(ValueError, match=r".* are mutually exclusive"): + projection.Linear(width=10, log_x=True, power_x=0.5) + + def test_assert_power_time(self): + "Test that setting both power_x and time_x keywords raises a ValueError" + with pytest.raises(ValueError, match=r".* are mutually exclusive"): + projection.Linear(width=10, power_x=0.5, time_x="t") + + def test_assert_log_time(self): + "Test that setting both log_x and time_x keywords raises a ValueError" + with pytest.raises(ValueError, match=r".* are mutually exclusive"): + projection.Linear(width=10, log_x=True, time_x="t") From 0f24da268a4a354dffe3a05625ee6dc6ac5b1b05 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 20 Dec 2022 20:52:34 +1100 Subject: [PATCH 26/30] Reworked the cylindrical projections to cater for the default and non-default params for the different cylindrical projections. --- pygmt/projection.py | 202 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 160 insertions(+), 42 deletions(-) diff --git a/pygmt/projection.py b/pygmt/projection.py index 1a88ac15c9c..0d3eb9865c1 100644 --- a/pygmt/projection.py +++ b/pygmt/projection.py @@ -66,7 +66,7 @@ def check_horizon(self, attribute, value): @attr.s(kw_only=True) -class _Cylindrical(_Projection): +class _CylindricalRequired(_Projection): """ Base class for cylindrical projections. @@ -95,6 +95,59 @@ class _Cylindrical(_Projection): ) +@attr.s(kw_only=True) +class _CylindricalOptionals(_Projection): + """ + Base class for cylindrical projections. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``c``. + """ + + central_longitude: float = attr.ib(default=None) + central_latitude: float = attr.ib(default=None) + width: float = attr.ib() + unit: str = attr.ib(default="c") + + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{_lon0}{_lat0}{width}{unit}", + ) + _lon0: str = attr.ib(init=False, repr=False, default="") + _lat0: str = attr.ib(init=False, repr=False, default="") + + @central_latitude.validator + def check_lon0(self, attribute, value): + """ + If supplying the central latitude, then the central longitude is required. + """ + msg = "central_longitude must be defined when defining central_latitude" + if self.central_longitude is None and self.central_latitude is not None: + raise ValueError(msg) + + def __attrs_post_init__(self): + """ + The central longitude and latitude are optionals for some of the + cylindrical projections. This work around is to preserve the + original behaviour. + """ + if self.central_longitude: + object.__setattr__(self, "_lon0", f"{self.central_longitude}/") + + if self.central_latitude: + object.__setattr__(self, "_lat0", f"{self.central_latitude}/") + + @attr.s(kw_only=True) class _Conic(_Projection): """ @@ -168,6 +221,48 @@ def __attrs_post_init__(self): object.__setattr__(self, "_central_meridian", cm_fmt) +@attr.s(frozen=True, kw_only=True) +class _ObliqueMercator(_Projection): + """ + Base class for the Oblique Mercator projection which has 3 config options. + + Parameters + ---------- + central_longitude : float + The longitude of the projection centre. + central_latitude : float + The latitude of the projection centre. + width : float + The figure width. + unit : str + The unit for the figure width in ``i`` for inch, ``c`` for centimetre. + Default is ``c``. + allow_southern_hemisphere : bool + If set to True, then allow projection poles in the southern hemisphere. + Default is to map any such poles to their antipodes in the northern + hemisphere. + align_yaxis : bool + If set to True, then align the oblique with the y-axis. + Default is to align with the x-axis. + """ + + central_longitude: float = attr.ib() + central_latitude: float = attr.ib() + width: float = attr.ib() + unit: str = attr.ib(default="c") + allow_southern_hemisphere: bool = attr.ib(default=False) + align_yaxis: bool = attr.ib(default=False) + + _fmt: str = attr.ib( + init=False, + repr=False, + default="", + ) + _code: str = attr.ib(init=False, repr=False, default="O") + _sth_hem: str = attr.ib(init=False, repr=False, default="") + _align_y: str = attr.ib(init=False, repr=False, default="") + + @attr.s(frozen=True) class LambertAzimuthalEqualArea(_Azimuthal): """ @@ -415,7 +510,7 @@ class EquidistantConic(_Conic): @attr.s(frozen=True) -class CassiniCylindrical(_Cylindrical): +class CassiniCylindrical(_CylindricalRequired): """ Class definition for the Cassini cylindrical projection. @@ -436,7 +531,7 @@ class CassiniCylindrical(_Cylindrical): @attr.s(frozen=True) -class MercatorCylindrical(_Cylindrical): +class MercatorCylindrical(_CylindricalOptionals): """ Class definition for the Mercator cylindrical projection. @@ -453,14 +548,11 @@ class MercatorCylindrical(_Cylindrical): Default is ``c``. """ - central_longitude: float = attr.ib(default=180, kw_only=True) - central_latitude: float = attr.ib(default=0, kw_only=True) - _code: str = attr.ib(init=False, repr=False, default="M") @attr.s(frozen=True) -class CylindricalStereographic(_Cylindrical): +class CylindricalStereographic(_CylindricalOptionals): """ Class definition for the cylindrical stereographic projection. @@ -477,14 +569,11 @@ class CylindricalStereographic(_Cylindrical): Default is ``c``. """ - central_longitude: float = attr.ib(default=180, kw_only=True) - central_latitude: float = attr.ib(default=0, kw_only=True) - _code: str = attr.ib(init=False, repr=False, default="Cyl_stere/") @attr.s(frozen=True) -class CylindricalEqualArea(_Cylindrical): +class CylindricalEqualArea(_CylindricalOptionals): """ Class definition for the cylindrical equal area projection. @@ -750,7 +839,7 @@ class Miller(_Miscellaneous): @attr.s(frozen=True, kw_only=True) -class ObliqueMercator1(_Projection): +class ObliqueMercator1(_ObliqueMercator): """ Class definition for the oblique Mercator 1 projection. @@ -776,22 +865,13 @@ class ObliqueMercator1(_Projection): Default is to align with the x-axis. """ - central_longitude: float = attr.ib() - central_latitude: float = attr.ib() azimuth: float = attr.ib() - width: float = attr.ib() - unit: str = attr.ib(default="c") - allow_southern_hemisphere: bool = attr.ib(default=False) - align_yaxis: bool = attr.ib(default=False) _fmt: str = attr.ib( init=False, repr=False, default="{_code}{_sth_hem}{central_longitude}/{central_latitude}/{azimuth}/{width}{unit}{_align_y}", ) - _code: str = attr.ib(init=False, repr=False, default="O") - _sth_hem: str = attr.ib(init=False, repr=False, default="") - _align_y: str = attr.ib(init=False, repr=False, default="") def __attrs_post_init__(self): """ @@ -806,7 +886,7 @@ def __attrs_post_init__(self): @attr.s(frozen=True, kw_only=True) -class ObliqueMercator2(_Projection): +class ObliqueMercator2(_ObliqueMercator): """ Class definition for the oblique Mercator 2 projection. @@ -825,25 +905,38 @@ class ObliqueMercator2(_Projection): unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``c``. + allow_southern_hemisphere : bool + If set to True, then allow projection poles in the southern hemisphere. + Default is to map any such poles to their antipodes in the northern + hemisphere. + align_yaxis : bool + If set to True, then align the oblique with the y-axis. + Default is to align with the x-axis. """ - central_longitude: float = attr.ib() - central_latitude: float = attr.ib() oblique_longitude: float = attr.ib() oblique_latitude: float = attr.ib() - width: float = attr.ib() - unit: str = attr.ib(default="c") _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_longitude}/{central_latitude}/{oblique_longitude}/{oblique_latitude}/{width}{unit}", + default="{_code}{_sth_hem}{central_longitude}/{central_latitude}/{oblique_longitude}/{oblique_latitude}/{width}{unit}{_align_y}", ) - _code: str = attr.ib(init=False, repr=False, default="Ob") + + def __attrs_post_init__(self): + """ + For frozen instances, we have to set using the traditonal way + using object.__setattr__(self, key, value). + """ + if self.allow_southern_hemisphere: + object.__setattr__(self, "_sth_hem", "B") + + if self.align_yaxis: + object.__setattr__(self, "_align_y", "+v") @attr.s(frozen=True, kw_only=True) -class ObliqueMercator3(_Projection): +class ObliqueMercator3(_ObliqueMercator): """ Class definition for the oblique Mercator 3 projection. @@ -862,25 +955,38 @@ class ObliqueMercator3(_Projection): unit : str The unit for the figure width in ``i`` for inch, ``c`` for centimetre. Default is ``c``. + allow_southern_hemisphere : bool + If set to True, then allow projection poles in the southern hemisphere. + Default is to map any such poles to their antipodes in the northern + hemisphere. + align_yaxis : bool + If set to True, then align the oblique with the y-axis. + Default is to align with the x-axis. """ - central_longitude: float = attr.ib() - central_latitude: float = attr.ib() pole_longitude: float = attr.ib() pole_latitude: float = attr.ib() - width: float = attr.ib() - unit: str = attr.ib(default="c") _fmt: str = attr.ib( init=False, repr=False, - default="{_code}{central_longitude}/{central_latitude}/{pole_longitude}/{pole_latitude}/{width}{unit}", + default="{_code}{_sth_hem}{central_longitude}/{central_latitude}/{pole_longitude}/{pole_latitude}/{width}{unit}{_align_y}", ) - _code: str = attr.ib(init=False, repr=False, default="Oc") + + def __attrs_post_init__(self): + """ + For frozen instances, we have to set using the traditonal way + using object.__setattr__(self, key, value). + """ + if self.allow_southern_hemisphere: + object.__setattr__(self, "_sth_hem", "C") + + if self.align_yaxis: + object.__setattr__(self, "_align_y", "+v") @attr.s(frozen=True) -class TransverseMercator(_Cylindrical): +class TransverseMercator(_CylindricalRequired): """ Class definition for the Transverse Mercator projection. @@ -897,7 +1003,22 @@ class TransverseMercator(_Cylindrical): Default is ``c``. """ + central_latitude: float = attr.ib(default=None) + _code: str = attr.ib(init=False, repr=False, default="T") + _fmt: str = attr.ib( + init=False, + repr=False, + default="{_code}{central_longitude}/{_lat0}{width}{unit}", + ) + _lat0: str = attr.ib(init=False, repr=False, default="") + + def __attrs_post_init__(self): + """ + The transverse mercator has the central meridan as an optional. + """ + if self.central_latitude: + object.__setattr__(self, "_lat0", f"{self.central_latitude}/") @attr.s(frozen=True, kw_only=True) @@ -931,16 +1052,16 @@ class UniversalTransverseMercator(_Projection): @attr.s(frozen=True) -class EquidistantCylindrical(_Cylindrical): +class EquidistantCylindrical(_CylindricalOptionals): """ Class definition for the equidistant cylindrical projection. Parameters ---------- central_longitude : float - The longitude of the projection centre. Default is 180. + The longitude of the projection centre. central_latitude : float - The latitude of the projection centre. Default is 0. + The latitude of the projection centre. width : float The figure width. unit : str @@ -948,9 +1069,6 @@ class EquidistantCylindrical(_Cylindrical): Default is ``c``. """ - central_longitude: float = attr.ib(default=180, kw_only=True) - central_latitude: float = attr.ib(default=0, kw_only=True) - _code: str = attr.ib(init=False, repr=False, default="Q") From a43d4c8940b90955f5da4707039cf9064aee167d Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Tue, 20 Dec 2022 20:53:18 +1100 Subject: [PATCH 27/30] Added tests for the 3 oblique mercator projection options. --- pygmt/tests/test_projections.py | 93 +++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/pygmt/tests/test_projections.py b/pygmt/tests/test_projections.py index 9c3374df69e..28389bbd6d1 100644 --- a/pygmt/tests/test_projections.py +++ b/pygmt/tests/test_projections.py @@ -501,6 +501,99 @@ def test_string_conversion2(self): assert str(self.prj2) == "J12c" +class TestObliqueMercator1: + """ + Tests for the Oblique Mercator projection (option 1). + """ + prj1 = projection.ObliqueMercator1( + central_longitude=145, central_latitude=-35, azimuth=45, width=12 + ) + prj2 = projection.ObliqueMercator1( + central_longitude=145, central_latitude=-35, azimuth=45, allow_southern_hemisphere=True, width=12 + ) + prj3 = projection.ObliqueMercator1( + central_longitude=145, central_latitude=-35, azimuth=45, align_yaxis=True, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "O145/-35/45/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "OA145/-35/45/12c" + + def test_string_conversion3(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "O145/-35/45/12c+v" + + +class TestObliqueMercator2: + """ + Tests for the Oblique Mercator projection (option 2). + """ + prj1 = projection.ObliqueMercator2( + central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, width=12 + ) + prj2 = projection.ObliqueMercator2( + central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, allow_southern_hemisphere=True, width=12 + ) + prj3 = projection.ObliqueMercator2( + central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, align_yaxis=True, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "O145/-35/110/-20/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "OB145/-35/110/-20/12c" + + def test_string_conversion3(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "O145/-35/110/-20/12c+v" + + +class TestObliqueMercator3: + """ + Tests for the Oblique Mercator projection (option 3). + """ + prj1 = projection.ObliqueMercator3( + central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, width=12 + ) + prj2 = projection.ObliqueMercator3( + central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, allow_southern_hemisphere=True, width=12 + ) + prj3 = projection.ObliqueMercator3( + central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, align_yaxis=True, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "O145/-35/110/-20/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "OC145/-35/110/-20/12c" + + def test_string_conversion3(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "O145/-35/110/-20/12c+v" + + class TestPolar: """ Tests for the Polar projection. From f39053aa2492ee24c77c5a0019a58a949afaedc8 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Wed, 21 Dec 2022 16:26:36 +1100 Subject: [PATCH 28/30] Added tests for UTM, mercator, equidistant cylindrical. Minor additions to other projection tests. --- pygmt/tests/test_projections.py | 112 ++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 12 deletions(-) diff --git a/pygmt/tests/test_projections.py b/pygmt/tests/test_projections.py index 28389bbd6d1..9fe97c5b396 100644 --- a/pygmt/tests/test_projections.py +++ b/pygmt/tests/test_projections.py @@ -222,17 +222,22 @@ class TestMercatorCylindrical: Tests for the Mercator Cylindrical projection. """ - prj = projection.MercatorCylindrical( + prj1 = projection.MercatorCylindrical( central_longitude=145, central_latitude=-35, width=12 ) + prj2 = projection.MercatorCylindrical(width=12) def test_default_unit(self): "Test the default value for the figure units" - assert self.prj.unit == "c" + assert self.prj1.unit == "c" - def test_string_conversion(self): + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "M145/-35/12c" + + def test_string_conversion2(self): "Test the string representation of the projection class" - assert str(self.prj) == "M145/-35/12c" + assert str(self.prj2) == "M12c" class TestCylindricalStereographic: @@ -240,17 +245,22 @@ class TestCylindricalStereographic: Tests for the Cylindrical Stereographic projection. """ - prj = projection.CylindricalStereographic( + prj1 = projection.CylindricalStereographic( central_longitude=145, central_latitude=-35, width=12 ) + prj2 = projection.CylindricalStereographic(width=12) def test_default_unit(self): "Test the default value for the figure units" - assert self.prj.unit == "c" + assert self.prj1.unit == "c" - def test_string_conversion(self): + def test_string_conversion1(self): "Test the string representation of the projection class" - assert str(self.prj) == "Cyl_stere/145/-35/12c" + assert str(self.prj1) == "Cyl_stere/145/-35/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "Cyl_stere/12c" class TestCylindricalEqualArea: @@ -258,17 +268,22 @@ class TestCylindricalEqualArea: Tests for the Cylindrical Equal Area projection. """ - prj = projection.CylindricalEqualArea( + prj1 = projection.CylindricalEqualArea( central_longitude=145, central_latitude=-35, width=12 ) + prj2 = projection.CylindricalEqualArea(width=12) def test_default_unit(self): "Test the default value for the figure units" - assert self.prj.unit == "c" + assert self.prj1.unit == "c" - def test_string_conversion(self): + def test_string_conversion1(self): "Test the string representation of the projection class" - assert str(self.prj) == "Y145/-35/12c" + assert str(self.prj1) == "Y145/-35/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "Y12c" class TestHammerEqualArea: @@ -505,6 +520,7 @@ class TestObliqueMercator1: """ Tests for the Oblique Mercator projection (option 1). """ + prj1 = projection.ObliqueMercator1( central_longitude=145, central_latitude=-35, azimuth=45, width=12 ) @@ -536,6 +552,7 @@ class TestObliqueMercator2: """ Tests for the Oblique Mercator projection (option 2). """ + prj1 = projection.ObliqueMercator2( central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, width=12 ) @@ -567,6 +584,7 @@ class TestObliqueMercator3: """ Tests for the Oblique Mercator projection (option 3). """ + prj1 = projection.ObliqueMercator3( central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, width=12 ) @@ -594,6 +612,76 @@ def test_string_conversion3(self): assert str(self.prj2) == "O145/-35/110/-20/12c+v" +class TestTransverseMercator: + """ + Tests for the Transverse Mercator projection. + """ + + prj1 = projection.TransverseMercator(central_longitude=145, width=12) + prj2 = projection.TransverseMercator( + central_longitude=145, central_latitude=-35, width=12 + ) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "T145/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "T145/-35/12c" + + +class TestUniversalTransverseMercator: + """ + Tests for the Universal Transverse Mercator projection. + """ + + prj1 = projection.UniversalTransverseMercator(zone="-55", width=12) + prj2 = projection.UniversalTransverseMercator(zone="55H", width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "U-55/12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "U55H/12c" + + +class TestEquidistantCylindrical: + """ + Tests for the Equidistant Cylindrical projection. + """ + + prj1 = projection.EquidistantCylindrical(width=12) + prj2 = projection.EquidistantCylindrical(central_longitude=145, width=12) + prj3 = projection.EquidistantCylindrical(central_longitude=145, central_latitude=-35, width=12) + + def test_default_unit(self): + "Test the default value for the figure units" + assert self.prj1.unit == "c" + + def test_string_conversion1(self): + "Test the string representation of the projection class" + assert str(self.prj1) == "Q12c" + + def test_string_conversion2(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "Q145/12c" + + def test_string_conversion3(self): + "Test the string representation of the projection class" + assert str(self.prj2) == "Q145/-35/12c" + + class TestPolar: """ Tests for the Polar projection. From f386b091fe2f68b0a4bc5e42757c1e9214073338 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Wed, 21 Dec 2022 16:58:45 +1100 Subject: [PATCH 29/30] Applied black formatting. --- pygmt/tests/test_projections.py | 60 ++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/pygmt/tests/test_projections.py b/pygmt/tests/test_projections.py index 9fe97c5b396..4bdca57ccad 100644 --- a/pygmt/tests/test_projections.py +++ b/pygmt/tests/test_projections.py @@ -477,9 +477,7 @@ class TestPolyconic: Tests for the Polyconic projection. """ - prj1 = projection.Polyconic( - central_longitude=145, central_latitude=-35, width=12 - ) + prj1 = projection.Polyconic(central_longitude=145, central_latitude=-35, width=12) prj2 = projection.Polyconic(width=12) def test_default_unit(self): @@ -525,10 +523,18 @@ class TestObliqueMercator1: central_longitude=145, central_latitude=-35, azimuth=45, width=12 ) prj2 = projection.ObliqueMercator1( - central_longitude=145, central_latitude=-35, azimuth=45, allow_southern_hemisphere=True, width=12 + central_longitude=145, + central_latitude=-35, + azimuth=45, + allow_southern_hemisphere=True, + width=12, ) prj3 = projection.ObliqueMercator1( - central_longitude=145, central_latitude=-35, azimuth=45, align_yaxis=True, width=12 + central_longitude=145, + central_latitude=-35, + azimuth=45, + align_yaxis=True, + width=12, ) def test_default_unit(self): @@ -554,13 +560,27 @@ class TestObliqueMercator2: """ prj1 = projection.ObliqueMercator2( - central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, width=12 + central_longitude=145, + central_latitude=-35, + oblique_longitude=110, + oblique_latitude=-20, + width=12, ) prj2 = projection.ObliqueMercator2( - central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, allow_southern_hemisphere=True, width=12 + central_longitude=145, + central_latitude=-35, + oblique_longitude=110, + oblique_latitude=-20, + allow_southern_hemisphere=True, + width=12, ) prj3 = projection.ObliqueMercator2( - central_longitude=145, central_latitude=-35, oblique_longitude=110, oblique_latitude=-20, align_yaxis=True, width=12 + central_longitude=145, + central_latitude=-35, + oblique_longitude=110, + oblique_latitude=-20, + align_yaxis=True, + width=12, ) def test_default_unit(self): @@ -586,13 +606,27 @@ class TestObliqueMercator3: """ prj1 = projection.ObliqueMercator3( - central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, width=12 + central_longitude=145, + central_latitude=-35, + pole_longitude=110, + pole_latitude=-20, + width=12, ) prj2 = projection.ObliqueMercator3( - central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, allow_southern_hemisphere=True, width=12 + central_longitude=145, + central_latitude=-35, + pole_longitude=110, + pole_latitude=-20, + allow_southern_hemisphere=True, + width=12, ) prj3 = projection.ObliqueMercator3( - central_longitude=145, central_latitude=-35, pole_longitude=110, pole_latitude=-20, align_yaxis=True, width=12 + central_longitude=145, + central_latitude=-35, + pole_longitude=110, + pole_latitude=-20, + align_yaxis=True, + width=12, ) def test_default_unit(self): @@ -663,7 +697,9 @@ class TestEquidistantCylindrical: prj1 = projection.EquidistantCylindrical(width=12) prj2 = projection.EquidistantCylindrical(central_longitude=145, width=12) - prj3 = projection.EquidistantCylindrical(central_longitude=145, central_latitude=-35, width=12) + prj3 = projection.EquidistantCylindrical( + central_longitude=145, central_latitude=-35, width=12 + ) def test_default_unit(self): "Test the default value for the figure units" From 20693e70598c6810db3d0f04f1c317d841d1ea98 Mon Sep 17 00:00:00 2001 From: Josh Sixsmith Date: Fri, 23 Dec 2022 15:45:07 +1100 Subject: [PATCH 30/30] Caught test fails and updated. --- pygmt/tests/test_projections.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pygmt/tests/test_projections.py b/pygmt/tests/test_projections.py index 4bdca57ccad..18a570e365c 100644 --- a/pygmt/tests/test_projections.py +++ b/pygmt/tests/test_projections.py @@ -283,7 +283,7 @@ def test_string_conversion1(self): def test_string_conversion2(self): "Test the string representation of the projection class" - assert str(self.prj1) == "Y12c" + assert str(self.prj2) == "Y12c" class TestHammerEqualArea: @@ -551,7 +551,7 @@ def test_string_conversion2(self): def test_string_conversion3(self): "Test the string representation of the projection class" - assert str(self.prj2) == "O145/-35/45/12c+v" + assert str(self.prj3) == "O145/-35/45/12c+v" class TestObliqueMercator2: @@ -597,7 +597,7 @@ def test_string_conversion2(self): def test_string_conversion3(self): "Test the string representation of the projection class" - assert str(self.prj2) == "O145/-35/110/-20/12c+v" + assert str(self.prj3) == "O145/-35/110/-20/12c+v" class TestObliqueMercator3: @@ -643,7 +643,7 @@ def test_string_conversion2(self): def test_string_conversion3(self): "Test the string representation of the projection class" - assert str(self.prj2) == "O145/-35/110/-20/12c+v" + assert str(self.prj3) == "O145/-35/110/-20/12c+v" class TestTransverseMercator: @@ -687,7 +687,7 @@ def test_string_conversion1(self): def test_string_conversion2(self): "Test the string representation of the projection class" - assert str(self.prj1) == "U55H/12c" + assert str(self.prj2) == "U55H/12c" class TestEquidistantCylindrical: @@ -715,7 +715,7 @@ def test_string_conversion2(self): def test_string_conversion3(self): "Test the string representation of the projection class" - assert str(self.prj2) == "Q145/-35/12c" + assert str(self.prj3) == "Q145/-35/12c" class TestPolar: @@ -738,15 +738,15 @@ def test_string_conversion1(self): def test_string_conversion2(self): "Test the string representation of the polar projection class" - assert str(self.prj1) == "P10c+a+r10+t45" + assert str(self.prj2) == "P10c+a+r10+t45" def test_string_conversion3(self): "Test the string representation of the polar projection class" - assert str(self.prj1) == "P10c+f33" + assert str(self.prj3) == "P10c+f33" def test_string_conversion4(self): "Test the string representation of the polar projection class" - assert str(self.prj1) == "P10c+z33" + assert str(self.prj4) == "P10c+z33" def test_assert_depth_options(self): "Test that a ValueError assertion is raised for the depth options" @@ -768,7 +768,7 @@ class TestLinear: prj2 = projection.Linear(width=10, geographic=True) prj3 = projection.Linear(width=10, log_x=True) prj4 = projection.Linear(width=10, log_x=True, height=35, log_y=True) - prj5 = projection.Linear(width=10, power_x=2, height=35, power_y=3) + prj5 = projection.Linear(width=10, power_x=0.5, height=35, power_y=3) prj6 = projection.Linear(width=10, time_x="t", height=35, power_y=3) def test_default_unit(self):