Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and additional options for Peirce Quincuncial projections #2978

Merged
merged 26 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
49b9a21
fix for Peirce Quincuncial in projections/adams.cpp to flip out south…
tcwilkinson Dec 9, 2021
fbc40f1
Add .rst docs for Peirce Quincuncial fix
tcwilkinson Dec 10, 2021
ff03769
update to new png images for docs for peirce_q and peirce_q_d
tcwilkinson Dec 10, 2021
09e6eb5
Reintroduction of negative latitudes in gie testing for peirce_q_d
tcwilkinson Dec 10, 2021
4dbdd70
CI Plot job: build PROJ from source (fixes #2961)
rouault Dec 10, 2021
f9ec544
Adjustment of expected results of peirce_q_g in gie tests to take acc…
tcwilkinson Dec 10, 2021
038df86
Tweaks to adams.cpp
tcwilkinson Dec 10, 2021
f7c58ad
Add peirce_q_d to index of projections in doc
tcwilkinson Dec 10, 2021
971882e
Merge pull request #2981 from rouault/fix_2961
rouault Dec 12, 2021
ecc8767
Remove git stash holdover in news.rst
DFEvans Dec 13, 2021
2072894
Merge pull request #2982 from DFEvans/patch-1
rouault Dec 13, 2021
3e8b273
fix Peirce Quincuncial to project as square, diamond, horizontal etc …
tcwilkinson Dec 14, 2021
802843f
fix for Peirce Quincuncial in projections/adams.cpp to flip out south…
tcwilkinson Dec 9, 2021
0a61b32
Add .rst docs for Peirce Quincuncial fix
tcwilkinson Dec 10, 2021
f66b8a8
update to new png images for docs for peirce_q and peirce_q_d
tcwilkinson Dec 10, 2021
b0ea355
Reintroduction of negative latitudes in gie testing for peirce_q_d
tcwilkinson Dec 10, 2021
6027c66
Adjustment of expected results of peirce_q_g in gie tests to take acc…
tcwilkinson Dec 10, 2021
a6d3309
Tweaks to adams.cpp
tcwilkinson Dec 10, 2021
85504df
Add peirce_q_d to index of projections in doc
tcwilkinson Dec 10, 2021
97651fa
fix Peirce Quincuncial to project as square, diamond, horizontal etc …
tcwilkinson Dec 14, 2021
c79bb06
Merge branch 'feature_fix_peirce_q' of github.com:tcwilkinson/PROJ in…
tcwilkinson Dec 15, 2021
6b1baa4
squash to allow clang to not worry about casting double as bool
tcwilkinson Dec 15, 2021
945b0ea
default for peirce_q set to _diamond_ as compromise between backward …
tcwilkinson Dec 15, 2021
1b8cc7d
Change params o_scrollx/y to scrollx/y
tcwilkinson Dec 15, 2021
819f861
Attempt to fix doc issues for peirce_q
tcwilkinson Dec 15, 2021
73e7ff2
Shrink number of tests per single type of peirce_q, add additional te…
tcwilkinson Dec 15, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions .github/workflows/doc_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,34 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: conda-incubator/setup-miniconda@v2

- name: Install PROJ build dependencies
run: |
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y --no-install-recommends -o APT::Immediate-Configure=0 \
cmake g++ sqlite3 pkg-config libsqlite3-dev libtiff-dev libcurl4-openssl-dev

- name: Build PROJ
run: |
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/opt/proj
make -j$(nproc)
sudo mkdir -p /opt/proj
sudo make install
cd ..

- name: Install plot dependencies
uses: conda-incubator/setup-miniconda@v2
with:
environment-file: docs/plot/environment.yml

- name: Plot
working-directory: ./docs/plot
run: ./plot.py plotdefs.json images/
run: |
export PATH=/opt/proj/bin:$PATH
export LD_LIBRARY_PATH=/opt/proj/lib
export PROJ_LIB=/opt/proj/share/proj
./plot.py plotdefs.json images/

- uses: actions/upload-artifact@v2
with:
Expand Down
1 change: 0 additions & 1 deletion docs/plot/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ dependencies:
- descartes
- geojson
- matplotlib
- proj
tcwilkinson marked this conversation as resolved.
Show resolved Hide resolved
- shapely < 1.8
39 changes: 36 additions & 3 deletions docs/plot/plotdefs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1051,13 +1051,46 @@
"type": "poly"
},
{
"filename": "peirce_q.png",
"filename": "peirce_q_square.png",
"latmax": 90,
"latmin": 0,
"latmin": -90,
"lonmax": 180,
"lonmin": -180,
"name": "peirce_q_square",
"projstring": "+proj=peirce_q +lon_0=25 +type=square",
"res": "low",
"type": "poly"
},
{
"filename": "peirce_q_diamond.png",
"latmax": 90,
"latmin": -90,
"lonmax": 180,
"lonmin": -180,
"name": "peirce_q",
"projstring": "+proj=peirce_q",
"projstring": "+proj=peirce_q +lon_0=25 +type=diamond",
"res": "low",
"type": "poly"
},
{
"filename": "peirce_q_horizontal.png",
"latmax": 90,
"latmin": -90,
"lonmax": 180,
"lonmin": -180,
"name": "peirce_q_horizontal",
"projstring": "+proj=peirce_q +lon_0=25 +type=horizontal",
"res": "low",
"type": "line"
},
{
"filename": "grieger_triptychial.png",
"latmax": 90,
"latmin": -90,
"lonmax": 180,
"lonmin": -180,
"name": "grieger_triptychial",
"projstring": "+proj=pipeline +step +proj=ob_tran +o_proj=peirce_q +o_lat_p=-45 +o_lon_p=45 +type=horizontal +scrollx=-0.25 +step +proj=affine +s11=-1 +s12=0 +s21=0 +s22=-1",
rouault marked this conversation as resolved.
Show resolved Hide resolved
"res": "low",
"type": "poly"
},
Expand Down
2 changes: 0 additions & 2 deletions docs/source/news.rst
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,6 @@ Bug fixes
* :c:func:`createOperations()`: fix incorrect height transformation between 3D promoted RGF93 and CH1903+ (`#2555 <https://github.com/OSGeo/PROJ/issues/2555>`_)



