gpxosm.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. //==============================================================================
  2. //
  3. // GPXOSM - the gpx openstreetmap class
  4. //
  5. // Copyright (C) 2019 Dick van Oudheusden
  6. //
  7. // This library is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU Lesser General Public
  9. // License as published by the Free Software Foundation; either
  10. // version 3 of the License, or (at your option) any later version.
  11. //
  12. // This library is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. // Lesser General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU Lesser General Public
  18. // License along with this library; if not, write to the Free
  19. // Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. //
  21. //==============================================================================
  22. #include <string>
  23. #include <cstring>
  24. #include <fstream>
  25. #include <sstream>
  26. #include <vector>
  27. #include <ctime>
  28. #include <iomanip>
  29. #include <limits>
  30. #include <algorithm>
  31. #include "Arguments.h"
  32. #include "XMLParser.h"
  33. #include "Algorithm.h"
  34. namespace gpxtools
  35. {
  36. // --Track --------------------------------------------------------
  37. class Track
  38. {
  39. public:
  40. struct Point
  41. {
  42. Point(std::string lat, std::string lon)
  43. {
  44. _lat = lat;
  45. _lon = lon;
  46. }
  47. Point()
  48. {
  49. clear();
  50. }
  51. void clear()
  52. {
  53. _lat.clear();
  54. _lon.clear();
  55. }
  56. std::string _lat;
  57. std::string _lon;
  58. };
  59. typedef std::vector<Point> Points;
  60. enum JoinResult { ERROR, UPDATED, OK };
  61. Track() :
  62. _name(),
  63. _type(),
  64. _copyright(),
  65. _way(),
  66. _relation(),
  67. _relations()
  68. {
  69. }
  70. ~Track()
  71. {
  72. }
  73. void setName(const std::string name) { _name = name; }
  74. void setType(const std::string type) { _type = type; }
  75. void setCopyright(const std::string copyright)
  76. {
  77. if (!copyright.empty()) _copyright = copyright;
  78. }
  79. void startRelation()
  80. {
  81. _relation._number.clear();
  82. _relation._points.clear();
  83. _relation._startPoint.clear();
  84. _relation._startIndex = std::string::npos;
  85. _relation._endPoint.clear();
  86. _relation._endIndex = std::string::npos;
  87. }
  88. void endRelation(const std::string &number)
  89. {
  90. _relation._number = number;
  91. _relations.push_back(_relation);
  92. }
  93. void startWay()
  94. {
  95. _way.clear();
  96. }
  97. void endWay()
  98. {
  99. if (join(_relation._points, _way) != ERROR)
  100. {
  101. _relation._points.insert(_relation._points.end(), _way.begin(), _way.end());
  102. }
  103. }
  104. void addPoint(const Point &point)
  105. {
  106. _way.push_back(point);
  107. }
  108. // Join the relations
  109. bool join()
  110. {
  111. bool ok = align();
  112. if (!ok)
  113. {
  114. report();
  115. }
  116. else
  117. {
  118. reduce();
  119. }
  120. return (ok);
  121. }
  122. void write(std::ostream &str) const
  123. {
  124. str << "<?xml version='1.0'?>" << std::endl;
  125. str << "<gpx version='1.1'>" << std::endl;
  126. if (!_copyright.empty())
  127. {
  128. str << " <metadata>" << std::endl;
  129. str << " <copyright author='" << _copyright << "' />" << std::endl;
  130. str << " </metadata>" << std::endl;
  131. }
  132. str << " <trk>" << std::endl;
  133. if (!_name.empty())
  134. {
  135. str << " <name>" << _name << "</name>" << std::endl;
  136. }
  137. if (!_type.empty())
  138. {
  139. str << " <type>" << _type << "</type>" << std::endl;
  140. }
  141. str << " <trkseg>" << std::endl;
  142. for (auto relation = _relations.begin(); relation != _relations.end(); ++relation)
  143. {
  144. for (auto point = relation->_points.begin(); point != relation->_points.end(); ++point)
  145. {
  146. str << " <trkpt lat='" << point->_lat << "' lon='" << point->_lon << "' />" << std::endl;
  147. }
  148. }
  149. str << " </trkseg>" << std::endl;
  150. str << " </trk>" << std::endl;
  151. str << "</gpx>" << std::endl;
  152. }
  153. private:
  154. struct Relation
  155. {
  156. std::string _number;
  157. Points _points;
  158. Point _startPoint;
  159. size_t _startIndex;
  160. Point _endPoint;
  161. size_t _endIndex;
  162. };
  163. // Align the relations
  164. bool align()
  165. {
  166. bool updated = false;
  167. bool error = false;
  168. // Join the relations
  169. do
  170. {
  171. updated = false;
  172. error = false;
  173. auto previous = _relations.begin();
  174. auto next = (previous != _relations.end() ? previous + 1 : _relations.end());
  175. for (; next != _relations.end(); ++previous, ++next)
  176. {
  177. switch(join(*previous, *next))
  178. {
  179. case ERROR : error = true; break;
  180. case UPDATED: updated = true; break;
  181. case OK : break;
  182. }
  183. }
  184. }
  185. while (updated);
  186. return (!error);
  187. }
  188. // Report unjoined relations
  189. void report()
  190. {
  191. auto previous = _relations.begin();
  192. auto next = (previous != _relations.end() ? previous + 1 : _relations.end());
  193. for (; next != _relations.end(); ++previous, ++next)
  194. {
  195. if (join(previous->_points, next->_points) == ERROR)
  196. {
  197. std::cerr << "Unable to join relations " << previous->_number << " and " << next->_number << ", use join point" << std::endl;
  198. }
  199. }
  200. }
  201. // Reduce the tracks with indices
  202. void reduce()
  203. {
  204. for (auto relation = _relations.begin(); relation != _relations.end(); ++relation)
  205. {
  206. size_t startIndex = relation->_startIndex;
  207. size_t endIndex = relation->_endIndex;
  208. if ((startIndex != std::string::npos) || (endIndex != std::string::npos))
  209. {
  210. if ((startIndex != std::string::npos) &&
  211. (endIndex != std::string::npos) &&
  212. (startIndex > endIndex))
  213. {
  214. std::reverse(relation->_points.begin(), relation->_points.end());
  215. startIndex = relation->_points.size() - startIndex;
  216. endIndex = relation->_points.size() - endIndex;
  217. }
  218. auto start = (startIndex != std::string::npos) ?
  219. relation->_points.begin() + long(startIndex) : relation->_points.begin();
  220. auto end = (endIndex != std::string::npos) ?
  221. relation->_points.begin() + long(endIndex) : relation->_points.end();
  222. relation->_points = Points(start, end);
  223. }
  224. }
  225. }
  226. static double calcDistance(const Point &first, const Point &second)
  227. {
  228. try
  229. {
  230. return gpx::calcDistance(std::stod(first._lat), std::stod(first._lon), std::stod(second._lat), std::stod(second._lon));
  231. }
  232. catch (...)
  233. {
  234. return std::numeric_limits<double>::max();
  235. }
  236. }
  237. static double calcDistance(const Point &point, const Points &points, size_t &index)
  238. {
  239. double minDistance = std::numeric_limits<double>::max();
  240. for (auto iter = points.begin(); iter != points.end(); ++iter)
  241. {
  242. double distance = calcDistance(*iter, point);
  243. if (distance < minDistance)
  244. {
  245. minDistance = distance;
  246. index = iter - points.begin();
  247. }
  248. }
  249. return minDistance;
  250. }
  251. // Join the ways
  252. JoinResult join(Points &previous, Points &next)
  253. {
  254. JoinResult result = OK;
  255. if (previous.empty())
  256. {
  257. // Okee, first track
  258. }
  259. else if (calcDistance(previous.back(), next.front()) < 10.0)
  260. {
  261. // Okee, inline
  262. }
  263. else if (calcDistance(previous.back(), next.back()) < 10.0)
  264. {
  265. std::reverse(next.begin(), next.end());
  266. result = UPDATED;
  267. }
  268. else if (calcDistance(previous.front(), next.front()) < 10.0)
  269. {
  270. std::reverse(previous.begin(), previous.end());
  271. result = UPDATED;
  272. }
  273. else if (calcDistance(previous.front(), next.back()) < 10.0)
  274. {
  275. std::reverse(previous.begin(), previous.end());
  276. std::reverse(next.begin(), next.end());
  277. result = UPDATED;
  278. }
  279. else
  280. {
  281. result = ERROR;
  282. }
  283. return result;
  284. }
  285. // Join relations
  286. JoinResult join(Relation &previous, Relation &next)
  287. {
  288. JoinResult result = OK;
  289. std::size_t index = std::string::npos;
  290. if (calcDistance(previous._points.back(), next._points.front()) < 10.0)
  291. {
  292. // Okee, inline
  293. }
  294. else if (calcDistance(previous._points.back(), next._points.back()) < 10.0)
  295. {
  296. std::reverse(next._points.begin(), next._points.end());
  297. result = UPDATED;
  298. }
  299. else if (calcDistance(previous._points.front(), next._points.front()) < 10.0)
  300. {
  301. std::reverse(previous._points.begin(), previous._points.end());
  302. result = UPDATED;
  303. }
  304. else if (calcDistance(previous._points.front(), next._points.back()) < 10.0)
  305. {
  306. std::reverse(previous._points.begin(), previous._points.end());
  307. std::reverse(next._points.begin(), next._points.end());
  308. result = UPDATED;
  309. }
  310. else if (calcDistance(previous._points.back(), next._points, index) < 10.0)
  311. {
  312. next._startPoint = previous._points.back();
  313. next._startIndex = index;
  314. }
  315. else if (calcDistance(previous._points.front(), next._points, index) < 10.0)
  316. {
  317. std::reverse(previous._points.begin(), previous._points.end());
  318. next._startPoint = previous._points.back();
  319. next._startIndex = index;
  320. result = UPDATED;
  321. }
  322. else if (calcDistance(next._points.front(), previous._points, index) < 10.0)
  323. {
  324. previous._endPoint = next._points.front();
  325. previous._endIndex = index;
  326. }
  327. else if (calcDistance(next._points.back(), previous._points, index) < 10.0)
  328. {
  329. std::reverse(next._points.begin(), next._points.end());
  330. previous._endPoint = next._points.front();
  331. previous._endIndex = index;
  332. result = UPDATED;
  333. }
  334. else
  335. {
  336. result = ERROR;
  337. }
  338. return result;
  339. }
  340. private:
  341. std::string _name;
  342. std::string _type;
  343. std::string _copyright;
  344. Points _way;
  345. Relation _relation;
  346. std::vector<Relation> _relations;
  347. };
  348. // -- Way ---------------------------------------------------------
  349. class Way : public XMLParserHandler
  350. {
  351. public:
  352. Way(const std::string &number, Track &track) :
  353. _track(track),
  354. _number(number),
  355. _xmlParser(this),
  356. _ref(nullptr)
  357. {
  358. }
  359. bool download()
  360. {
  361. bool ok = false;
  362. std::string cmd = "wget -q -O - http://www.openstreetmap.org/api/0.6/way/" + _number + "/full";
  363. FILE *stream = popen(cmd.c_str(), "r");
  364. if (stream != nullptr)
  365. {
  366. char buffer[4096];
  367. ok = true;
  368. while (ok && (fgets(buffer, sizeof(buffer), stream) != nullptr))
  369. {
  370. //std::cout << buffer << std::endl;
  371. ok = _xmlParser.parse(buffer, false);
  372. }
  373. if (ok)
  374. _xmlParser.parse("", true);
  375. pclose(stream);
  376. }
  377. else
  378. {
  379. std::cerr << "Unable to start: " << cmd << std::endl;
  380. }
  381. return ok;
  382. }
  383. private:
  384. std::string fetch(const Attributes &attributes, const std::string &key)
  385. {
  386. auto attribute = attributes.find(key);
  387. return (attribute != attributes.end() ? attribute->second : "");
  388. }
  389. // Interface XMLParserHandler
  390. virtual void startElement(const std::string &path, const std::string &, const Attributes &attributes)
  391. {
  392. if (path == "/osm")
  393. {
  394. _ref = new Ref();
  395. }
  396. if (path == "/osm/node")
  397. {
  398. std::string lat = fetch(attributes, "lat");
  399. std::string lon = fetch(attributes, "lon");
  400. std::string id = fetch(attributes, "id");
  401. if (!lat.empty() && !lon.empty() && !id.empty())
  402. {
  403. //std::cout << "Point:" << lat << ',' << lon << std::endl;
  404. (*_ref)[id] = Track::Point(lat, lon);
  405. }
  406. }
  407. if (path == "/osm/way/nd")
  408. {
  409. std::string ref = fetch(attributes, "ref");
  410. if (!ref.empty() && _ref->find(ref) != _ref->end())
  411. {
  412. auto point = _ref->find(ref);
  413. if (point != _ref->end())
  414. {
  415. _track.addPoint(point->second);
  416. }
  417. }
  418. }
  419. }
  420. virtual void endElement(const std::string &path, const std::string &)
  421. {
  422. if (path == "/osm")
  423. {
  424. delete _ref; _ref = nullptr;
  425. }
  426. }
  427. virtual void text(const std::string &, const std::string &) { }
  428. private:
  429. Track &_track;
  430. std::string _number;
  431. XMLParser _xmlParser;
  432. typedef std::map<std::string, Track::Point> Ref;
  433. Ref *_ref;
  434. Way();
  435. };
  436. // -- Relation ----------------------------------------------------
  437. class Relation : public XMLParserHandler
  438. {
  439. public:
  440. Relation(Track &track) :
  441. _track(track),
  442. _xmlParser(this)
  443. {
  444. }
  445. bool download(const std::string &number)
  446. {
  447. bool ok = false;
  448. std::string cmd = "wget -q -O - http://www.openstreetmap.org/api/0.6/relation/" + number;
  449. FILE *stream = popen(cmd.c_str(), "r");
  450. if (stream != nullptr)
  451. {
  452. char buffer[4096];
  453. ok = true;
  454. while (ok && (fgets(buffer, sizeof(buffer), stream) != nullptr))
  455. {
  456. ok = _xmlParser.parse(buffer, false);
  457. }
  458. if (ok)
  459. _xmlParser.parse("", true);
  460. pclose(stream);
  461. }
  462. else
  463. {
  464. std::cerr << "Unable to start: " << cmd << std::endl;
  465. }
  466. _track.startRelation();
  467. for (auto way = _ways.begin(); way != _ways.end(); ++way)
  468. {
  469. _track.startWay();
  470. (*way)->download();
  471. _track.endWay();
  472. }
  473. _track.endRelation(number);
  474. return ok;
  475. }
  476. private:
  477. std::string fetch(const Attributes &attributes, const std::string &key)
  478. {
  479. auto attribute = attributes.find(key);
  480. return (attribute != attributes.end() ? attribute->second : "");
  481. }
  482. // Interface XMLParserHandler
  483. virtual void startElement(const std::string &path, const std::string &, const Attributes &attributes)
  484. {
  485. if (path == "/osm")
  486. {
  487. _track.setCopyright(fetch(attributes, "copyright"));
  488. }
  489. if (path == "/osm/relation/member")
  490. {
  491. if (fetch(attributes, "type") == "way")
  492. {
  493. std::string way = fetch(attributes, "ref");
  494. //std::cout << "Way:" << way << std::endl;
  495. try
  496. {
  497. std::stol(way);
  498. _ways.push_back(new Way(way, _track));
  499. }
  500. catch(...)
  501. {
  502. std::cerr << "Invalid way: " << way << " in relation ignored." << std::endl;
  503. }
  504. }
  505. }
  506. }
  507. virtual void endElement(const std::string &, const std::string &) { }
  508. virtual void text(const std::string &, const std::string &) { }
  509. private:
  510. Track &_track;
  511. XMLParser _xmlParser;
  512. std::vector<Way*> _ways;
  513. Relation();
  514. };
  515. // -- GPXOSM ------------------------------------------------------
  516. class GPXOSM
  517. {
  518. public:
  519. GPXOSM() :
  520. _arguments("gpxosm [OPTION].. [RELATION]..\nDownload and combine OSM relations to a GPX-file.\n", "gpxosm v0.1",
  521. "Generate a GPX-file on standard output based on a sequence on Openstreetmap relations."),
  522. _name (_arguments, true, 'n', "name", "NAME", "set the name of the GPX-track", ""),
  523. _type (_arguments, true, 't', "type", "TYPE", "set the type of the GPX-track", "")
  524. {
  525. }
  526. ~GPXOSM()
  527. {
  528. }
  529. bool processArguments(int argc, char *argv[])
  530. {
  531. std::vector<std::string> relations;
  532. if (!_arguments.parse(argc,argv, relations))
  533. {
  534. return false;
  535. }
  536. else
  537. {
  538. gpxtools::Track track;
  539. track.setName(_name.value());
  540. track.setType(_type.value());
  541. download(relations, track);
  542. if (track.join())
  543. {
  544. track.write(std::cout);
  545. }
  546. }
  547. return true;
  548. }
  549. private:
  550. void download(const std::vector<std::string> &relations, Track &track)
  551. {
  552. for (auto number = relations.begin(); number != relations.end(); ++number)
  553. {
  554. try
  555. {
  556. std::stol(*number);
  557. Relation relation(track);
  558. relation.download(*number);
  559. }
  560. catch(...)
  561. {
  562. std::cerr << "Invalid relation: " << *number << " ignored." << std::endl;
  563. }
  564. }
  565. }
  566. private:
  567. arg::Arguments _arguments;
  568. arg::Argument _name;
  569. arg::Argument _type;
  570. };
  571. }
  572. int main(int argc, char *argv[])
  573. {
  574. gpxtools::GPXOSM gpxOSM;
  575. if (gpxOSM.processArguments(argc, argv))
  576. {
  577. return 0;
  578. }
  579. return 1;
  580. }
  581. #if 0
  582. <?xml version="1.0" encoding="UTF-8"?>
  583. <osm version="0.6" generator="CGImap 0.6.1 (1898 thorn-03.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
  584. <relation id="9327" visible="true" version="846" changeset="65105226" timestamp="2018-12-02T21:02:37Z" user="SomeoneElse" uid="61942">
  585. <member type="way" ref="26730670" role=""/>
  586. <member type="way" ref="26730671" role=""/>
  587. <member type="way" ref="91649821" role=""/>
  588. <member type="way" ref="529697769" role=""/>
  589. <tag k="name" v="National Byway (Yorkshire)"/>
  590. <tag k="network" v="rcn"/>
  591. <tag k="operator" v="National Byway"/>
  592. <tag k="ref" v="NB"/>
  593. <tag k="route" v="bicycle"/>
  594. <tag k="type" v="route"/>
  595. </relation>
  596. </osm>
  597. <?xml version="1.0" encoding="UTF-8"?>
  598. <osm version="0.6" generator="CGImap 0.6.1 (1909 thorn-01.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
  599. <node id="42881242" visible="true" version="6" changeset="34691835" timestamp="2015-10-17T11:28:08Z" user="vindert" uid="1683540" lat="51.5174500" lon="4.2908800">
  600. <tag k="expected_rwn_route_relations" v="3"/>
  601. <tag k="rwn_ref" v="94"/>
  602. </node>
  603. <node id="42881332" visible="true" version="3" changeset="3451199" timestamp="2009-12-25T22:27:26Z" user="AND_fixbot" uid="211771" lat="51.5174900" lon="4.2909200"/>
  604. <way id="231747375" visible="true" version="4" changeset="27197750" timestamp="2014-12-03T11:29:43Z" user="It's so funny_mechanical" uid="2394881">
  605. <nd ref="42881332"/>
  606. <tag k="highway" v="tertiary"/>
  607. <tag k="name" v="Ligneweg"/>
  608. </way>
  609. </osm>
  610. #endif