isea.hpp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. // Boost.Geometry - gis-projections (based on PROJ4)
  2. // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2017, 2018, 2019.
  4. // Modifications copyright (c) 2017-2019, Oracle and/or its affiliates.
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. // This file is converted from PROJ4, http://trac.osgeo.org/proj
  10. // PROJ4 is originally written by Gerald Evenden (then of the USGS)
  11. // PROJ4 is maintained by Frank Warmerdam
  12. // PROJ4 is converted to Boost.Geometry by Barend Gehrels
  13. // Last updated version of proj: 5.0.0
  14. // Original copyright notice:
  15. // This code was entirely written by Nathan Wagner
  16. // and is in the public domain.
  17. // Permission is hereby granted, free of charge, to any person obtaining a
  18. // copy of this software and associated documentation files (the "Software"),
  19. // to deal in the Software without restriction, including without limitation
  20. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  21. // and/or sell copies of the Software, and to permit persons to whom the
  22. // Software is furnished to do so, subject to the following conditions:
  23. // The above copyright notice and this permission notice shall be included
  24. // in all copies or substantial portions of the Software.
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  26. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  27. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  28. // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  29. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  30. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  31. // DEALINGS IN THE SOFTWARE.
  32. #ifndef BOOST_GEOMETRY_PROJECTIONS_ISEA_HPP
  33. #define BOOST_GEOMETRY_PROJECTIONS_ISEA_HPP
  34. #include <sstream>
  35. #include <boost/core/ignore_unused.hpp>
  36. #include <boost/geometry/core/assert.hpp>
  37. #include <boost/geometry/srs/projections/impl/base_static.hpp>
  38. #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
  39. #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
  40. #include <boost/geometry/srs/projections/impl/pj_param.hpp>
  41. #include <boost/geometry/srs/projections/impl/projects.hpp>
  42. #include <boost/geometry/util/math.hpp>
  43. namespace boost { namespace geometry
  44. {
  45. namespace projections
  46. {
  47. #ifndef DOXYGEN_NO_DETAIL
  48. namespace detail { namespace isea
  49. {
  50. static const double epsilon = std::numeric_limits<double>::epsilon();
  51. /* sqrt(5)/M_PI */
  52. static const double isea_scale = 0.8301572857837594396028083;
  53. /* 26.565051177 degrees */
  54. static const double v_lat = 0.46364760899944494524;
  55. /* 52.62263186 */
  56. static const double e_rad = 0.91843818702186776133;
  57. /* 10.81231696 */
  58. static const double f_rad = 0.18871053072122403508;
  59. /* R tan(g) sin(60) */
  60. static const double table_g = 0.6615845383;
  61. /* H = 0.25 R tan g = */
  62. static const double table_h = 0.1909830056;
  63. //static const double RPRIME = 0.91038328153090290025;
  64. static const double isea_std_lat = 1.01722196792335072101;
  65. static const double isea_std_lon = .19634954084936207740;
  66. template <typename T>
  67. inline T deg30_rad() { return T(30) * geometry::math::d2r<T>(); }
  68. template <typename T>
  69. inline T deg120_rad() { return T(120) * geometry::math::d2r<T>(); }
  70. template <typename T>
  71. inline T deg72_rad() { return T(72) * geometry::math::d2r<T>(); }
  72. template <typename T>
  73. inline T deg90_rad() { return geometry::math::half_pi<T>(); }
  74. template <typename T>
  75. inline T deg144_rad() { return T(144) * geometry::math::d2r<T>(); }
  76. template <typename T>
  77. inline T deg36_rad() { return T(36) * geometry::math::d2r<T>(); }
  78. template <typename T>
  79. inline T deg108_rad() { return T(108) * geometry::math::d2r<T>(); }
  80. template <typename T>
  81. inline T deg180_rad() { return geometry::math::pi<T>(); }
  82. inline bool downtri(int tri) { return (((tri - 1) / 5) % 2 == 1); }
  83. /*
  84. * Proj 4 provides its own entry points into
  85. * the code, so none of the library functions
  86. * need to be global
  87. */
  88. struct hex {
  89. int iso;
  90. int x, y, z;
  91. };
  92. /* y *must* be positive down as the xy /iso conversion assumes this */
  93. inline
  94. int hex_xy(struct hex *h) {
  95. if (!h->iso) return 1;
  96. if (h->x >= 0) {
  97. h->y = -h->y - (h->x+1)/2;
  98. } else {
  99. /* need to round toward -inf, not toward zero, so x-1 */
  100. h->y = -h->y - h->x/2;
  101. }
  102. h->iso = 0;
  103. return 1;
  104. }
  105. inline
  106. int hex_iso(struct hex *h) {
  107. if (h->iso) return 1;
  108. if (h->x >= 0) {
  109. h->y = (-h->y - (h->x+1)/2);
  110. } else {
  111. /* need to round toward -inf, not toward zero, so x-1 */
  112. h->y = (-h->y - (h->x)/2);
  113. }
  114. h->z = -h->x - h->y;
  115. h->iso = 1;
  116. return 1;
  117. }
  118. template <typename T>
  119. inline
  120. int hexbin2(T const& width, T x, T y, int *i, int *j)
  121. {
  122. T z, rx, ry, rz;
  123. T abs_dx, abs_dy, abs_dz;
  124. int ix, iy, iz, s;
  125. struct hex h;
  126. static const T cos_deg30 = cos(deg30_rad<T>());
  127. x = x / cos_deg30; /* rotated X coord */
  128. y = y - x / 2.0; /* adjustment for rotated X */
  129. /* adjust for actual hexwidth */
  130. x /= width;
  131. y /= width;
  132. z = -x - y;
  133. rx = floor(x + 0.5);
  134. ix = (int)rx;
  135. ry = floor(y + 0.5);
  136. iy = (int)ry;
  137. rz = floor(z + 0.5);
  138. iz = (int)rz;
  139. s = ix + iy + iz;
  140. if (s) {
  141. abs_dx = fabs(rx - x);
  142. abs_dy = fabs(ry - y);
  143. abs_dz = fabs(rz - z);
  144. if (abs_dx >= abs_dy && abs_dx >= abs_dz) {
  145. ix -= s;
  146. } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) {
  147. iy -= s;
  148. } else {
  149. iz -= s;
  150. }
  151. }
  152. h.x = ix;
  153. h.y = iy;
  154. h.z = iz;
  155. h.iso = 1;
  156. hex_xy(&h);
  157. *i = h.x;
  158. *j = h.y;
  159. return ix * 100 + iy;
  160. }
  161. //enum isea_poly { isea_none = 0, isea_icosahedron = 20 };
  162. //enum isea_topology { isea_hexagon=6, isea_triangle=3, isea_diamond=4 };
  163. enum isea_address_form {
  164. /*isea_addr_geo,*/ isea_addr_q2di, isea_addr_seqnum,
  165. /*isea_addr_interleave,*/ isea_addr_plane, isea_addr_q2dd,
  166. isea_addr_projtri, isea_addr_vertex2dd, isea_addr_hex
  167. };
  168. template <typename T>
  169. struct isea_dgg {
  170. T o_lat, o_lon, o_az; /* orientation, radians */
  171. T radius; /* radius of the earth in meters, ignored 1.0 */
  172. unsigned long serial;
  173. //int pole; /* true if standard snyder */
  174. int aperture; /* valid values depend on partitioning method */
  175. int resolution;
  176. int triangle; /* triangle of last transformed point */
  177. int quad; /* quad of last transformed point */
  178. //isea_poly polyhedron; /* ignored, icosahedron */
  179. //isea_topology topology; /* ignored, hexagon */
  180. isea_address_form output; /* an isea_address_form */
  181. };
  182. template <typename T>
  183. struct isea_pt {
  184. T x, y;
  185. };
  186. template <typename T>
  187. struct isea_geo {
  188. T lon, lat;
  189. };
  190. template <typename T>
  191. struct isea_address {
  192. T x,y; /* or i,j or lon,lat depending on type */
  193. int type; /* enum isea_address_form */
  194. int number;
  195. };
  196. /* ENDINC */
  197. enum snyder_polyhedron {
  198. snyder_poly_hexagon = 0, snyder_poly_pentagon = 1,
  199. snyder_poly_tetrahedron = 2, snyder_poly_cube = 3,
  200. snyder_poly_octahedron = 4, snyder_poly_dodecahedron = 5,
  201. snyder_poly_icosahedron = 6
  202. };
  203. template <typename T>
  204. struct snyder_constants {
  205. T g, G, theta, ea_w, ea_a, ea_b, g_w, g_a, g_b;
  206. };
  207. template <typename T>
  208. inline const snyder_constants<T> * constants()
  209. {
  210. /* TODO put these in radians to avoid a later conversion */
  211. static snyder_constants<T> result[] = {
  212. {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0},
  213. {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027},
  214. {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
  215. {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
  216. {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
  217. {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
  218. {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0}
  219. };
  220. return result;
  221. }
  222. template <typename T>
  223. inline const isea_geo<T> * vertex()
  224. {
  225. static isea_geo<T> result[] = {
  226. { 0.0, deg90_rad<T>()},
  227. { deg180_rad<T>(), v_lat},
  228. {-deg108_rad<T>(), v_lat},
  229. {-deg36_rad<T>(), v_lat},
  230. { deg36_rad<T>(), v_lat},
  231. { deg108_rad<T>(), v_lat},
  232. {-deg144_rad<T>(), -v_lat},
  233. {-deg72_rad<T>(), -v_lat},
  234. { 0.0, -v_lat},
  235. { deg72_rad<T>(), -v_lat},
  236. { deg144_rad<T>(), -v_lat},
  237. { 0.0, -deg90_rad<T>()}
  238. };
  239. return result;
  240. }
  241. /* TODO make an isea_pt array of the vertices as well */
  242. 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};
  243. /* triangle Centers */
  244. template <typename T>
  245. inline const isea_geo<T> * icostriangles()
  246. {
  247. static isea_geo<T> result[] = {
  248. { 0.0, 0.0},
  249. {-deg144_rad<T>(), e_rad},
  250. {-deg72_rad<T>(), e_rad},
  251. { 0.0, e_rad},
  252. { deg72_rad<T>(), e_rad},
  253. { deg144_rad<T>(), e_rad},
  254. {-deg144_rad<T>(), f_rad},
  255. {-deg72_rad<T>(), f_rad},
  256. { 0.0, f_rad},
  257. { deg72_rad<T>(), f_rad},
  258. { deg144_rad<T>(), f_rad},
  259. {-deg108_rad<T>(), -f_rad},
  260. {-deg36_rad<T>(), -f_rad},
  261. { deg36_rad<T>(), -f_rad},
  262. { deg108_rad<T>(), -f_rad},
  263. { deg180_rad<T>(), -f_rad},
  264. {-deg108_rad<T>(), -e_rad},
  265. {-deg36_rad<T>(), -e_rad},
  266. { deg36_rad<T>(), -e_rad},
  267. { deg108_rad<T>(), -e_rad},
  268. { deg180_rad<T>(), -e_rad},
  269. };
  270. return result;
  271. }
  272. template <typename T>
  273. inline T az_adjustment(int triangle)
  274. {
  275. T adj;
  276. isea_geo<T> v;
  277. isea_geo<T> c;
  278. v = vertex<T>()[tri_v1[triangle]];
  279. c = icostriangles<T>()[triangle];
  280. /* TODO looks like the adjustment is always either 0 or 180 */
  281. /* at least if you pick your vertex carefully */
  282. adj = atan2(cos(v.lat) * sin(v.lon - c.lon),
  283. cos(c.lat) * sin(v.lat)
  284. - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon));
  285. return adj;
  286. }
  287. template <typename T>
  288. inline isea_pt<T> isea_triangle_xy(int triangle)
  289. {
  290. isea_pt<T> c;
  291. T Rprime = 0.91038328153090290025;
  292. triangle = (triangle - 1) % 20;
  293. c.x = table_g * ((triangle % 5) - 2) * 2.0;
  294. if (triangle > 9) {
  295. c.x += table_g;
  296. }
  297. switch (triangle / 5) {
  298. case 0:
  299. c.y = 5.0 * table_h;
  300. break;
  301. case 1:
  302. c.y = table_h;
  303. break;
  304. case 2:
  305. c.y = -table_h;
  306. break;
  307. case 3:
  308. c.y = -5.0 * table_h;
  309. break;
  310. default:
  311. /* should be impossible */
  312. BOOST_THROW_EXCEPTION( projection_exception() );
  313. };
  314. c.x *= Rprime;
  315. c.y *= Rprime;
  316. return c;
  317. }
  318. /* snyder eq 14 */
  319. template <typename T>
  320. inline T sph_azimuth(T const& f_lon, T const& f_lat, T const& t_lon, T const& t_lat)
  321. {
  322. T az;
  323. az = atan2(cos(t_lat) * sin(t_lon - f_lon),
  324. cos(f_lat) * sin(t_lat)
  325. - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon)
  326. );
  327. return az;
  328. }
  329. /* coord needs to be in radians */
  330. template <typename T>
  331. inline int isea_snyder_forward(isea_geo<T> * ll, isea_pt<T> * out)
  332. {
  333. static T const two_pi = detail::two_pi<T>();
  334. static T const d2r = geometry::math::d2r<T>();
  335. int i;
  336. /*
  337. * spherical distance from center of polygon face to any of its
  338. * vertexes on the globe
  339. */
  340. T g;
  341. /*
  342. * spherical angle between radius vector to center and adjacent edge
  343. * of spherical polygon on the globe
  344. */
  345. T G;
  346. /*
  347. * plane angle between radius vector to center and adjacent edge of
  348. * plane polygon
  349. */
  350. T theta;
  351. /* additional variables from snyder */
  352. T q, Rprime, H, Ag, Azprime, Az, dprime, f, rho,
  353. x, y;
  354. /* variables used to store intermediate results */
  355. T cot_theta, tan_g, az_offset;
  356. /* how many multiples of 60 degrees we adjust the azimuth */
  357. int Az_adjust_multiples;
  358. snyder_constants<T> c;
  359. /*
  360. * TODO by locality of reference, start by trying the same triangle
  361. * as last time
  362. */
  363. /* TODO put these constants in as radians to begin with */
  364. c = constants<T>()[snyder_poly_icosahedron];
  365. theta = c.theta * d2r;
  366. g = c.g * d2r;
  367. G = c.G * d2r;
  368. for (i = 1; i <= 20; i++) {
  369. T z;
  370. isea_geo<T> center;
  371. center = icostriangles<T>()[i];
  372. /* step 1 */
  373. z = acos(sin(center.lat) * sin(ll->lat)
  374. + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon));
  375. /* not on this triangle */
  376. if (z > g + 0.000005) { /* TODO DBL_EPSILON */
  377. continue;
  378. }
  379. Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat);
  380. /* step 2 */
  381. /* This calculates "some" vertex coordinate */
  382. az_offset = az_adjustment<T>(i);
  383. Az -= az_offset;
  384. /* TODO I don't know why we do this. It's not in snyder */
  385. /* maybe because we should have picked a better vertex */
  386. if (Az < 0.0) {
  387. Az += two_pi;
  388. }
  389. /*
  390. * adjust Az for the point to fall within the range of 0 to
  391. * 2(90 - theta) or 60 degrees for the hexagon, by
  392. * and therefore 120 degrees for the triangle
  393. * of the icosahedron
  394. * subtracting or adding multiples of 60 degrees to Az and
  395. * recording the amount of adjustment
  396. */
  397. Az_adjust_multiples = 0;
  398. while (Az < 0.0) {
  399. Az += deg120_rad<T>();
  400. Az_adjust_multiples--;
  401. }
  402. while (Az > deg120_rad<T>() + epsilon) {
  403. Az -= deg120_rad<T>();
  404. Az_adjust_multiples++;
  405. }
  406. /* step 3 */
  407. cot_theta = 1.0 / tan(theta);
  408. tan_g = tan(g); /* TODO this is a constant */
  409. /* Calculate q from eq 9. */
  410. /* TODO cot_theta is cot(30) */
  411. q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta);
  412. /* not in this triangle */
  413. if (z > q + 0.000005) {
  414. continue;
  415. }
  416. /* step 4 */
  417. /* Apply equations 5-8 and 10-12 in order */
  418. /* eq 5 */
  419. /* Rprime = 0.9449322893 * R; */
  420. /* R' in the paper is for the truncated */
  421. Rprime = 0.91038328153090290025;
  422. /* eq 6 */
  423. H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G));
  424. /* eq 7 */
  425. /* Ag = (Az + G + H - deg180_rad) * M_PI * R * R / deg180_rad; */
  426. Ag = Az + G + H - deg180_rad<T>();
  427. /* eq 8 */
  428. Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta);
  429. /* eq 10 */
  430. /* cot(theta) = 1.73205080756887729355 */
  431. dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta);
  432. /* eq 11 */
  433. f = dprime / (2.0 * Rprime * sin(q / 2.0));
  434. /* eq 12 */
  435. rho = 2.0 * Rprime * f * sin(z / 2.0);
  436. /*
  437. * add back the same 60 degree multiple adjustment from step
  438. * 2 to Azprime
  439. */
  440. Azprime += deg120_rad<T>() * Az_adjust_multiples;
  441. /* calculate rectangular coordinates */
  442. x = rho * sin(Azprime);
  443. y = rho * cos(Azprime);
  444. /*
  445. * TODO
  446. * translate coordinates to the origin for the particular
  447. * hexagon on the flattened polyhedral map plot
  448. */
  449. out->x = x;
  450. out->y = y;
  451. return i;
  452. }
  453. /*
  454. * should be impossible, this implies that the coordinate is not on
  455. * any triangle
  456. */
  457. //fprintf(stderr, "impossible transform: %f %f is not on any triangle\n",
  458. // ll->lon * geometry::math::r2d<double>(), ll->lat * geometry::math::r2d<double>());
  459. std::stringstream ss;
  460. ss << "impossible transform: " << ll->lon * geometry::math::r2d<T>()
  461. << " " << ll->lat * geometry::math::r2d<T>() << " is not on any triangle.";
  462. BOOST_THROW_EXCEPTION( projection_exception(ss.str()) );
  463. /* not reached */
  464. return 0; /* supresses a warning */
  465. }
  466. /*
  467. * return the new coordinates of any point in orginal coordinate system.
  468. * Define a point (newNPold) in orginal coordinate system as the North Pole in
  469. * new coordinate system, and the great circle connect the original and new
  470. * North Pole as the lon0 longitude in new coordinate system, given any point
  471. * in orginal coordinate system, this function return the new coordinates.
  472. */
  473. /* formula from Snyder, Map Projections: A working manual, p31 */
  474. /*
  475. * old north pole at np in new coordinates
  476. * could be simplified a bit with fewer intermediates
  477. *
  478. * TODO take a result pointer
  479. */
  480. template <typename T>
  481. inline isea_geo<T> snyder_ctran(isea_geo<T> * np, isea_geo<T> * pt)
  482. {
  483. static T const pi = detail::pi<T>();
  484. static T const two_pi = detail::two_pi<T>();
  485. isea_geo<T> npt;
  486. T alpha, phi, lambda, lambda0, beta, lambdap, phip;
  487. T sin_phip;
  488. T lp_b; /* lambda prime minus beta */
  489. T cos_p, sin_a;
  490. phi = pt->lat;
  491. lambda = pt->lon;
  492. alpha = np->lat;
  493. beta = np->lon;
  494. lambda0 = beta;
  495. cos_p = cos(phi);
  496. sin_a = sin(alpha);
  497. /* mpawm 5-7 */
  498. sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0);
  499. /* mpawm 5-8b */
  500. /* use the two argument form so we end up in the right quadrant */
  501. lp_b = atan2(cos_p * sin(lambda - lambda0),
  502. (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi)));
  503. lambdap = lp_b + beta;
  504. /* normalize longitude */
  505. /* TODO can we just do a modulus ? */
  506. lambdap = fmod(lambdap, two_pi);
  507. while (lambdap > pi)
  508. lambdap -= two_pi;
  509. while (lambdap < -pi)
  510. lambdap += two_pi;
  511. phip = asin(sin_phip);
  512. npt.lat = phip;
  513. npt.lon = lambdap;
  514. return npt;
  515. }
  516. template <typename T>
  517. inline isea_geo<T> isea_ctran(isea_geo<T> * np, isea_geo<T> * pt, T const& lon0)
  518. {
  519. static T const pi = detail::pi<T>();
  520. static T const two_pi = detail::two_pi<T>();
  521. isea_geo<T> npt;
  522. np->lon += pi;
  523. npt = snyder_ctran(np, pt);
  524. np->lon -= pi;
  525. npt.lon -= (pi - lon0 + np->lon);
  526. /*
  527. * snyder is down tri 3, isea is along side of tri1 from vertex 0 to
  528. * vertex 1 these are 180 degrees apart
  529. */
  530. npt.lon += pi;
  531. /* normalize longitude */
  532. npt.lon = fmod(npt.lon, two_pi);
  533. while (npt.lon > pi)
  534. npt.lon -= two_pi;
  535. while (npt.lon < -pi)
  536. npt.lon += two_pi;
  537. return npt;
  538. }
  539. /* in radians */
  540. /* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */
  541. template <typename T>
  542. inline int isea_grid_init(isea_dgg<T> * g)
  543. {
  544. if (!g)
  545. return 0;
  546. //g->polyhedron = isea_icosahedron;
  547. g->o_lat = isea_std_lat;
  548. g->o_lon = isea_std_lon;
  549. g->o_az = 0.0;
  550. g->aperture = 4;
  551. g->resolution = 6;
  552. g->radius = 1.0;
  553. //g->topology = isea_hexagon;
  554. return 1;
  555. }
  556. template <typename T>
  557. inline int isea_orient_isea(isea_dgg<T> * g)
  558. {
  559. if (!g)
  560. return 0;
  561. g->o_lat = isea_std_lat;
  562. g->o_lon = isea_std_lon;
  563. g->o_az = 0.0;
  564. return 1;
  565. }
  566. template <typename T>
  567. inline int isea_orient_pole(isea_dgg<T> * g)
  568. {
  569. static T const half_pi = detail::half_pi<T>();
  570. if (!g)
  571. return 0;
  572. g->o_lat = half_pi;
  573. g->o_lon = 0.0;
  574. g->o_az = 0;
  575. return 1;
  576. }
  577. template <typename T>
  578. inline int isea_transform(isea_dgg<T> * g, isea_geo<T> * in,
  579. isea_pt<T> * out)
  580. {
  581. isea_geo<T> i, pole;
  582. int tri;
  583. pole.lat = g->o_lat;
  584. pole.lon = g->o_lon;
  585. i = isea_ctran(&pole, in, g->o_az);
  586. tri = isea_snyder_forward(&i, out);
  587. out->x *= g->radius;
  588. out->y *= g->radius;
  589. g->triangle = tri;
  590. return tri;
  591. }
  592. template <typename T>
  593. inline void isea_rotate(isea_pt<T> * pt, T const& degrees)
  594. {
  595. static T const d2r = geometry::math::d2r<T>();
  596. static T const two_pi = detail::two_pi<T>();
  597. T rad;
  598. T x, y;
  599. rad = -degrees * d2r;
  600. while (rad >= two_pi) rad -= two_pi;
  601. while (rad <= -two_pi) rad += two_pi;
  602. x = pt->x * cos(rad) + pt->y * sin(rad);
  603. y = -pt->x * sin(rad) + pt->y * cos(rad);
  604. pt->x = x;
  605. pt->y = y;
  606. }
  607. template <typename T>
  608. inline int isea_tri_plane(int tri, isea_pt<T> *pt, T const& radius)
  609. {
  610. isea_pt<T> tc; /* center of triangle */
  611. if (downtri(tri)) {
  612. isea_rotate(pt, 180.0);
  613. }
  614. tc = isea_triangle_xy<T>(tri);
  615. tc.x *= radius;
  616. tc.y *= radius;
  617. pt->x += tc.x;
  618. pt->y += tc.y;
  619. return tri;
  620. }
  621. /* convert projected triangle coords to quad xy coords, return quad number */
  622. template <typename T>
  623. inline int isea_ptdd(int tri, isea_pt<T> *pt)
  624. {
  625. int downtri, quad;
  626. downtri = (((tri - 1) / 5) % 2 == 1);
  627. quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1;
  628. isea_rotate(pt, downtri ? 240.0 : 60.0);
  629. if (downtri) {
  630. pt->x += 0.5;
  631. /* pt->y += cos(30.0 * M_PI / 180.0); */
  632. pt->y += .86602540378443864672;
  633. }
  634. return quad;
  635. }
  636. template <typename T>
  637. inline int isea_dddi_ap3odd(isea_dgg<T> *g, int quad, isea_pt<T> *pt, isea_pt<T> *di)
  638. {
  639. static T const pi = detail::pi<T>();
  640. isea_pt<T> v;
  641. T hexwidth;
  642. T sidelength; /* in hexes */
  643. int d, i;
  644. int maxcoord;
  645. hex h;
  646. /* This is the number of hexes from apex to base of a triangle */
  647. sidelength = (math::pow(T(2), g->resolution) + T(1)) / T(2);
  648. /* apex to base is cos(30deg) */
  649. hexwidth = cos(pi / 6.0) / sidelength;
  650. /* TODO I think sidelength is always x.5, so
  651. * (int)sidelength * 2 + 1 might be just as good
  652. */
  653. maxcoord = (int) (sidelength * 2.0 + 0.5);
  654. v = *pt;
  655. hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
  656. h.iso = 0;
  657. hex_iso(&h);
  658. d = h.x - h.z;
  659. i = h.x + h.y + h.y;
  660. /*
  661. * you want to test for max coords for the next quad in the same
  662. * "row" first to get the case where both are max
  663. */
  664. if (quad <= 5) {
  665. if (d == 0 && i == maxcoord) {
  666. /* north pole */
  667. quad = 0;
  668. d = 0;
  669. i = 0;
  670. } else if (i == maxcoord) {
  671. /* upper right in next quad */
  672. quad += 1;
  673. if (quad == 6)
  674. quad = 1;
  675. i = maxcoord - d;
  676. d = 0;
  677. } else if (d == maxcoord) {
  678. /* lower right in quad to lower right */
  679. quad += 5;
  680. d = 0;
  681. }
  682. } else if (quad >= 6) {
  683. if (i == 0 && d == maxcoord) {
  684. /* south pole */
  685. quad = 11;
  686. d = 0;
  687. i = 0;
  688. } else if (d == maxcoord) {
  689. /* lower right in next quad */
  690. quad += 1;
  691. if (quad == 11)
  692. quad = 6;
  693. d = maxcoord - i;
  694. i = 0;
  695. } else if (i == maxcoord) {
  696. /* upper right in quad to upper right */
  697. quad = (quad - 4) % 5;
  698. i = 0;
  699. }
  700. }
  701. di->x = d;
  702. di->y = i;
  703. g->quad = quad;
  704. return quad;
  705. }
  706. template <typename T>
  707. inline int isea_dddi(isea_dgg<T> *g, int quad, isea_pt<T> *pt, isea_pt<T> *di)
  708. {
  709. isea_pt<T> v;
  710. T hexwidth;
  711. int sidelength; /* in hexes */
  712. hex h;
  713. if (g->aperture == 3 && g->resolution % 2 != 0) {
  714. return isea_dddi_ap3odd(g, quad, pt, di);
  715. }
  716. /* todo might want to do this as an iterated loop */
  717. if (g->aperture >0) {
  718. sidelength = (int) (math::pow(T(g->aperture), T(g->resolution / T(2))) + T(0.5));
  719. } else {
  720. sidelength = g->resolution;
  721. }
  722. hexwidth = 1.0 / sidelength;
  723. v = *pt;
  724. isea_rotate(&v, -30.0);
  725. hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
  726. h.iso = 0;
  727. hex_iso(&h);
  728. /* we may actually be on another quad */
  729. if (quad <= 5) {
  730. if (h.x == 0 && h.z == -sidelength) {
  731. /* north pole */
  732. quad = 0;
  733. h.z = 0;
  734. h.y = 0;
  735. h.x = 0;
  736. } else if (h.z == -sidelength) {
  737. quad = quad + 1;
  738. if (quad == 6)
  739. quad = 1;
  740. h.y = sidelength - h.x;
  741. h.z = h.x - sidelength;
  742. h.x = 0;
  743. } else if (h.x == sidelength) {
  744. quad += 5;
  745. h.y = -h.z;
  746. h.x = 0;
  747. }
  748. } else if (quad >= 6) {
  749. if (h.z == 0 && h.x == sidelength) {
  750. /* south pole */
  751. quad = 11;
  752. h.x = 0;
  753. h.y = 0;
  754. h.z = 0;
  755. } else if (h.x == sidelength) {
  756. quad = quad + 1;
  757. if (quad == 11)
  758. quad = 6;
  759. h.x = h.y + sidelength;
  760. h.y = 0;
  761. h.z = -h.x;
  762. } else if (h.y == -sidelength) {
  763. quad -= 4;
  764. h.y = 0;
  765. h.z = -h.x;
  766. }
  767. }
  768. di->x = h.x;
  769. di->y = -h.z;
  770. g->quad = quad;
  771. return quad;
  772. }
  773. template <typename T>
  774. inline int isea_ptdi(isea_dgg<T> *g, int tri, isea_pt<T> *pt,
  775. isea_pt<T> *di)
  776. {
  777. isea_pt<T> v;
  778. int quad;
  779. v = *pt;
  780. quad = isea_ptdd(tri, &v);
  781. quad = isea_dddi(g, quad, &v, di);
  782. return quad;
  783. }
  784. /* q2di to seqnum */
  785. template <typename T>
  786. inline int isea_disn(isea_dgg<T> *g, int quad, isea_pt<T> *di)
  787. {
  788. int sidelength;
  789. int sn, height;
  790. int hexes;
  791. if (quad == 0) {
  792. g->serial = 1;
  793. return g->serial;
  794. }
  795. /* hexes in a quad */
  796. hexes = (int) (math::pow(T(g->aperture), T(g->resolution)) + T(0.5));
  797. if (quad == 11) {
  798. g->serial = 1 + 10 * hexes + 1;
  799. return g->serial;
  800. }
  801. if (g->aperture == 3 && g->resolution % 2 == 1) {
  802. height = (int) (math::pow(T(g->aperture), T((g->resolution - 1) / T(2))));
  803. sn = ((int) di->x) * height;
  804. sn += ((int) di->y) / height;
  805. sn += (quad - 1) * hexes;
  806. sn += 2;
  807. } else {
  808. sidelength = (int) (math::pow(T(g->aperture), T(g->resolution / T(2))) + T(0.5));
  809. sn = (int) ((quad - 1) * hexes + sidelength * di->x + di->y + 2);
  810. }
  811. g->serial = sn;
  812. return sn;
  813. }
  814. /* TODO just encode the quad in the d or i coordinate
  815. * quad is 0-11, which can be four bits.
  816. * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf
  817. */
  818. /* convert a q2di to global hex coord */
  819. template <typename T>
  820. inline int isea_hex(isea_dgg<T> *g, int tri, isea_pt<T> *pt,
  821. isea_pt<T> *hex)
  822. {
  823. isea_pt<T> v;
  824. #ifdef BOOST_GEOMETRY_PROJECTIONS_FIXME
  825. int sidelength;
  826. int d, i, x, y;
  827. #endif // BOOST_GEOMETRY_PROJECTIONS_FIXME
  828. int quad;
  829. quad = isea_ptdi(g, tri, pt, &v);
  830. hex->x = ((int)v.x << 4) + quad;
  831. hex->y = v.y;
  832. return 1;
  833. #ifdef BOOST_GEOMETRY_PROJECTIONS_FIXME
  834. d = (int)v.x;
  835. i = (int)v.y;
  836. /* Aperture 3 odd resolutions */
  837. if (g->aperture == 3 && g->resolution % 2 != 0) {
  838. int offset = (int)(pow(T(3.0), T(g->resolution - 1)) + 0.5);
  839. d += offset * ((g->quad-1) % 5);
  840. i += offset * ((g->quad-1) % 5);
  841. if (quad == 0) {
  842. d = 0;
  843. i = offset;
  844. } else if (quad == 11) {
  845. d = 2 * offset;
  846. i = 0;
  847. } else if (quad > 5) {
  848. d += offset;
  849. }
  850. x = (2*d - i) /3;
  851. y = (2*i - d) /3;
  852. hex->x = x + offset / 3;
  853. hex->y = y + 2 * offset / 3;
  854. return 1;
  855. }
  856. /* aperture 3 even resolutions and aperture 4 */
  857. sidelength = (int) (pow(T(g->aperture), T(g->resolution / 2.0)) + 0.5);
  858. if (g->quad == 0) {
  859. hex->x = 0;
  860. hex->y = sidelength;
  861. } else if (g->quad == 11) {
  862. hex->x = sidelength * 2;
  863. hex->y = 0;
  864. } else {
  865. hex->x = d + sidelength * ((g->quad-1) % 5);
  866. if (g->quad > 5) hex->x += sidelength;
  867. hex->y = i + sidelength * ((g->quad-1) % 5);
  868. }
  869. return 1;
  870. #endif // BOOST_GEOMETRY_PROJECTIONS_FIXME
  871. }
  872. template <typename T>
  873. inline isea_pt<T> isea_forward(isea_dgg<T> *g, isea_geo<T> *in)
  874. {
  875. int tri;
  876. isea_pt<T> out, coord;
  877. tri = isea_transform(g, in, &out);
  878. if (g->output == isea_addr_plane) {
  879. isea_tri_plane(tri, &out, g->radius);
  880. return out;
  881. }
  882. /* convert to isea standard triangle size */
  883. out.x = out.x / g->radius * isea_scale;
  884. out.y = out.y / g->radius * isea_scale;
  885. out.x += 0.5;
  886. out.y += 2.0 * .14433756729740644112;
  887. switch (g->output) {
  888. case isea_addr_projtri:
  889. /* nothing to do, already in projected triangle */
  890. break;
  891. case isea_addr_vertex2dd:
  892. g->quad = isea_ptdd(tri, &out);
  893. break;
  894. case isea_addr_q2dd:
  895. /* Same as above, we just don't print as much */
  896. g->quad = isea_ptdd(tri, &out);
  897. break;
  898. case isea_addr_q2di:
  899. g->quad = isea_ptdi(g, tri, &out, &coord);
  900. return coord;
  901. break;
  902. case isea_addr_seqnum:
  903. isea_ptdi(g, tri, &out, &coord);
  904. /* disn will set g->serial */
  905. isea_disn(g, g->quad, &coord);
  906. return coord;
  907. break;
  908. case isea_addr_hex:
  909. isea_hex(g, tri, &out, &coord);
  910. return coord;
  911. break;
  912. default:
  913. // isea_addr_plane handled above
  914. BOOST_GEOMETRY_ASSERT(false);
  915. break;
  916. }
  917. return out;
  918. }
  919. /*
  920. * Proj 4 integration code follows
  921. */
  922. template <typename T>
  923. struct par_isea
  924. {
  925. isea_dgg<T> dgg;
  926. };
  927. template <typename T, typename Parameters>
  928. struct base_isea_spheroid
  929. {
  930. par_isea<T> m_proj_parm;
  931. // FORWARD(s_forward)
  932. // Project coordinates from geographic (lon, lat) to cartesian (x, y)
  933. inline void fwd(Parameters const& , T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
  934. {
  935. isea_pt<T> out;
  936. isea_geo<T> in;
  937. in.lon = lp_lon;
  938. in.lat = lp_lat;
  939. isea_dgg<T> copy = this->m_proj_parm.dgg;
  940. out = isea_forward(&copy, &in);
  941. xy_x = out.x;
  942. xy_y = out.y;
  943. }
  944. static inline std::string get_name()
  945. {
  946. return "isea_spheroid";
  947. }
  948. };
  949. template <typename T>
  950. inline void isea_orient_init(srs::detail::proj4_parameters const& params,
  951. par_isea<T>& proj_parm)
  952. {
  953. std::string opt = pj_get_param_s(params, "orient");
  954. if (! opt.empty()) {
  955. if (opt == std::string("isea")) {
  956. isea_orient_isea(&proj_parm.dgg);
  957. } else if (opt == std::string("pole")) {
  958. isea_orient_pole(&proj_parm.dgg);
  959. } else {
  960. BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
  961. }
  962. }
  963. }
  964. template <typename T>
  965. inline void isea_orient_init(srs::dpar::parameters<T> const& params,
  966. par_isea<T>& proj_parm)
  967. {
  968. typename srs::dpar::parameters<T>::const_iterator
  969. it = pj_param_find(params, srs::dpar::orient);
  970. if (it != params.end()) {
  971. srs::dpar::value_orient o = static_cast<srs::dpar::value_orient>(it->template get_value<int>());
  972. if (o == srs::dpar::orient_isea) {
  973. isea_orient_isea(&proj_parm.dgg);
  974. } else if (o == srs::dpar::orient_pole) {
  975. isea_orient_pole(&proj_parm.dgg);
  976. } else {
  977. BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
  978. }
  979. }
  980. }
  981. template <typename T>
  982. inline void isea_mode_init(srs::detail::proj4_parameters const& params,
  983. par_isea<T>& proj_parm)
  984. {
  985. std::string opt = pj_get_param_s(params, "mode");
  986. if (! opt.empty()) {
  987. if (opt == std::string("plane")) {
  988. proj_parm.dgg.output = isea_addr_plane;
  989. } else if (opt == std::string("di")) {
  990. proj_parm.dgg.output = isea_addr_q2di;
  991. } else if (opt == std::string("dd")) {
  992. proj_parm.dgg.output = isea_addr_q2dd;
  993. } else if (opt == std::string("hex")) {
  994. proj_parm.dgg.output = isea_addr_hex;
  995. } else {
  996. BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
  997. }
  998. }
  999. }
  1000. template <typename T>
  1001. inline void isea_mode_init(srs::dpar::parameters<T> const& params,
  1002. par_isea<T>& proj_parm)
  1003. {
  1004. typename srs::dpar::parameters<T>::const_iterator
  1005. it = pj_param_find(params, srs::dpar::mode);
  1006. if (it != params.end()) {
  1007. srs::dpar::value_mode m = static_cast<srs::dpar::value_mode>(it->template get_value<int>());
  1008. if (m == srs::dpar::mode_plane) {
  1009. proj_parm.dgg.output = isea_addr_plane;
  1010. } else if (m == srs::dpar::mode_di) {
  1011. proj_parm.dgg.output = isea_addr_q2di;
  1012. } else if (m == srs::dpar::mode_dd) {
  1013. proj_parm.dgg.output = isea_addr_q2dd;
  1014. } else if (m == srs::dpar::mode_hex) {
  1015. proj_parm.dgg.output = isea_addr_hex;
  1016. } else {
  1017. BOOST_THROW_EXCEPTION( projection_exception(error_ellipsoid_use_required) );
  1018. }
  1019. }
  1020. }
  1021. // Icosahedral Snyder Equal Area
  1022. template <typename Params, typename T>
  1023. inline void setup_isea(Params const& params, par_isea<T>& proj_parm)
  1024. {
  1025. std::string opt;
  1026. isea_grid_init(&proj_parm.dgg);
  1027. proj_parm.dgg.output = isea_addr_plane;
  1028. /* proj_parm.dgg.radius = par.a; / * otherwise defaults to 1 */
  1029. /* calling library will scale, I think */
  1030. isea_orient_init(params, proj_parm);
  1031. pj_param_r<srs::spar::azi>(params, "azi", srs::dpar::azi, proj_parm.dgg.o_az);
  1032. pj_param_r<srs::spar::lon_0>(params, "lon_0", srs::dpar::lon_0, proj_parm.dgg.o_lon);
  1033. pj_param_r<srs::spar::lat_0>(params, "lat_0", srs::dpar::lat_0, proj_parm.dgg.o_lat);
  1034. // TODO: this parameter is set below second time
  1035. pj_param_i<srs::spar::aperture>(params, "aperture", srs::dpar::aperture, proj_parm.dgg.aperture);
  1036. // TODO: this parameter is set below second time
  1037. pj_param_i<srs::spar::resolution>(params, "resolution", srs::dpar::resolution, proj_parm.dgg.resolution);
  1038. isea_mode_init(params, proj_parm);
  1039. // TODO: pj_param_exists -> pj_get_param_b ?
  1040. if (pj_param_exists<srs::spar::rescale>(params, "rescale", srs::dpar::rescale)) {
  1041. proj_parm.dgg.radius = isea_scale;
  1042. }
  1043. if (pj_param_i<srs::spar::resolution>(params, "resolution", srs::dpar::resolution, proj_parm.dgg.resolution)) {
  1044. /* empty */
  1045. } else {
  1046. proj_parm.dgg.resolution = 4;
  1047. }
  1048. if (pj_param_i<srs::spar::aperture>(params, "aperture", srs::dpar::aperture, proj_parm.dgg.aperture)) {
  1049. /* empty */
  1050. } else {
  1051. proj_parm.dgg.aperture = 3;
  1052. }
  1053. }
  1054. }} // namespace detail::isea
  1055. #endif // doxygen
  1056. /*!
  1057. \brief Icosahedral Snyder Equal Area projection
  1058. \ingroup projections
  1059. \tparam Geographic latlong point type
  1060. \tparam Cartesian xy point type
  1061. \tparam Parameters parameter type
  1062. \par Projection characteristics
  1063. - Spheroid
  1064. \par Projection parameters
  1065. - orient (string)
  1066. - azi: Azimuth (or Gamma) (degrees)
  1067. - lon_0: Central meridian (degrees)
  1068. - lat_0: Latitude of origin (degrees)
  1069. - aperture (integer)
  1070. - resolution (integer)
  1071. - mode (string)
  1072. - rescale
  1073. \par Example
  1074. \image html ex_isea.gif
  1075. */
  1076. template <typename T, typename Parameters>
  1077. struct isea_spheroid : public detail::isea::base_isea_spheroid<T, Parameters>
  1078. {
  1079. template <typename Params>
  1080. inline isea_spheroid(Params const& params, Parameters const& )
  1081. {
  1082. detail::isea::setup_isea(params, this->m_proj_parm);
  1083. }
  1084. };
  1085. #ifndef DOXYGEN_NO_DETAIL
  1086. namespace detail
  1087. {
  1088. // Static projection
  1089. BOOST_GEOMETRY_PROJECTIONS_DETAIL_STATIC_PROJECTION_F(srs::spar::proj_isea, isea_spheroid)
  1090. // Factory entry(s)
  1091. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_F(isea_entry, isea_spheroid)
  1092. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(isea_init)
  1093. {
  1094. BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(isea, isea_entry)
  1095. }
  1096. } // namespace detail
  1097. #endif // doxygen
  1098. } // namespace projections
  1099. }} // namespace boost::geometry
  1100. #endif // BOOST_GEOMETRY_PROJECTIONS_ISEA_HPP