>>>>>>> Stashed changes
8.0.0 Release Notes
++++++++++++++++++++++++++++++++++++++++
*March 1st 2021*
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 76 additions & 3 deletions docs/source/operations/projections/peirce_q.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@
Peirce Quincuncial
********************************************************************************

The Peirce Quincuncial projection is a conformal map projection
that transforms the circle of the northern hemisphere into a square,
and the southern hemisphere split into four triangles arranged
around the square to form a quincunx. The resulting projection
is a regular diamond shape or can be rotated to form a square.
The resulting tile can be infinitely tessellated. Though this implementation
defaults to a central meridian of 0, it is more common to use a central
meridian of around 25 to optimise the distortions. Peirce's original
published map from 1879 used a central meridian of approx -70.
The diamond and square versions can be produced using the
``+type=diamond`` and ``+type=square`` options respectively.
This implementation includes an alternative lateral projection
which places hemispheres side-by-side (``+type=horizontal`` or
``+type=vertical``). Combined with a general oblique transformation,
this can be used to produced a Grieger Triptychial projection
(see example below).

+---------------------+----------------------------------------------------------+
| **Classification** | Miscellaneous |
+---------------------+----------------------------------------------------------+
Expand All @@ -21,12 +38,33 @@ Peirce Quincuncial
+---------------------+----------------------------------------------------------+


.. figure:: ./images/peirce_q.png
.. figure:: ./images/peirce_q_square.png
:width: 500 px
:align: center
:alt: Peirce Quincuncial (Square)

