1 // Boost.Geometry - gis-projections (based on PROJ4)
3 // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2017, 2018, 2019.
6 // Modifications copyright (c) 2017-2019, Oracle and/or its affiliates.
7 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.
9 // Use, modification and distribution is subject to the Boost Software License,
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
13 // This file is converted from PROJ4, http://trac.osgeo.org/proj
14 // PROJ4 is originally written by Gerald Evenden (then of the USGS)
15 // PROJ4 is maintained by Frank Warmerdam
16 // PROJ4 is converted to Boost.Geometry by Barend Gehrels
18 // Last updated version of proj: 5.0.0
20 // Original copyright notice:
22 // This code was entirely written by Nathan Wagner
23 // and is in the public domain.
25 // Permission is hereby granted, free of charge, to any person obtaining a
26 // copy of this software and associated documentation files (the "Software"),
27 // to deal in the Software without restriction, including without limitation
28 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
29 // and/or sell copies of the Software, and to permit persons to whom the
30 // Software is furnished to do so, subject to the following conditions:
32 // The above copyright notice and this permission notice shall be included
33 // in all copies or substantial portions of the Software.
35 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
36 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
40 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
41 // DEALINGS IN THE SOFTWARE.
43 #ifndef BOOST_GEOMETRY_PROJECTIONS_ISEA_HPP
44 #define BOOST_GEOMETRY_PROJECTIONS_ISEA_HPP
48 #include <boost/core/ignore_unused.hpp>
50 #include <boost/geometry/core/assert.hpp>
52 #include <boost/geometry/srs/projections/impl/base_static.hpp>
53 #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
54 #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
55 #include <boost/geometry/srs/projections/impl/pj_param.hpp>
56 #include <boost/geometry/srs/projections/impl/projects.hpp>
58 #include <boost/geometry/util/math.hpp>
60 namespace boost { namespace geometry
65 #ifndef DOXYGEN_NO_DETAIL
66 namespace detail { namespace isea
68 static const double epsilon = std::numeric_limits<double>::epsilon();
71 static const double isea_scale = 0.8301572857837594396028083;
72 /* 26.565051177 degrees */
73 static const double v_lat = 0.46364760899944494524;
75 static const double e_rad = 0.91843818702186776133;
77 static const double f_rad = 0.18871053072122403508;
78 /* R tan(g) sin(60) */
79 static const double table_g = 0.6615845383;
80 /* H = 0.25 R tan g = */
81 static const double table_h = 0.1909830056;
82 //static const double RPRIME = 0.91038328153090290025;
83 static const double isea_std_lat = 1.01722196792335072101;
84 static const double isea_std_lon = .19634954084936207740;
87 inline T deg30_rad() { return T(30) * geometry::math::d2r<T>(); }
89 inline T deg120_rad() { return T(120) * geometry::math::d2r<T>(); }
91 inline T deg72_rad() { return T(72) * geometry::math::d2r<T>(); }
93 inline T deg90_rad() { return geometry::math::half_pi<T>(); }
95 inline T deg144_rad() { return T(144) * geometry::math::d2r<T>(); }
97 inline T deg36_rad() { return T(36) * geometry::math::d2r<T>(); }
99 inline T deg108_rad() { return T(108) * geometry::math::d2r<T>(); }
100 template <typename T>
101 inline T deg180_rad() { return geometry::math::pi<T>(); }
103 inline bool downtri(int tri) { return (((tri - 1) / 5) % 2 == 1); }
106 * Proj 4 provides its own entry points into
107 * the code, so none of the library functions
116 /* y *must* be positive down as the xy /iso conversion assumes this */
118 int hex_xy(struct hex *h) {
119 if (!h->iso) return 1;
121 h->y = -h->y - (h->x+1)/2;
123 /* need to round toward -inf, not toward zero, so x-1 */
124 h->y = -h->y - h->x/2;
132 int hex_iso(struct hex *h) {
133 if (h->iso) return 1;
136 h->y = (-h->y - (h->x+1)/2);
138 /* need to round toward -inf, not toward zero, so x-1 */
139 h->y = (-h->y - (h->x)/2);
147 template <typename T>
149 int hexbin2(T const& width, T x, T y, int *i, int *j)
152 T abs_dx, abs_dy, abs_dz;
156 static const T cos_deg30 = cos(deg30_rad<T>());
158 x = x / cos_deg30; /* rotated X coord */
159 y = y - x / 2.0; /* adjustment for rotated X */
161 /* adjust for actual hexwidth */
177 abs_dx = fabs(rx - x);
178 abs_dy = fabs(ry - y);
179 abs_dz = fabs(rz - z);
181 if (abs_dx >= abs_dy && abs_dx >= abs_dz) {
183 } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) {
197 return ix * 100 + iy;
200 //enum isea_poly { isea_none = 0, isea_icosahedron = 20 };
201 //enum isea_topology { isea_hexagon=6, isea_triangle=3, isea_diamond=4 };
202 enum isea_address_form {
203 /*isea_addr_geo,*/ isea_addr_q2di, isea_addr_seqnum,
204 /*isea_addr_interleave,*/ isea_addr_plane, isea_addr_q2dd,
205 isea_addr_projtri, isea_addr_vertex2dd, isea_addr_hex
208 template <typename T>
210 T o_lat, o_lon, o_az; /* orientation, radians */
211 T radius; /* radius of the earth in meters, ignored 1.0 */
212 unsigned long serial;
213 //int pole; /* true if standard snyder */
214 int aperture; /* valid values depend on partitioning method */
216 int triangle; /* triangle of last transformed point */
217 int quad; /* quad of last transformed point */
218 //isea_poly polyhedron; /* ignored, icosahedron */
219 //isea_topology topology; /* ignored, hexagon */
220 isea_address_form output; /* an isea_address_form */
223 template <typename T>
228 template <typename T>
233 template <typename T>
234 struct isea_address {
235 T x,y; /* or i,j or lon,lat depending on type */
236 int type; /* enum isea_address_form */
242 enum snyder_polyhedron {
243 snyder_poly_hexagon = 0, snyder_poly_pentagon = 1,
244 snyder_poly_tetrahedron = 2, snyder_poly_cube = 3,
245 snyder_poly_octahedron = 4, snyder_poly_dodecahedron = 5,
246 snyder_poly_icosahedron = 6
249 template <typename T>
250 struct snyder_constants {
251 T g, G, theta, ea_w, ea_a, ea_b, g_w, g_a, g_b;
254 template <typename T>
255 inline const snyder_constants<T> * constants()
257 /* TODO put these in radians to avoid a later conversion */
258 static snyder_constants<T> result[] = {
259 {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0},
260 {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027},
261 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
262 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
263 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
264 {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
265 {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0}
270 template <typename T>
271 inline const isea_geo<T> * vertex()
273 static isea_geo<T> result[] = {
274 { 0.0, deg90_rad<T>()},
275 { deg180_rad<T>(), v_lat},
276 {-deg108_rad<T>(), v_lat},
277 {-deg36_rad<T>(), v_lat},
278 { deg36_rad<T>(), v_lat},
279 { deg108_rad<T>(), v_lat},
280 {-deg144_rad<T>(), -v_lat},
281 {-deg72_rad<T>(), -v_lat},
283 { deg72_rad<T>(), -v_lat},
284 { deg144_rad<T>(), -v_lat},
285 { 0.0, -deg90_rad<T>()}
290 /* TODO make an isea_pt array of the vertices as well */
292 static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, 11, 11, 11, 11};
294 /* triangle Centers */
295 template <typename T>
296 inline const isea_geo<T> * icostriangles()
298 static isea_geo<T> result[] = {
300 {-deg144_rad<T>(), e_rad},
301 {-deg72_rad<T>(), e_rad},
303 { deg72_rad<T>(), e_rad},
304 { deg144_rad<T>(), e_rad},
305 {-deg144_rad<T>(), f_rad},
306 {-deg72_rad<T>(), f_rad},
308 { deg72_rad<T>(), f_rad},
309 { deg144_rad<T>(), f_rad},
310 {-deg108_rad<T>(), -f_rad},
311 {-deg36_rad<T>(), -f_rad},
312 { deg36_rad<T>(), -f_rad},
313 { deg108_rad<T>(), -f_rad},
314 { deg180_rad<T>(), -f_rad},
315 {-deg108_rad<T>(), -e_rad},
316 {-deg36_rad<T>(), -e_rad},
317 { deg36_rad<T>(), -e_rad},
318 { deg108_rad<T>(), -e_rad},
319 { deg180_rad<T>(), -e_rad},
324 template <typename T>
325 inline T az_adjustment(int triangle)
332 v = vertex<T>()[tri_v1[triangle]];
333 c = icostriangles<T>()[triangle];
335 /* TODO looks like the adjustment is always either 0 or 180 */
336 /* at least if you pick your vertex carefully */
337 adj = atan2(cos(v.lat) * sin(v.lon - c.lon),
338 cos(c.lat) * sin(v.lat)
339 - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon));
343 template <typename T>
344 inline isea_pt<T> isea_triangle_xy(int triangle)
347 T Rprime = 0.91038328153090290025;
349 triangle = (triangle - 1) % 20;
351 c.x = table_g * ((triangle % 5) - 2) * 2.0;
355 switch (triangle / 5) {
366 c.y = -5.0 * table_h;
369 /* should be impossible */
370 BOOST_THROW_EXCEPTION( projection_exception() );
379 template <typename T>
380 inline T sph_azimuth(T const& f_lon, T const& f_lat, T const& t_lon, T const& t_lat)
384 az = atan2(cos(t_lat) * sin(t_lon - f_lon),
385 cos(f_lat) * sin(t_lat)
386 - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon)
391 /* coord needs to be in radians */
392 template <typename T>
393 inline int isea_snyder_forward(isea_geo<T> * ll, isea_pt<T> * out)
395 static T const two_pi = detail::two_pi<T>();
396 static T const d2r = geometry::math::d2r<T>();
401 * spherical distance from center of polygon face to any of its
402 * vertexes on the globe
407 * spherical angle between radius vector to center and adjacent edge
408 * of spherical polygon on the globe
413 * plane angle between radius vector to center and adjacent edge of
418 /* additional variables from snyder */
419 T q, Rprime, H, Ag, Azprime, Az, dprime, f, rho,
422 /* variables used to store intermediate results */
423 T cot_theta, tan_g, az_offset;
425 /* how many multiples of 60 degrees we adjust the azimuth */
426 int Az_adjust_multiples;
428 snyder_constants<T> c;
431 * TODO by locality of reference, start by trying the same triangle
435 /* TODO put these constants in as radians to begin with */
436 c = constants<T>()[snyder_poly_icosahedron];
437 theta = c.theta * d2r;
441 for (i = 1; i <= 20; i++) {
445 center = icostriangles<T>()[i];
448 z = acos(sin(center.lat) * sin(ll->lat)
449 + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon));
451 /* not on this triangle */
452 if (z > g + 0.000005) { /* TODO DBL_EPSILON */
456 Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat);
460 /* This calculates "some" vertex coordinate */
461 az_offset = az_adjustment<T>(i);
465 /* TODO I don't know why we do this. It's not in snyder */
466 /* maybe because we should have picked a better vertex */
471 * adjust Az for the point to fall within the range of 0 to
472 * 2(90 - theta) or 60 degrees for the hexagon, by
473 * and therefore 120 degrees for the triangle
475 * subtracting or adding multiples of 60 degrees to Az and
476 * recording the amount of adjustment
479 Az_adjust_multiples = 0;
481 Az += deg120_rad<T>();
482 Az_adjust_multiples--;
484 while (Az > deg120_rad<T>() + epsilon) {
485 Az -= deg120_rad<T>();
486 Az_adjust_multiples++;
490 cot_theta = 1.0 / tan(theta);
491 tan_g = tan(g); /* TODO this is a constant */
493 /* Calculate q from eq 9. */
494 /* TODO cot_theta is cot(30) */
495 q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta);
497 /* not in this triangle */
498 if (z > q + 0.000005) {
503 /* Apply equations 5-8 and 10-12 in order */
506 /* Rprime = 0.9449322893 * R; */
507 /* R' in the paper is for the truncated */
508 Rprime = 0.91038328153090290025;
511 H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G));
514 /* Ag = (Az + G + H - deg180_rad) * M_PI * R * R / deg180_rad; */
515 Ag = Az + G + H - deg180_rad<T>();
518 Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta);
521 /* cot(theta) = 1.73205080756887729355 */
522 dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta);
525 f = dprime / (2.0 * Rprime * sin(q / 2.0));
528 rho = 2.0 * Rprime * f * sin(z / 2.0);
531 * add back the same 60 degree multiple adjustment from step
535 Azprime += deg120_rad<T>() * Az_adjust_multiples;
537 /* calculate rectangular coordinates */
539 x = rho * sin(Azprime);
540 y = rho * cos(Azprime);
544 * translate coordinates to the origin for the particular
545 * hexagon on the flattened polyhedral map plot
555 * should be impossible, this implies that the coordinate is not on
559 //fprintf(stderr, "impossible transform: %f %f is not on any triangle\n",
560 // ll->lon * geometry::math::r2d<double>(), ll->lat * geometry::math::r2d<double>());
561 std::stringstream ss;
562 ss << "impossible transform: " << ll->lon * geometry::math::r2d<T>()
563 << " " << ll->lat * geometry::math::r2d<T>() << " is not on any triangle.";
565 BOOST_THROW_EXCEPTION( projection_exception(ss.str()) );
568 return 0; /* supresses a warning */
572 * return the new coordinates of any point in orginal coordinate system.
573 * Define a point (newNPold) in orginal coordinate system as the North Pole in
574 * new coordinate system, and the great circle connect the original and new
575 * North Pole as the lon0 longitude in new coordinate system, given any point
576 * in orginal coordinate system, this function return the new coordinates.
580 /* formula from Snyder, Map Projections: A working manual, p31 */
582 * old north pole at np in new coordinates
583 * could be simplified a bit with fewer intermediates
585 * TODO take a result pointer
587 template <typename T>
588 inline isea_geo<T> snyder_ctran(isea_geo<T> * np, isea_geo<T> * pt)
590 static T const pi = detail::pi<T>();
591 static T const two_pi = detail::two_pi<T>();
594 T alpha, phi, lambda, lambda0, beta, lambdap, phip;
596 T lp_b; /* lambda prime minus beta */
609 sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0);
613 /* use the two argument form so we end up in the right quadrant */
614 lp_b = atan2(cos_p * sin(lambda - lambda0),
615 (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi)));
617 lambdap = lp_b + beta;
619 /* normalize longitude */
620 /* TODO can we just do a modulus ? */
621 lambdap = fmod(lambdap, two_pi);
624 while (lambdap < -pi)
627 phip = asin(sin_phip);
635 template <typename T>
636 inline isea_geo<T> isea_ctran(isea_geo<T> * np, isea_geo<T> * pt, T const& lon0)
638 static T const pi = detail::pi<T>();
639 static T const two_pi = detail::two_pi<T>();
644 npt = snyder_ctran(np, pt);
647 npt.lon -= (pi - lon0 + np->lon);
650 * snyder is down tri 3, isea is along side of tri1 from vertex 0 to
651 * vertex 1 these are 180 degrees apart
654 /* normalize longitude */
655 npt.lon = fmod(npt.lon, two_pi);
658 while (npt.lon < -pi)
666 /* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */
668 template <typename T>
669 inline int isea_grid_init(isea_dgg<T> * g)
674 //g->polyhedron = isea_icosahedron;
675 g->o_lat = isea_std_lat;
676 g->o_lon = isea_std_lon;
681 //g->topology = isea_hexagon;
686 template <typename T>
687 inline int isea_orient_isea(isea_dgg<T> * g)
691 g->o_lat = isea_std_lat;
692 g->o_lon = isea_std_lon;
697 template <typename T>
698 inline int isea_orient_pole(isea_dgg<T> * g)
700 static T const half_pi = detail::half_pi<T>();
710 template <typename T>
711 inline int isea_transform(isea_dgg<T> * g, isea_geo<T> * in,
720 i = isea_ctran(&pole, in, g->o_az);
722 tri = isea_snyder_forward(&i, out);
731 template <typename T>
732 inline void isea_rotate(isea_pt<T> * pt, T const& degrees)
734 static T const d2r = geometry::math::d2r<T>();
735 static T const two_pi = detail::two_pi<T>();
741 rad = -degrees * d2r;
742 while (rad >= two_pi) rad -= two_pi;
743 while (rad <= -two_pi) rad += two_pi;
745 x = pt->x * cos(rad) + pt->y * sin(rad);
746 y = -pt->x * sin(rad) + pt->y * cos(rad);
752 template <typename T>
753 inline int isea_tri_plane(int tri, isea_pt<T> *pt, T const& radius)
755 isea_pt<T> tc; /* center of triangle */
758 isea_rotate(pt, 180.0);
760 tc = isea_triangle_xy<T>(tri);
769 /* convert projected triangle coords to quad xy coords, return quad number */
770 template <typename T>
771 inline int isea_ptdd(int tri, isea_pt<T> *pt)
775 downtri = (((tri - 1) / 5) % 2 == 1);
776 quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1;
778 isea_rotate(pt, downtri ? 240.0 : 60.0);
781 /* pt->y += cos(30.0 * M_PI / 180.0); */
782 pt->y += .86602540378443864672;
787 template <typename T>
788 inline int isea_dddi_ap3odd(isea_dgg<T> *g, int quad, isea_pt<T> *pt, isea_pt<T> *di)
790 static T const pi = detail::pi<T>();
794 T sidelength; /* in hexes */
799 /* This is the number of hexes from apex to base of a triangle */
800 sidelength = (math::pow(T(2), g->resolution) + T(1)) / T(2);
802 /* apex to base is cos(30deg) */
803 hexwidth = cos(pi / 6.0) / sidelength;
805 /* TODO I think sidelength is always x.5, so
806 * (int)sidelength * 2 + 1 might be just as good
808 maxcoord = (int) (sidelength * 2.0 + 0.5);
811 hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
819 * you want to test for max coords for the next quad in the same
820 * "row" first to get the case where both are max
823 if (d == 0 && i == maxcoord) {
828 } else if (i == maxcoord) {
829 /* upper right in next quad */
835 } else if (d == maxcoord) {
836 /* lower right in quad to lower right */
840 } else if (quad >= 6) {
841 if (i == 0 && d == maxcoord) {
846 } else if (d == maxcoord) {
847 /* lower right in next quad */
853 } else if (i == maxcoord) {
854 /* upper right in quad to upper right */
855 quad = (quad - 4) % 5;
867 template <typename T>
868 inline int isea_dddi(isea_dgg<T> *g, int quad, isea_pt<T> *pt, isea_pt<T> *di)
872 int sidelength; /* in hexes */
875 if (g->aperture == 3 && g->resolution % 2 != 0) {
876 return isea_dddi_ap3odd(g, quad, pt, di);
878 /* todo might want to do this as an iterated loop */
879 if (g->aperture >0) {
880 sidelength = (int) (math::pow(T(g->aperture), T(g->resolution / T(2))) + T(0.5));
882 sidelength = g->resolution;
885 hexwidth = 1.0 / sidelength;
888 isea_rotate(&v, -30.0);
889 hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
893 /* we may actually be on another quad */
895 if (h.x == 0 && h.z == -sidelength) {
901 } else if (h.z == -sidelength) {
905 h.y = sidelength - h.x;
906 h.z = h.x - sidelength;
908 } else if (h.x == sidelength) {
913 } else if (quad >= 6) {
914 if (h.z == 0 && h.x == sidelength) {
920 } else if (h.x == sidelength) {
924 h.x = h.y + sidelength;
927 } else if (h.y == -sidelength) {
940 template <typename T>
941 inline int isea_ptdi(isea_dgg<T> *g, int tri, isea_pt<T> *pt,
948 quad = isea_ptdd(tri, &v);
949 quad = isea_dddi(g, quad, &v, di);
954 template <typename T>
955 inline int isea_disn(isea_dgg<T> *g, int quad, isea_pt<T> *di)
965 /* hexes in a quad */
966 hexes = (int) (math::pow(T(g->aperture), T(g->resolution)) + T(0.5));
968 g->serial = 1 + 10 * hexes + 1;
971 if (g->aperture == 3 && g->resolution % 2 == 1) {
972 height = (int) (math::pow(T(g->aperture), T((g->resolution - 1) / T(2))));
973 sn = ((int) di->x) * height;
974 sn += ((int) di->y) / height;
975 sn += (quad - 1) * hexes;
978 sidelength = (int) (math::pow(T(g->aperture), T(g->resolution / T(2))) + T(0.5));
979 sn = (int) ((quad - 1) * hexes + sidelength * di->x + di->y + 2);
986 /* TODO just encode the quad in the d or i coordinate
987 * quad is 0-11, which can be four bits.
988 * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf
990 /* convert a q2di to global hex coord */
991 template <typename T>
992 inline int isea_hex(isea_dgg<T> *g, int tri, isea_pt<T> *pt,
996 #ifdef BOOST_GEOMETRY_PROJECTIONS_FIXME
999 #endif // BOOST_GEOMETRY_PROJECTIONS_FIXME
1002 quad = isea_ptdi(g, tri, pt, &v);
1004 hex->x = ((int)v.x << 4) + quad;
1008 #ifdef BOOST_GEOMETRY_PROJECTIONS_FIXME
1012 /* Aperture 3 odd resolutions */
1013 if (g->aperture == 3 && g->resolution % 2 != 0) {
1014 int offset = (int)(pow(T(3.0), T(g->resolution - 1)) + 0.5);
1016 d += offset * ((g->quad-1) % 5);
1017 i += offset * ((g->quad-1) % 5);
1022 } else if (quad == 11) {
1025 } else if (quad > 5) {
1032 hex->x = x + offset / 3;
1033 hex->y = y + 2 * offset / 3;
1037 /* aperture 3 even resolutions and aperture 4 */
1038 sidelength = (int) (pow(T(g->aperture), T(g->resolution / 2.0)) + 0.5);
1041 hex->y = sidelength;
1042 } else if (g->quad == 11) {
1043 hex->x = sidelength * 2;
1046 hex->x = d + sidelength * ((g->quad-1) % 5);
1047 if (g->quad > 5) hex->x += sidelength;
1048 hex->y = i + sidelength * ((g->quad-1) % 5);
1052 #endif // BOOST_GEOMETRY_PROJECTIONS_FIXME
1055 template <typename T>
1056 inline isea_pt<T> isea_forward(isea_dgg<T> *g, isea_geo<T> *in)
1059 isea_pt<T> out, coord;
1061 tri = isea_transform(g, in, &out);
1063 if (g->output == isea_addr_plane) {
1064 isea_tri_plane(tri, &out, g->radius);
1068 /* convert to isea standard triangle size */
1069 out.x = out.x / g->radius * isea_scale;
1070 out.y = out.y / g->radius * isea_scale;
1072 out.y += 2.0 * .14433756729740644112;
1074 switch (g->output) {
1075 case isea_addr_projtri:
1076 /* nothing to do, already in projected triangle */
1078 case isea_addr_vertex2dd:
1079 g->quad = isea_ptdd(tri, &out);
1081 case isea_addr_q2dd:
1082 /* Same as above, we just don't print as much */
1083 g->quad = isea_ptdd(tri, &out);
1085 case isea_addr_q2di:
1086 g->quad = isea_ptdi(g, tri, &out, &coord);
1089 case isea_addr_seqnum:
1090 isea_ptdi(g, tri, &out, &coord);
1091 /* disn will set g->serial */
1092 isea_disn(g, g->quad, &coord);
1096 isea_hex(g, tri, &out, &coord);
1100 // isea_addr_plane handled above
1101 BOOST_GEOMETRY_ASSERT(false);
1108 * Proj 4 integration code follows
1111 template <typename T>
1117 template <typename T, typename Parameters>
1118 struct base_isea_spheroid
1120 par_isea<T> m_proj_parm;
1122 // FORWARD(s_forward)
1123 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
1124 inline void fwd(Parameters const& , T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
1132 isea_dgg<T> copy = this->m_proj_parm.dgg;
1133 out = isea_forward(©, &in);
1139 static inline std::string get_name()
1141 return "isea_spheroid";
1146 template <typename T>
1147 inline void isea_orient_init(srs::detail::proj4_parameters const& params,
1148 par_isea<T>& proj_parm)
1150 std::string opt = pj_get_param_s(params, "orient");
1151 if (! opt.empty()) {
1152 if (opt == std::string("isea")) {
1153 isea_orient_isea(&proj_parm.dgg);
1154 } else if (opt == std::string("pole")) {
1155 isea_orient_pole(&proj_parm.dgg);
1157 BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
1162 template <typename T>
1163 inline void isea_orient_init(srs::dpar::parameters<T> const& params,
1164 par_isea<T>& proj_parm)
1166 typename srs::dpar::parameters<T>::const_iterator
1167 it = pj_param_find(params, srs::dpar::orient);
1168 if (it != params.end()) {
1169 srs::dpar::value_orient o = static_cast<srs::dpar::value_orient>(it->template get_value<int>());
1170 if (o == srs::dpar::orient_isea) {
1171 isea_orient_isea(&proj_parm.dgg);
1172 } else if (o == srs::dpar::orient_pole) {
1173 isea_orient_pole(&proj_parm.dgg);
1175 BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
1180 template <typename T>
1181 inline void isea_mode_init(srs::detail::proj4_parameters const& params,
1182 par_isea<T>& proj_parm)
1184 std::string opt = pj_get_param_s(params, "mode");
1185 if (! opt.empty()) {
1186 if (opt == std::string("plane")) {
1187 proj_parm.dgg.output = isea_addr_plane;
1188 } else if (opt == std::string("di")) {
1189 proj_parm.dgg.output = isea_addr_q2di;
1190 } else if (opt == std::string("dd")) {
1191 proj_parm.dgg.output = isea_addr_q2dd;
1192 } else if (opt == std::string("hex")) {
1193 proj_parm.dgg.output = isea_addr_hex;
1195 BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
1200 template <typename T>
1201 inline void isea_mode_init(srs::dpar::parameters<T> const& params,
1202 par_isea<T>& proj_parm)
1204 typename srs::dpar::parameters<T>::const_iterator
1205 it = pj_param_find(params, srs::dpar::mode);
1206 if (it != params.end()) {
1207 srs::dpar::value_mode m = static_cast<srs::dpar::value_mode>(it->template get_value<int>());
1208 if (m == srs::dpar::mode_plane) {
1209 proj_parm.dgg.output = isea_addr_plane;
1210 } else if (m == srs::dpar::mode_di) {
1211 proj_parm.dgg.output = isea_addr_q2di;
1212 } else if (m == srs::dpar::mode_dd) {
1213 proj_parm.dgg.output = isea_addr_q2dd;
1214 } else if (m == srs::dpar::mode_hex) {
1215 proj_parm.dgg.output = isea_addr_hex;
1217 BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
1222 // Icosahedral Snyder Equal Area
1223 template <typename Params, typename T>
1224 inline void setup_isea(Params const& params, par_isea<T>& proj_parm)
1228 isea_grid_init(&proj_parm.dgg);
1230 proj_parm.dgg.output = isea_addr_plane;
1231 /* proj_parm.dgg.radius = par.a; / * otherwise defaults to 1 */
1232 /* calling library will scale, I think */
1234 isea_orient_init(params, proj_parm);
1236 pj_param_r<srs::spar::azi>(params, "azi", srs::dpar::azi, proj_parm.dgg.o_az);
1237 pj_param_r<srs::spar::lon_0>(params, "lon_0", srs::dpar::lon_0, proj_parm.dgg.o_lon);
1238 pj_param_r<srs::spar::lat_0>(params, "lat_0", srs::dpar::lat_0, proj_parm.dgg.o_lat);
1239 // TODO: this parameter is set below second time
1240 pj_param_i<srs::spar::aperture>(params, "aperture", srs::dpar::aperture, proj_parm.dgg.aperture);
1241 // TODO: this parameter is set below second time
1242 pj_param_i<srs::spar::resolution>(params, "resolution", srs::dpar::resolution, proj_parm.dgg.resolution);
1244 isea_mode_init(params, proj_parm);
1246 // TODO: pj_param_exists -> pj_get_param_b ?
1247 if (pj_param_exists<srs::spar::rescale>(params, "rescale", srs::dpar::rescale)) {
1248 proj_parm.dgg.radius = isea_scale;
1251 if (pj_param_i<srs::spar::resolution>(params, "resolution", srs::dpar::resolution, proj_parm.dgg.resolution)) {
1254 proj_parm.dgg.resolution = 4;
1257 if (pj_param_i<srs::spar::aperture>(params, "aperture", srs::dpar::aperture, proj_parm.dgg.aperture)) {
1260 proj_parm.dgg.aperture = 3;
1264 }} // namespace detail::isea
1268 \brief Icosahedral Snyder Equal Area projection
1269 \ingroup projections
1270 \tparam Geographic latlong point type
1271 \tparam Cartesian xy point type
1272 \tparam Parameters parameter type
1273 \par Projection characteristics
1275 \par Projection parameters
1277 - azi: Azimuth (or Gamma) (degrees)
1278 - lon_0: Central meridian (degrees)
1279 - lat_0: Latitude of origin (degrees)
1280 - aperture (integer)
1281 - resolution (integer)
1285 \image html ex_isea.gif
1287 template <typename T, typename Parameters>
1288 struct isea_spheroid : public detail::isea::base_isea_spheroid<T, Parameters>
1290 template <typename Params>
1291 inline isea_spheroid(Params const& params, Parameters const& )
1293 detail::isea::setup_isea(params, this->m_proj_parm);
1297 #ifndef DOXYGEN_NO_DETAIL
1301 // Static projection
1302 BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION_F(srs::spar::proj_isea, isea_spheroid)
1305 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_F(isea_entry, isea_spheroid)
1307 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(isea_init)
1309 BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(isea, isea_entry)
1312 } // namespace detail
1315 } // namespace projections
1317 }} // namespace boost::geometry
1319 #endif // BOOST_GEOMETRY_PROJECTIONS_ISEA_HPP