pj_gridinfo.hpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  1. // Boost.Geometry
  2. // This file is manually converted from PROJ4
  3. // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
  4. // This file was modified by Oracle on 2018, 2019.
  5. // Modifications copyright (c) 2018-2019, Oracle and/or its affiliates.
  6. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. // This file is converted from PROJ4, http://trac.osgeo.org/proj
  11. // PROJ4 is originally written by Gerald Evenden (then of the USGS)
  12. // PROJ4 is maintained by Frank Warmerdam
  13. // This file was converted to Geometry Library by Adam Wulkiewicz
  14. // Original copyright notice:
  15. // Author: Frank Warmerdam, [email protected]
  16. // Copyright (c) 2000, Frank Warmerdam
  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_SRS_PROJECTIONS_IMPL_PJ_GRIDINFO_HPP
  33. #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_GRIDINFO_HPP
  34. #include <boost/geometry/core/assert.hpp>
  35. #include <boost/geometry/util/math.hpp>
  36. #include <boost/algorithm/string/predicate.hpp>
  37. #include <boost/algorithm/string/trim.hpp>
  38. #include <boost/cstdint.hpp>
  39. #include <algorithm>
  40. #include <string>
  41. #include <vector>
  42. namespace boost { namespace geometry { namespace projections
  43. {
  44. namespace detail
  45. {
  46. /************************************************************************/
  47. /* swap_words() */
  48. /* */
  49. /* Convert the byte order of the given word(s) in place. */
  50. /************************************************************************/
  51. inline bool is_lsb()
  52. {
  53. static const int byte_order_test = 1;
  54. static bool result = (1 == ((const unsigned char *) (&byte_order_test))[0]);
  55. return result;
  56. }
  57. inline void swap_words( char *data, int word_size, int word_count )
  58. {
  59. for (int word = 0; word < word_count; word++)
  60. {
  61. for (int i = 0; i < word_size/2; i++)
  62. {
  63. std::swap(data[i], data[word_size-i-1]);
  64. }
  65. data += word_size;
  66. }
  67. }
  68. inline bool cstr_equal(const char * s1, const char * s2, std::size_t n)
  69. {
  70. return std::equal(s1, s1 + n, s2);
  71. }
  72. struct is_trimmable_char
  73. {
  74. inline bool operator()(char ch)
  75. {
  76. return ch == '\n' || ch == ' ';
  77. }
  78. };
  79. // structs originally defined in projects.h
  80. struct pj_ctable
  81. {
  82. struct lp_t { double lam, phi; };
  83. struct flp_t { float lam, phi; };
  84. struct ilp_t { boost::int32_t lam, phi; };
  85. std::string id; // ascii info
  86. lp_t ll; // lower left corner coordinates
  87. lp_t del; // size of cells
  88. ilp_t lim; // limits of conversion matrix
  89. std::vector<flp_t> cvs; // conversion matrix
  90. inline void swap(pj_ctable & r)
  91. {
  92. id.swap(r.id);
  93. std::swap(ll, r.ll);
  94. std::swap(del, r.del);
  95. std::swap(lim, r.lim);
  96. cvs.swap(r.cvs);
  97. }
  98. };
  99. struct pj_gi_load
  100. {
  101. enum format_t { missing = 0, ntv1, ntv2, gtx, ctable, ctable2 };
  102. typedef boost::long_long_type offset_t;
  103. explicit pj_gi_load(std::string const& gname = "",
  104. format_t f = missing,
  105. offset_t off = 0,
  106. bool swap = false)
  107. : gridname(gname)
  108. , format(f)
  109. , grid_offset(off)
  110. , must_swap(swap)
  111. {}
  112. std::string gridname; // identifying name of grid, eg "conus" or ntv2_0.gsb
  113. format_t format; // format of this grid, ie "ctable", "ntv1", "ntv2" or "missing".
  114. offset_t grid_offset; // offset in file, for delayed loading
  115. bool must_swap; // only for NTv2
  116. pj_ctable ct;
  117. inline void swap(pj_gi_load & r)
  118. {
  119. gridname.swap(r.gridname);
  120. std::swap(format, r.format);
  121. std::swap(grid_offset, r.grid_offset);
  122. std::swap(must_swap, r.must_swap);
  123. ct.swap(r.ct);
  124. }
  125. };
  126. struct pj_gi
  127. : pj_gi_load
  128. {
  129. explicit pj_gi(std::string const& gname = "",
  130. pj_gi_load::format_t f = missing,
  131. pj_gi_load::offset_t off = 0,
  132. bool swap = false)
  133. : pj_gi_load(gname, f, off, swap)
  134. {}
  135. std::vector<pj_gi> children;
  136. inline void swap(pj_gi & r)
  137. {
  138. pj_gi_load::swap(r);
  139. children.swap(r.children);
  140. }
  141. };
  142. typedef std::vector<pj_gi> pj_gridinfo;
  143. /************************************************************************/
  144. /* pj_gridinfo_load_ctable() */
  145. /* */
  146. /* Load the data portion of a ctable formatted grid. */
  147. /************************************************************************/
  148. // Originally nad_ctable_load() defined in nad_init.c
  149. template <typename IStream>
  150. bool pj_gridinfo_load_ctable(IStream & is, pj_gi_load & gi)
  151. {
  152. pj_ctable & ct = gi.ct;
  153. // Move the input stream by the size of the proj4 original CTABLE
  154. std::size_t header_size = 80
  155. + 2 * sizeof(pj_ctable::lp_t)
  156. + sizeof(pj_ctable::ilp_t)
  157. + sizeof(pj_ctable::flp_t*);
  158. is.seekg(header_size);
  159. // read all the actual shift values
  160. std::size_t a_size = ct.lim.lam * ct.lim.phi;
  161. ct.cvs.resize(a_size);
  162. std::size_t ch_size = sizeof(pj_ctable::flp_t) * a_size;
  163. is.read(reinterpret_cast<char*>(&ct.cvs[0]), ch_size);
  164. if (is.fail() || std::size_t(is.gcount()) != ch_size)
  165. {
  166. ct.cvs.clear();
  167. //ctable loading failed on fread() - binary incompatible?
  168. return false;
  169. }
  170. return true;
  171. }
  172. /************************************************************************/
  173. /* pj_gridinfo_load_ctable2() */
  174. /* */
  175. /* Load the data portion of a ctable2 formatted grid. */
  176. /************************************************************************/
  177. // Originally nad_ctable2_load() defined in nad_init.c
  178. template <typename IStream>
  179. bool pj_gridinfo_load_ctable2(IStream & is, pj_gi_load & gi)
  180. {
  181. pj_ctable & ct = gi.ct;
  182. is.seekg(160);
  183. // read all the actual shift values
  184. std::size_t a_size = ct.lim.lam * ct.lim.phi;
  185. ct.cvs.resize(a_size);
  186. std::size_t ch_size = sizeof(pj_ctable::flp_t) * a_size;
  187. is.read(reinterpret_cast<char*>(&ct.cvs[0]), ch_size);
  188. if (is.fail() || std::size_t(is.gcount()) != ch_size)
  189. {
  190. //ctable2 loading failed on fread() - binary incompatible?
  191. ct.cvs.clear();
  192. return false;
  193. }
  194. if (! is_lsb())
  195. {
  196. swap_words(reinterpret_cast<char*>(&ct.cvs[0]), 4, (int)a_size * 2);
  197. }
  198. return true;
  199. }
  200. /************************************************************************/
  201. /* pj_gridinfo_load_ntv1() */
  202. /* */
  203. /* NTv1 format. */
  204. /* We process one line at a time. Note that the array storage */
  205. /* direction (e-w) is different in the NTv1 file and what */
  206. /* the CTABLE is supposed to have. The phi/lam are also */
  207. /* reversed, and we have to be aware of byte swapping. */
  208. /************************************************************************/
  209. // originally in pj_gridinfo_load() function
  210. template <typename IStream>
  211. inline bool pj_gridinfo_load_ntv1(IStream & is, pj_gi_load & gi)
  212. {
  213. static const double s2r = math::d2r<double>() / 3600.0;
  214. std::size_t const r_size = gi.ct.lim.lam * 2;
  215. std::size_t const ch_size = sizeof(double) * r_size;
  216. is.seekg(gi.grid_offset);
  217. std::vector<double> row_buf(r_size);
  218. gi.ct.cvs.resize(gi.ct.lim.lam * gi.ct.lim.phi);
  219. for (boost::int32_t row = 0; row < gi.ct.lim.phi; row++ )
  220. {
  221. is.read(reinterpret_cast<char*>(&row_buf[0]), ch_size);
  222. if (is.fail() || std::size_t(is.gcount()) != ch_size)
  223. {
  224. gi.ct.cvs.clear();
  225. return false;
  226. }
  227. if (is_lsb())
  228. swap_words(reinterpret_cast<char*>(&row_buf[0]), 8, (int)r_size);
  229. // convert seconds to radians
  230. for (boost::int32_t i = 0; i < gi.ct.lim.lam; i++ )
  231. {
  232. pj_ctable::flp_t & cvs = gi.ct.cvs[row * gi.ct.lim.lam + (gi.ct.lim.lam - i - 1)];
  233. cvs.phi = (float) (row_buf[i*2] * s2r);
  234. cvs.lam = (float) (row_buf[i*2+1] * s2r);
  235. }
  236. }
  237. return true;
  238. }
  239. /* -------------------------------------------------------------------- */
  240. /* pj_gridinfo_load_ntv2() */
  241. /* */
  242. /* NTv2 format. */
  243. /* We process one line at a time. Note that the array storage */
  244. /* direction (e-w) is different in the NTv2 file and what */
  245. /* the CTABLE is supposed to have. The phi/lam are also */
  246. /* reversed, and we have to be aware of byte swapping. */
  247. /* -------------------------------------------------------------------- */
  248. // originally in pj_gridinfo_load() function
  249. template <typename IStream>
  250. inline bool pj_gridinfo_load_ntv2(IStream & is, pj_gi_load & gi)
  251. {
  252. static const double s2r = math::d2r<double>() / 3600.0;
  253. std::size_t const r_size = gi.ct.lim.lam * 4;
  254. std::size_t const ch_size = sizeof(float) * r_size;
  255. is.seekg(gi.grid_offset);
  256. std::vector<float> row_buf(r_size);
  257. gi.ct.cvs.resize(gi.ct.lim.lam * gi.ct.lim.phi);
  258. for (boost::int32_t row = 0; row < gi.ct.lim.phi; row++ )
  259. {
  260. is.read(reinterpret_cast<char*>(&row_buf[0]), ch_size);
  261. if (is.fail() || std::size_t(is.gcount()) != ch_size)
  262. {
  263. gi.ct.cvs.clear();
  264. return false;
  265. }
  266. if (gi.must_swap)
  267. {
  268. swap_words(reinterpret_cast<char*>(&row_buf[0]), 4, (int)r_size);
  269. }
  270. // convert seconds to radians
  271. for (boost::int32_t i = 0; i < gi.ct.lim.lam; i++ )
  272. {
  273. pj_ctable::flp_t & cvs = gi.ct.cvs[row * gi.ct.lim.lam + (gi.ct.lim.lam - i - 1)];
  274. // skip accuracy values
  275. cvs.phi = (float) (row_buf[i*4] * s2r);
  276. cvs.lam = (float) (row_buf[i*4+1] * s2r);
  277. }
  278. }
  279. return true;
  280. }
  281. /************************************************************************/
  282. /* pj_gridinfo_load_gtx() */
  283. /* */
  284. /* GTX format. */
  285. /************************************************************************/
  286. // originally in pj_gridinfo_load() function
  287. template <typename IStream>
  288. inline bool pj_gridinfo_load_gtx(IStream & is, pj_gi_load & gi)
  289. {
  290. boost::int32_t words = gi.ct.lim.lam * gi.ct.lim.phi;
  291. std::size_t const ch_size = sizeof(float) * words;
  292. is.seekg(gi.grid_offset);
  293. // TODO: Consider changing this unintuitive code
  294. // NOTE: Vertical shift data (one float per point) is stored in a container
  295. // holding horizontal shift data (two floats per point).
  296. gi.ct.cvs.resize((words + 1) / 2);
  297. is.read(reinterpret_cast<char*>(&gi.ct.cvs[0]), ch_size);
  298. if (is.fail() || std::size_t(is.gcount()) != ch_size)
  299. {
  300. gi.ct.cvs.clear();
  301. return false;
  302. }
  303. if (is_lsb())
  304. {
  305. swap_words(reinterpret_cast<char*>(&gi.ct.cvs[0]), 4, words);
  306. }
  307. return true;
  308. }
  309. /************************************************************************/
  310. /* pj_gridinfo_load() */
  311. /* */
  312. /* This function is intended to implement delayed loading of */
  313. /* the data contents of a grid file. The header and related */
  314. /* stuff are loaded by pj_gridinfo_init(). */
  315. /************************************************************************/
  316. template <typename IStream>
  317. inline bool pj_gridinfo_load(IStream & is, pj_gi_load & gi)
  318. {
  319. if (! gi.ct.cvs.empty())
  320. {
  321. return true;
  322. }
  323. if (! is.is_open())
  324. {
  325. return false;
  326. }
  327. // Original platform specific CTable format.
  328. if (gi.format == pj_gi::ctable)
  329. {
  330. return pj_gridinfo_load_ctable(is, gi);
  331. }
  332. // CTable2 format.
  333. else if (gi.format == pj_gi::ctable2)
  334. {
  335. return pj_gridinfo_load_ctable2(is, gi);
  336. }
  337. // NTv1 format.
  338. else if (gi.format == pj_gi::ntv1)
  339. {
  340. return pj_gridinfo_load_ntv1(is, gi);
  341. }
  342. // NTv2 format.
  343. else if (gi.format == pj_gi::ntv2)
  344. {
  345. return pj_gridinfo_load_ntv2(is, gi);
  346. }
  347. // GTX format.
  348. else if (gi.format == pj_gi::gtx)
  349. {
  350. return pj_gridinfo_load_gtx(is, gi);
  351. }
  352. else
  353. {
  354. return false;
  355. }
  356. }
  357. /************************************************************************/
  358. /* pj_gridinfo_parent() */
  359. /* */
  360. /* Seek a parent grid file by name from a grid list */
  361. /************************************************************************/
  362. template <typename It>
  363. inline It pj_gridinfo_parent(It first, It last, std::string const& name)
  364. {
  365. for ( ; first != last ; ++first)
  366. {
  367. if (first->ct.id == name)
  368. return first;
  369. It parent = pj_gridinfo_parent(first->children.begin(), first->children.end(), name);
  370. if( parent != first->children.end() )
  371. return parent;
  372. }
  373. return last;
  374. }
  375. /************************************************************************/
  376. /* pj_gridinfo_init_ntv2() */
  377. /* */
  378. /* Load a ntv2 (.gsb) file. */
  379. /************************************************************************/
  380. template <typename IStream>
  381. inline bool pj_gridinfo_init_ntv2(std::string const& gridname,
  382. IStream & is,
  383. pj_gridinfo & gridinfo)
  384. {
  385. BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
  386. BOOST_STATIC_ASSERT( sizeof(double) == 8 );
  387. static const double s2r = math::d2r<double>() / 3600.0;
  388. std::size_t gridinfo_orig_size = gridinfo.size();
  389. // Read the overview header.
  390. char header[11*16];
  391. is.read(header, sizeof(header));
  392. if( is.fail() )
  393. {
  394. return false;
  395. }
  396. bool must_swap = (header[8] == 11)
  397. ? !is_lsb()
  398. : is_lsb();
  399. // NOTE: This check is not implemented in proj4
  400. if (! cstr_equal(header + 56, "SECONDS", 7))
  401. {
  402. return false;
  403. }
  404. // Byte swap interesting fields if needed.
  405. if( must_swap )
  406. {
  407. swap_words( header+8, 4, 1 );
  408. swap_words( header+8+16, 4, 1 );
  409. swap_words( header+8+32, 4, 1 );
  410. swap_words( header+8+7*16, 8, 1 );
  411. swap_words( header+8+8*16, 8, 1 );
  412. swap_words( header+8+9*16, 8, 1 );
  413. swap_words( header+8+10*16, 8, 1 );
  414. }
  415. // Get the subfile count out ... all we really use for now.
  416. boost::int32_t num_subfiles;
  417. memcpy( &num_subfiles, header+8+32, 4 );
  418. // Step through the subfiles, creating a PJ_GRIDINFO for each.
  419. for( boost::int32_t subfile = 0; subfile < num_subfiles; subfile++ )
  420. {
  421. // Read header.
  422. is.read(header, sizeof(header));
  423. if( is.fail() )
  424. {
  425. return false;
  426. }
  427. if(! cstr_equal(header, "SUB_NAME", 8))
  428. {
  429. return false;
  430. }
  431. // Byte swap interesting fields if needed.
  432. if( must_swap )
  433. {
  434. swap_words( header+8+16*4, 8, 1 );
  435. swap_words( header+8+16*5, 8, 1 );
  436. swap_words( header+8+16*6, 8, 1 );
  437. swap_words( header+8+16*7, 8, 1 );
  438. swap_words( header+8+16*8, 8, 1 );
  439. swap_words( header+8+16*9, 8, 1 );
  440. swap_words( header+8+16*10, 4, 1 );
  441. }
  442. // Initialize a corresponding "ct" structure.
  443. pj_ctable ct;
  444. pj_ctable::lp_t ur;
  445. ct.id = std::string(header + 8, 8);
  446. ct.ll.lam = - *((double *) (header+7*16+8)); /* W_LONG */
  447. ct.ll.phi = *((double *) (header+4*16+8)); /* S_LAT */
  448. ur.lam = - *((double *) (header+6*16+8)); /* E_LONG */
  449. ur.phi = *((double *) (header+5*16+8)); /* N_LAT */
  450. ct.del.lam = *((double *) (header+9*16+8));
  451. ct.del.phi = *((double *) (header+8*16+8));
  452. ct.lim.lam = (boost::int32_t) (fabs(ur.lam-ct.ll.lam)/ct.del.lam + 0.5) + 1;
  453. ct.lim.phi = (boost::int32_t) (fabs(ur.phi-ct.ll.phi)/ct.del.phi + 0.5) + 1;
  454. ct.ll.lam *= s2r;
  455. ct.ll.phi *= s2r;
  456. ct.del.lam *= s2r;
  457. ct.del.phi *= s2r;
  458. boost::int32_t gs_count;
  459. memcpy( &gs_count, header + 8 + 16*10, 4 );
  460. if( gs_count != ct.lim.lam * ct.lim.phi )
  461. {
  462. return false;
  463. }
  464. //ct.cvs.clear();
  465. // Create a new gridinfo for this if we aren't processing the
  466. // 1st subfile, and initialize our grid info.
  467. // Attach to the correct list or sublist.
  468. // TODO is offset needed?
  469. pj_gi gi(gridname, pj_gi::ntv2, is.tellg(), must_swap);
  470. gi.ct = ct;
  471. if( subfile == 0 )
  472. {
  473. gridinfo.push_back(gi);
  474. }
  475. else if( cstr_equal(header+24, "NONE", 4) )
  476. {
  477. gridinfo.push_back(gi);
  478. }
  479. else
  480. {
  481. pj_gridinfo::iterator git = pj_gridinfo_parent(gridinfo.begin() + gridinfo_orig_size,
  482. gridinfo.end(),
  483. std::string((const char*)header+24, 8));
  484. if( git == gridinfo.end() )
  485. {
  486. gridinfo.push_back(gi);
  487. }
  488. else
  489. {
  490. git->children.push_back(gi);
  491. }
  492. }
  493. // Seek past the data.
  494. is.seekg(gs_count * 16, std::ios::cur);
  495. }
  496. return true;
  497. }
  498. /************************************************************************/
  499. /* pj_gridinfo_init_ntv1() */
  500. /* */
  501. /* Load an NTv1 style Canadian grid shift file. */
  502. /************************************************************************/
  503. template <typename IStream>
  504. inline bool pj_gridinfo_init_ntv1(std::string const& gridname,
  505. IStream & is,
  506. pj_gridinfo & gridinfo)
  507. {
  508. BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
  509. BOOST_STATIC_ASSERT( sizeof(double) == 8 );
  510. static const double d2r = math::d2r<double>();
  511. // Read the header.
  512. char header[176];
  513. is.read(header, sizeof(header));
  514. if( is.fail() )
  515. {
  516. return false;
  517. }
  518. // Regularize fields of interest.
  519. if( is_lsb() )
  520. {
  521. swap_words( header+8, 4, 1 );
  522. swap_words( header+24, 8, 1 );
  523. swap_words( header+40, 8, 1 );
  524. swap_words( header+56, 8, 1 );
  525. swap_words( header+72, 8, 1 );
  526. swap_words( header+88, 8, 1 );
  527. swap_words( header+104, 8, 1 );
  528. }
  529. // NTv1 grid shift file has wrong record count, corrupt?
  530. if( *((boost::int32_t *) (header+8)) != 12 )
  531. {
  532. return false;
  533. }
  534. // NOTE: This check is not implemented in proj4
  535. if (! cstr_equal(header + 120, "SECONDS", 7))
  536. {
  537. return false;
  538. }
  539. // Fill in CTABLE structure.
  540. pj_ctable ct;
  541. pj_ctable::lp_t ur;
  542. ct.id = "NTv1 Grid Shift File";
  543. ct.ll.lam = - *((double *) (header+72));
  544. ct.ll.phi = *((double *) (header+24));
  545. ur.lam = - *((double *) (header+56));
  546. ur.phi = *((double *) (header+40));
  547. ct.del.lam = *((double *) (header+104));
  548. ct.del.phi = *((double *) (header+88));
  549. ct.lim.lam = (boost::int32_t) (fabs(ur.lam-ct.ll.lam)/ct.del.lam + 0.5) + 1;
  550. ct.lim.phi = (boost::int32_t) (fabs(ur.phi-ct.ll.phi)/ct.del.phi + 0.5) + 1;
  551. ct.ll.lam *= d2r;
  552. ct.ll.phi *= d2r;
  553. ct.del.lam *= d2r;
  554. ct.del.phi *= d2r;
  555. //ct.cvs.clear();
  556. // is offset needed?
  557. gridinfo.push_back(pj_gi(gridname, pj_gi::ntv1, is.tellg()));
  558. gridinfo.back().ct = ct;
  559. return true;
  560. }
  561. /************************************************************************/
  562. /* pj_gridinfo_init_gtx() */
  563. /* */
  564. /* Load a NOAA .gtx vertical datum shift file. */
  565. /************************************************************************/
  566. template <typename IStream>
  567. inline bool pj_gridinfo_init_gtx(std::string const& gridname,
  568. IStream & is,
  569. pj_gridinfo & gridinfo)
  570. {
  571. BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
  572. BOOST_STATIC_ASSERT( sizeof(double) == 8 );
  573. static const double d2r = math::d2r<double>();
  574. // Read the header.
  575. char header[40];
  576. is.read(header, sizeof(header));
  577. if( is.fail() )
  578. {
  579. return false;
  580. }
  581. // Regularize fields of interest and extract.
  582. double xorigin, yorigin, xstep, ystep;
  583. boost::int32_t rows, columns;
  584. if( is_lsb() )
  585. {
  586. swap_words( header+0, 8, 4 );
  587. swap_words( header+32, 4, 2 );
  588. }
  589. memcpy( &yorigin, header+0, 8 );
  590. memcpy( &xorigin, header+8, 8 );
  591. memcpy( &ystep, header+16, 8 );
  592. memcpy( &xstep, header+24, 8 );
  593. memcpy( &rows, header+32, 4 );
  594. memcpy( &columns, header+36, 4 );
  595. // gtx file header has invalid extents, corrupt?
  596. if( xorigin < -360 || xorigin > 360
  597. || yorigin < -90 || yorigin > 90 )
  598. {
  599. return false;
  600. }
  601. // Fill in CTABLE structure.
  602. pj_ctable ct;
  603. ct.id = "GTX Vertical Grid Shift File";
  604. ct.ll.lam = xorigin;
  605. ct.ll.phi = yorigin;
  606. ct.del.lam = xstep;
  607. ct.del.phi = ystep;
  608. ct.lim.lam = columns;
  609. ct.lim.phi = rows;
  610. // some GTX files come in 0-360 and we shift them back into the
  611. // expected -180 to 180 range if possible. This does not solve
  612. // problems with grids spanning the dateline.
  613. if( ct.ll.lam >= 180.0 )
  614. ct.ll.lam -= 360.0;
  615. if( ct.ll.lam >= 0.0 && ct.ll.lam + ct.del.lam * ct.lim.lam > 180.0 )
  616. {
  617. //"This GTX spans the dateline! This will cause problems." );
  618. }
  619. ct.ll.lam *= d2r;
  620. ct.ll.phi *= d2r;
  621. ct.del.lam *= d2r;
  622. ct.del.phi *= d2r;
  623. //ct.cvs.clear();
  624. // is offset needed?
  625. gridinfo.push_back(pj_gi(gridname, pj_gi::gtx, 40));
  626. gridinfo.back().ct = ct;
  627. return true;
  628. }
  629. /************************************************************************/
  630. /* pj_gridinfo_init_ctable2() */
  631. /* */
  632. /* Read the header portion of a "ctable2" format grid. */
  633. /************************************************************************/
  634. // Originally nad_ctable2_init() defined in nad_init.c
  635. template <typename IStream>
  636. inline bool pj_gridinfo_init_ctable2(std::string const& gridname,
  637. IStream & is,
  638. pj_gridinfo & gridinfo)
  639. {
  640. BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
  641. BOOST_STATIC_ASSERT( sizeof(double) == 8 );
  642. char header[160];
  643. is.read(header, sizeof(header));
  644. if( is.fail() )
  645. {
  646. return false;
  647. }
  648. if( !is_lsb() )
  649. {
  650. swap_words( header + 96, 8, 4 );
  651. swap_words( header + 128, 4, 2 );
  652. }
  653. // ctable2 - wrong header!
  654. if (! cstr_equal(header, "CTABLE V2", 9))
  655. {
  656. return false;
  657. }
  658. // read the table header
  659. pj_ctable ct;
  660. ct.id = std::string(header + 16, std::find(header + 16, header + 16 + 80, '\0'));
  661. //memcpy( &ct.ll.lam, header + 96, 8 );
  662. //memcpy( &ct.ll.phi, header + 104, 8 );
  663. //memcpy( &ct.del.lam, header + 112, 8 );
  664. //memcpy( &ct.del.phi, header + 120, 8 );
  665. //memcpy( &ct.lim.lam, header + 128, 4 );
  666. //memcpy( &ct.lim.phi, header + 132, 4 );
  667. memcpy( &ct.ll, header + 96, 40 );
  668. // do some minimal validation to ensure the structure isn't corrupt
  669. if ( (ct.lim.lam < 1) || (ct.lim.lam > 100000)
  670. || (ct.lim.phi < 1) || (ct.lim.phi > 100000) )
  671. {
  672. return false;
  673. }
  674. // trim white space and newlines off id
  675. boost::trim_right_if(ct.id, is_trimmable_char());
  676. //ct.cvs.clear();
  677. gridinfo.push_back(pj_gi(gridname, pj_gi::ctable2));
  678. gridinfo.back().ct = ct;
  679. return true;
  680. }
  681. /************************************************************************/
  682. /* pj_gridinfo_init_ctable() */
  683. /* */
  684. /* Read the header portion of a "ctable" format grid. */
  685. /************************************************************************/
  686. // Originally nad_ctable_init() defined in nad_init.c
  687. template <typename IStream>
  688. inline bool pj_gridinfo_init_ctable(std::string const& gridname,
  689. IStream & is,
  690. pj_gridinfo & gridinfo)
  691. {
  692. BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
  693. BOOST_STATIC_ASSERT( sizeof(double) == 8 );
  694. // 80 + 2*8 + 2*8 + 2*4
  695. char header[120];
  696. // NOTE: in proj4 data is loaded directly into CTABLE
  697. is.read(header, sizeof(header));
  698. if( is.fail() )
  699. {
  700. return false;
  701. }
  702. // NOTE: in proj4 LSB is not checked here
  703. // read the table header
  704. pj_ctable ct;
  705. ct.id = std::string(header, std::find(header, header + 80, '\0'));
  706. memcpy( &ct.ll, header + 80, 40 );
  707. // do some minimal validation to ensure the structure isn't corrupt
  708. if ( (ct.lim.lam < 1) || (ct.lim.lam > 100000)
  709. || (ct.lim.phi < 1) || (ct.lim.phi > 100000) )
  710. {
  711. return false;
  712. }
  713. // trim white space and newlines off id
  714. boost::trim_right_if(ct.id, is_trimmable_char());
  715. //ct.cvs.clear();
  716. gridinfo.push_back(pj_gi(gridname, pj_gi::ctable));
  717. gridinfo.back().ct = ct;
  718. return true;
  719. }
  720. /************************************************************************/
  721. /* pj_gridinfo_init() */
  722. /* */
  723. /* Open and parse header details from a datum gridshift file */
  724. /* returning a list of PJ_GRIDINFOs for the grids in that */
  725. /* file. This superceeds use of nad_init() for modern */
  726. /* applications. */
  727. /************************************************************************/
  728. template <typename IStream>
  729. inline bool pj_gridinfo_init(std::string const& gridname,
  730. IStream & is,
  731. pj_gridinfo & gridinfo)
  732. {
  733. char header[160];
  734. // Check if the stream is opened.
  735. if (! is.is_open()) {
  736. return false;
  737. }
  738. // Load a header, to determine the file type.
  739. is.read(header, sizeof(header));
  740. if ( is.fail() ) {
  741. return false;
  742. }
  743. is.seekg(0);
  744. // Determine file type.
  745. if ( cstr_equal(header + 0, "HEADER", 6)
  746. && cstr_equal(header + 96, "W GRID", 6)
  747. && cstr_equal(header + 144, "TO NAD83 ", 16) )
  748. {
  749. return pj_gridinfo_init_ntv1(gridname, is, gridinfo);
  750. }
  751. else if( cstr_equal(header + 0, "NUM_OREC", 8)
  752. && cstr_equal(header + 48, "GS_TYPE", 7) )
  753. {
  754. return pj_gridinfo_init_ntv2(gridname, is, gridinfo);
  755. }
  756. else if( boost::algorithm::ends_with(gridname, "gtx")
  757. || boost::algorithm::ends_with(gridname, "GTX") )
  758. {
  759. return pj_gridinfo_init_gtx(gridname, is, gridinfo);
  760. }
  761. else if( cstr_equal(header + 0, "CTABLE V2", 9) )
  762. {
  763. return pj_gridinfo_init_ctable2(gridname, is, gridinfo);
  764. }
  765. else
  766. {
  767. return pj_gridinfo_init_ctable(gridname, is, gridinfo);
  768. }
  769. }
  770. } // namespace detail
  771. }}} // namespace boost::geometry::projections
  772. #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_GRIDINFO_HPP