proj-string: ``+proj=peirce_q +lon_0=25 +type=square``

.. figure:: ./images/peirce_q_diamond.png
:width: 500 px
:align: center
:alt: Peirce Quincuncial (Diamond)

proj-string: ``+proj=peirce_q +lon_0=25 +type=diamond``

.. figure:: ./images/peirce_q_horizontal.png
:width: 500 px
:align: center
:alt: Peirce Quincuncial (Horizontal)

proj-string: ``+proj=peirce_q +lon_0=25 +type=horizontal``

.. figure:: ./images/grieger_triptychial.png
:width: 500 px
:align: center
:alt: Peirce Quincuncial
:alt: Grieger Triptychial

proj-string: ``+proj=peirce_q``
proj-string: ``+proj=pipeline +step +proj=ob_tran +o_proj=peirce_q +o_lat_p=-45 +o_lon_p=45 +o_type=horizontal +o_scrollx=-0.25 +step +proj=affine +s11=-1 +s12=0 +s21=0 +s22=-1``
tcwilkinson marked this conversation as resolved.
Show resolved Hide resolved

Parameters
################################################################################
Expand All @@ -35,6 +73,41 @@ Parameters

.. include:: ../options/lon_0.rst

.. option:: +type=square/diamond/horizontal/vertical/nhemisphere/shemisphere

.. versionadded:: 8.2.1

*Defaults to diamond.*

Indicates the type of transformation applied to the southern hemisphere:
``square`` and ``diamond`` represent the traditional quincuncial form suggested
by Peirce with the southern hemisphere divided into 4 triangles and reflected
outward from the northern hemisphere. The ``square`` type is rotated by 45
degrees to produce the conventional square presentation. The origin lies at
the centre of the square or diamond.

By contrast, the ``horizontal`` and ``vertical`` forms reflect the southern
hemisphere laterally across the x or y axis respectively to produce a rectangular
form. The origin lies at the centre of the rectangle.

The other two types, ``nhemisphere`` and ``shemisphere``, discard latitudes of less
than 0 or more than 0, respectively, to allow single hemispheres to be selected.
The origin lies at the centre of the square or diamond.

.. option:: +scrollx=<value>

For ``horizontal`` type allows a scalar circular scroll of resulting x coordinates
to shift sections of the projection to the other horizontal side of the map.

*Defaults to 0.0. Must be a scale between -1.0 and 1.0.*

.. option:: +scrolly=<value>

For ``vertical`` type allows a scalar circular scroll of resulting y coordinates
to shift sections of the projection to the other vertical side of the map.

*Defaults to 0.0. Must be a scale between -1.0 and 1.0.*

.. include:: ../options/R.rst

.. include:: ../options/x_0.rst
Expand Down
150 changes: 148 additions & 2 deletions src/projections/adams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,21 @@
* PROJ by Kristian Evers. Original code found in file src/proj_guyou.c, see
* https://github.com/rouault/libproj4/blob/master/libproject-1.01/src/proj_guyou.c
* for reference.
* Fix for Peirce Quincuncial projection to diamond or square by Toby C. Wilkinson to
* correctly flip out southern hemisphere into the four triangles of Peirce's
* quincunx. The fix inspired by a similar rotate and translate solution applied
* by Jonathan Feinberg for cartopy, see
* https://github.com/jonathf/cartopy/blob/8172cac7fc45cafc86573d408ce85b74258a9c28/lib/cartopy/peircequincuncial.py
* Added original code for horizontal and vertical arrangement of hemispheres by Toby
* C. Wilkinson to allow creation of lateral quincuncial projections, such as Grieger's
* Triptychial, see, e.g.:
* - Grieger, B. (2020). “Optimized global map projections for specific applications:
* the triptychial projection and the Spilhaus projection”. EGU2020-9885.
* https://doi.org/10.5194/egusphere-egu2020-9885
*
* Copyright (c) 2005, 2006, 2009 Gerald I. Evenden
* Copyright (c) 2020 Kristian Evers
* Copyright (c) 2021 Toby C Wilkinson
*
* Related material
* ----------------
Expand Down Expand Up @@ -48,8 +60,20 @@ enum projection_type {
ADAMS_WS2,
};

