isea.hpp 45 KB

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