enum peirce_type {
PEIRCE_Q_SQUARE,
PEIRCE_Q_DIAMOND,
PEIRCE_Q_NHEMISPHERE,
PEIRCE_Q_SHEMISPHERE,
PEIRCE_Q_HORIZONTAL,
PEIRCE_Q_VERTICAL,
};

struct pj_opaque {
projection_type mode;
peirce_type pqtype;
double scrollx = 0.0;
double scrolly = 0.0;
};

} // anonymous namespace
Expand Down Expand Up @@ -118,9 +142,18 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) {
}
break;
case PEIRCE_Q: {
if( lp.phi < -TOL ) {
/* lam0 - note that the original Peirce model used a central meridian of around -70deg, but the default within proj is +lon0=0 */
if (Q->pqtype == PEIRCE_Q_NHEMISPHERE) {
if( lp.phi < -TOL ) {
proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
return proj_coord_error().xy;
}
}
if (Q->pqtype == PEIRCE_Q_SHEMISPHERE) {
if( lp.phi > -TOL ) {
proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
return proj_coord_error().xy;
}
}
const double sl = sin(lp.lam);
const double cl = cos(lp.lam);
Expand Down Expand Up @@ -173,7 +206,71 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) {
xy.x = ell_int_5(m);
xy.y = ell_int_5(n);

if (Q->mode == ADAMS_HEMI || Q->mode == ADAMS_WS2) { /* rotate by 45deg. */
if (Q->mode == PEIRCE_Q) {
/* Constant complete elliptic integral of the first kind with m=0.5, calculated using https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.ellipk.html . Used as basic as scaled shift distance */
constexpr double shd = 1.8540746773013719 * 2;

/* For square and diamond Quincuncial projections, spin out southern hemisphere to triangular segments of quincunx (before rotation for square)*/
if( Q->pqtype == PEIRCE_Q_SQUARE || ( Q->pqtype == PEIRCE_Q_DIAMOND )) {
if (lp.phi < 0.) { /* fold out segments */
if (lp.lam < ( -0.75 * M_PI )) xy.y = shd - xy.y; /* top left segment, shift up and reflect y */
if ( (lp.lam < (-0.25 * M_PI)) && (lp.lam >= ( -0.75 * M_PI ))) xy.x = - shd - xy.x; /* left segment, shift left and reflect x */
if ( (lp.lam < (0.25 * M_PI)) && (lp.lam >= ( -0.25 * M_PI ))) xy.y = - shd - xy.y; /* bottom segment, shift down and reflect y */
if ( (lp.lam < (0.75 * M_PI)) && (lp.lam >= ( 0.25 * M_PI ))) xy.x = shd - xy.x; /* right segment, shift right and reflect x */
if (lp.lam >= (0.75 * M_PI)) xy.y = shd - xy.y; /* top right segment, shift up and reflect y */
}
}

/* For square types rotate xy by 45 deg */
if( Q->pqtype == PEIRCE_Q_SQUARE ) {
const double temp = xy.x;
xy.x = RSQRT2 * (xy.x - xy.y);
xy.y = RSQRT2 * (temp + xy.y);
}

/* For rectangle Quincuncial projs, spin out southern hemisphere to east (horizontal) or north (vertical) after rotation */
if( Q->pqtype == PEIRCE_Q_HORIZONTAL ) {
if (lp.phi < 0.) {
xy.x = shd - xy.x; /* reflect x to east */
}
xy.x = xy.x - (shd / 2); /* shift everything so origin is in middle of two hemispheres */
}
if( Q->pqtype == PEIRCE_Q_VERTICAL ) {
if (lp.phi < 0.) {
xy.y = shd - xy.y; /* reflect y to north */
}
xy.y = xy.y - (shd / 2); /* shift everything so origin is in middle of two hemispheres */
}

//if o_scrollx param present, scroll x
if (!(Q->scrollx == 0.0) && (Q->pqtype == PEIRCE_Q_HORIZONTAL) ) {
double xscale = 2.0;
double xthresh = shd / 2;
xy.x = xy.x + (Q->scrollx * (xthresh * 2 * xscale)); /*shift relative to proj width*/
if(xy.x >= (xthresh * xscale)) {
xy.x = xy.x - (shd * xscale);
}
else if (xy.x < -(xthresh * xscale)) {
xy.x = xy.x + (shd * xscale);
}
}

//if o_scrolly param present, scroll y
if (!(Q->scrolly == 0.0) && (Q->pqtype == PEIRCE_Q_VERTICAL)) {
double yscale = 2.0;
double ythresh = shd / 2;
xy.y = xy.y + (Q->scrolly * (ythresh * 2 * yscale)); /*shift relative to proj height*/
if(xy.y >= (ythresh * yscale)) {
xy.y = xy.y - (shd * yscale);
}
else if (xy.y < -(ythresh * yscale)) {
xy.y = xy.y + (shd * yscale);
}
}

}

if (Q->mode == ADAMS_HEMI || Q->mode == ADAMS_WS2 ) { /* rotate by 45deg. */
const double temp = xy.x;
xy.x = RSQRT2 * (xy.x - xy.y);
xy.y = RSQRT2 * (temp + xy.y);
Expand Down Expand Up @@ -218,6 +315,55 @@ static PJ *setup(PJ *P, projection_type mode) {
if( mode == ADAMS_WS2 )
P->inv = adams_inverse;

if( mode == PEIRCE_Q) {
// Quincuncial projections type options: square, diamond, hemisphere, horizontal (rectangle) or vertical (rectangle)
const char* pqtype = pj_param (P->ctx, P->params, "stype").s;

if (!pqtype) pqtype = "diamond"; /* default if type value not supplied */

if (strcmp(pqtype, "square") == 0) {
Q->pqtype = PEIRCE_Q_SQUARE;
}
else if (strcmp(pqtype, "diamond") == 0) {
Q->pqtype = PEIRCE_Q_DIAMOND;
}
else if (strcmp(pqtype, "nhemisphere") == 0) {
Q->pqtype = PEIRCE_Q_NHEMISPHERE;
}
else if (strcmp(pqtype, "shemisphere") == 0) {
Q->pqtype = PEIRCE_Q_SHEMISPHERE;
}
else if (strcmp(pqtype, "horizontal") == 0) {
Q->pqtype = PEIRCE_Q_HORIZONTAL;
if (pj_param(P->ctx, P->params, "tscrollx").i) {
double scrollx;
scrollx = pj_param(P->ctx, P->params, "dscrollx").f;
if (scrollx > 1 || scrollx < -1) {
proj_log_error(P, _("Invalid value for scrollx: |scrollx| should between -1 and 1"));
return pj_default_destructor (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
}
Q->scrollx = scrollx;
}
}
else if (strcmp(pqtype, "vertical") == 0) {
Q->pqtype = PEIRCE_Q_VERTICAL;
if (pj_param(P->ctx, P->params, "tscrolly").i) {
double scrolly;
scrolly = pj_param(P->ctx, P->params, "dscrolly").f;
if (scrolly > 1 || scrolly < -1) {
proj_log_error(P, _("Invalid value for scrolly: |scrolly| should between -1 and 1"));
return pj_default_destructor (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
}
Q->scrolly = scrolly;
}
}
else {
proj_log_error (P, _("peirce_q: invalid value for 'type' parameter"));
return pj_default_destructor (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
}

}

return P;
}

Expand Down
Loading