summaryrefslogtreecommitdiffstats
path: root/server/src/gpx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/gpx.cpp')
-rw-r--r--server/src/gpx.cpp341
1 files changed, 195 insertions, 146 deletions
diff --git a/server/src/gpx.cpp b/server/src/gpx.cpp
index 62dccff..44a6bb2 100644
--- a/server/src/gpx.cpp
+++ b/server/src/gpx.cpp
@@ -13,177 +13,226 @@ using namespace std::literals::string_view_literals;
13 13
14namespace routemon::gpx { 14namespace routemon::gpx {
15 15
16 namespace v10 { 16namespace v10 {
17 17
18 constexpr auto xmlns = "http://www.topografix.com/GPX/1/0"sv; 18constexpr auto xmlns = "http://www.topografix.com/GPX/1/0"sv;
19 auto qname(std::string_view local) -> xml::qname_view { 19auto qname(std::string_view local) -> xml::qname_view
20 return {.ns_uri = xmlns, .local = local}; 20{
21 } 21 return {.ns_uri = xmlns, .local = local};
22}
22 23
23 auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) -> xml::parser<geo::point> { 24auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs)
24 auto parse_xml_double = [](std::string_view sv) -> std::optional<double> { 25 -> xml::parser<geo::point>
25 return util::parse_double(sv, std::chars_format::fixed); 26{
26 }; 27 auto parse_xml_double = [](std::string_view sv) -> std::optional<double>
27 auto mlat = std::optional<double>{}; 28 { return util::parse_double(sv, std::chars_format::fixed); };
28 auto mlon = std::optional<double>{}; 29 auto mlat = std::optional<double>{};
29 for (auto const& [name, value] : attrs) { 30 auto mlon = std::optional<double>{};
30 if (name == qname("lat")) { 31 for (auto const& [name, value] : attrs)
31 mlat = parse_xml_double(value); 32 {
32 } else if (name == qname("lon")) { 33 if (name == qname("lat"))
33 mlon = parse_xml_double(value); 34 {
34 } 35 mlat = parse_xml_double(value);
35 }
36 if (!mlat || !mlon)
37 throw std::runtime_error{"expected valid latitude and longitude for waypoint"};
38 co_await xml::ignore_contents(e);
39 co_return geo::point{*mlon, *mlat};
40 } 36 }
41 37 else if (name == qname("lon"))
42 auto parse_trkseg(xml::executor_ref e, xml::attribute_view) -> xml::parser<track_segment> { 38 {
43 auto s = track_segment{}; 39 mlon = parse_xml_double(value);
44 while (auto mwpt = co_await allow_element(e, qname("trkpt"), xml::hohalo<parse_wpt>()))
45 bgeo::append(s.waypoints, *mwpt);
46 co_return std::move(s);
47 } 40 }
41 }
42 if (!mlat || !mlon)
43 throw std::runtime_error{
44 "expected valid latitude and longitude for waypoint"
45 };
46 co_await xml::ignore_contents(e);
47 co_return geo::point{*mlon, *mlat};
48}
48 49
49 auto parse_trk(xml::executor_ref e, xml::attribute_view) -> xml::parser<track> { 50auto parse_trkseg(xml::executor_ref e, xml::attribute_view)
50 auto t = track{}; 51 -> xml::parser<track_segment>
51 t.name = co_await allow_element(e, qname("name"), xml::hohalo<xml::read_string_contents>()); 52{
52 co_await allow_element(e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>()); 53 auto s = track_segment{};
53 t.desc = co_await allow_element(e, qname("desc"), xml::hohalo<xml::read_string_contents>()); 54 while (auto mwpt = co_await allow_element(
54 co_await xml::ignore_contents(e, /* until */ qname("trkseg")); 55 e, qname("trkpt"), xml::hohalo<parse_wpt>()))
55 while (auto mseg = co_await allow_element(e, qname("trkseg"), xml::hohalo<parse_trkseg>())) 56 bgeo::append(s.waypoints, *mwpt);
56 t.segments.push_back(std::move(*mseg)); 57 co_return std::move(s);
57 co_return std::move(t); 58}
58 }
59 59
60 auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) -> xml::parser<file> { 60auto parse_trk(xml::executor_ref e, xml::attribute_view) -> xml::parser<track>
61 auto f = file{}; 61{
62 if (attrs.lookup(qname("version")) != "1.0"sv) 62 auto t = track{};
63 throw std::runtime_error{"expected GPX version to be 1.0"}; 63 t.name = co_await allow_element(
64 if (auto mcreator = attrs.lookup(qname("creator"))) 64 e, qname("name"), xml::hohalo<xml::read_string_contents>());
65 f.creator = *mcreator; 65 co_await allow_element(
66 else 66 e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>());
67 throw std::runtime_error{"expected GPX file to have creator"}; 67 t.desc = co_await allow_element(
68 e, qname("desc"), xml::hohalo<xml::read_string_contents>());
69 co_await xml::ignore_contents(e, /* until */ qname("trkseg"));
70 while (auto mseg = co_await allow_element(
71 e, qname("trkseg"), xml::hohalo<parse_trkseg>()))
72 t.segments.push_back(std::move(*mseg));
73 co_return std::move(t);
74}
68 75
69 f.meta.name = co_await allow_element(e, qname("name"), xml::hohalo<xml::read_string_contents>()); 76auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs)
70 f.meta.desc = co_await allow_element(e, qname("desc"), xml::hohalo<xml::read_string_contents>()); 77 -> xml::parser<file>
78{
79 auto f = file{};
80 if (attrs.lookup(qname("version")) != "1.0"sv)
81 throw std::runtime_error{"expected GPX version to be 1.0"};
82 if (auto mcreator = attrs.lookup(qname("creator")))
83 f.creator = *mcreator;
84 else
85 throw std::runtime_error{"expected GPX file to have creator"};
71 86
72 co_await xml::ignore_contents(e, /* until */ qname("trk")); 87 f.meta.name = co_await allow_element(
73 while (auto mtrk = co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>())) 88 e, qname("name"), xml::hohalo<xml::read_string_contents>());
74 f.tracks.push_back(std::move(*mtrk)); 89 f.meta.desc = co_await allow_element(
75 co_await xml::ignore_contents(e); 90 e, qname("desc"), xml::hohalo<xml::read_string_contents>());
76 91
77 co_return std::move(f); 92 co_await xml::ignore_contents(e, /* until */ qname("trk"));
78 } 93 while (auto mtrk =
94 co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>()))
95 f.tracks.push_back(std::move(*mtrk));
96 co_await xml::ignore_contents(e);
79 97
80 } // namespace v10 98 co_return std::move(f);
99}
81 100
82 namespace v11 { 101} // namespace v10
83 102
84 constexpr auto xmlns = "http://www.topografix.com/GPX/1/1"sv; 103namespace v11 {
85 auto qname(std::string_view local) -> xml::qname_view {
86 return {.ns_uri = xmlns, .local = local};
87 }
88 104
89 auto parse_metadata(xml::executor_ref e, xml::attribute_view) -> xml::parser<metadata> { 105constexpr auto xmlns = "http://www.topografix.com/GPX/1/1"sv;
90 auto meta = metadata{}; 106auto qname(std::string_view local) -> xml::qname_view
91 meta.name = co_await allow_element(e, qname("name"), xml::hohalo<xml::read_string_contents>()); 107{
92 meta.desc = co_await allow_element(e, qname("desc"), xml::hohalo<xml::read_string_contents>()); 108 return {.ns_uri = xmlns, .local = local};
93 co_await xml::ignore_contents(e); 109}
94 co_return meta;
95 }
96 110
97 auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) -> xml::parser<geo::point> { 111auto parse_metadata(xml::executor_ref e, xml::attribute_view)
98 auto parse_xml_double = [](std::string_view sv) -> std::optional<double> { 112 -> xml::parser<metadata>
99 return util::parse_double(sv, std::chars_format::fixed); 113{
100 }; 114 auto meta = metadata{};
101 auto mlat = std::optional<double>{}; 115 meta.name = co_await allow_element(
102 auto mlon = std::optional<double>{}; 116 e, qname("name"), xml::hohalo<xml::read_string_contents>());
103 for (auto const& [name, value] : attrs) { 117 meta.desc = co_await allow_element(
104 if (name == qname("lat")) { 118 e, qname("desc"), xml::hohalo<xml::read_string_contents>());
105 mlat = parse_xml_double(value); 119 co_await xml::ignore_contents(e);
106 } else if (name == qname("lon")) { 120 co_return meta;
107 mlon = parse_xml_double(value); 121}
108 }
109 }
110 if (!mlat || !mlon)
111 throw std::runtime_error{"expected valid latitude and longitude for waypoint"};
112 co_await xml::ignore_contents(e);
113 co_return geo::point{*mlon, *mlat};
114 }
115 122
116 auto parse_trkseg(xml::executor_ref e, xml::attribute_view) -> xml::parser<track_segment> { 123auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs)
117 auto s = track_segment{}; 124 -> xml::parser<geo::point>
118 while (auto mwpt = co_await allow_element(e, qname("trkpt"), xml::hohalo<parse_wpt>())) 125{
119 bgeo::append(s.waypoints, *mwpt); 126 auto parse_xml_double = [](std::string_view sv) -> std::optional<double>
120 co_await allow_element(e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>()); 127 { return util::parse_double(sv, std::chars_format::fixed); };
121 co_return std::move(s); 128 auto mlat = std::optional<double>{};
129 auto mlon = std::optional<double>{};
130 for (auto const& [name, value] : attrs)
131 {
132 if (name == qname("lat"))
133 {
134 mlat = parse_xml_double(value);
122 } 135 }
123 136 else if (name == qname("lon"))
124 auto parse_trk(xml::executor_ref e, xml::attribute_view) -> xml::parser<track> { 137 {
125 auto t = track{}; 138 mlon = parse_xml_double(value);
126 t.name = co_await allow_element(e, qname("name"), xml::hohalo<xml::read_string_contents>());
127 co_await allow_element(e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>());
128 t.desc = co_await allow_element(e, qname("desc"), xml::hohalo<xml::read_string_contents>());
129 co_await xml::ignore_contents(e, /* until */ qname("trkseg"));
130 while (auto mseg = co_await allow_element(e, qname("trkseg"), xml::hohalo<parse_trkseg>()))
131 t.segments.push_back(std::move(*mseg));
132 co_return std::move(t);
133 } 139 }
140 }
141 if (!mlat || !mlon)
142 throw std::runtime_error{
143 "expected valid latitude and longitude for waypoint"
144 };
145 co_await xml::ignore_contents(e);
146 co_return geo::point{*mlon, *mlat};
147}
134 148
135 auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) -> xml::parser<file> { 149auto parse_trkseg(xml::executor_ref e, xml::attribute_view)
136 auto f = file{}; 150 -> xml::parser<track_segment>
137 if (attrs.lookup(qname("version")) != "1.1"sv) 151{
138 throw std::runtime_error{"expected GPX version to be 1.1"}; 152 auto s = track_segment{};
139 if (auto mcreator = attrs.lookup(qname("creator"))) 153 while (auto mwpt = co_await allow_element(
140 f.creator = *mcreator; 154 e, qname("trkpt"), xml::hohalo<parse_wpt>()))
141 else 155 bgeo::append(s.waypoints, *mwpt);
142 throw std::runtime_error{"expected GPX file to have creator"}; 156 co_await allow_element(
157 e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>());
158 co_return std::move(s);
159}
143 160
144 if (auto mmeta = co_await allow_element(e, qname("metadata"), xml::hohalo<parse_metadata>())) 161auto parse_trk(xml::executor_ref e, xml::attribute_view) -> xml::parser<track>
145 f.meta = *mmeta; 162{
146 co_await xml::ignore_contents(e, /* until */ qname("trk")); 163 auto t = track{};
147 while (auto mtrk = co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>())) 164 t.name = co_await allow_element(
148 f.tracks.push_back(std::move(*mtrk)); 165 e, qname("name"), xml::hohalo<xml::read_string_contents>());
149 co_await allow_element(e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>()); 166 co_await allow_element(
167 e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>());
168 t.desc = co_await allow_element(
169 e, qname("desc"), xml::hohalo<xml::read_string_contents>());
170 co_await xml::ignore_contents(e, /* until */ qname("trkseg"));
171 while (auto mseg = co_await allow_element(
172 e, qname("trkseg"), xml::hohalo<parse_trkseg>()))
173 t.segments.push_back(std::move(*mseg));
174 co_return std::move(t);
175}
150 176
151 co_return std::move(f); 177auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs)
152 } 178 -> xml::parser<file>
179{
180 auto f = file{};
181 if (attrs.lookup(qname("version")) != "1.1"sv)
182 throw std::runtime_error{"expected GPX version to be 1.1"};
183 if (auto mcreator = attrs.lookup(qname("creator")))
184 f.creator = *mcreator;
185 else
186 throw std::runtime_error{"expected GPX file to have creator"};
153 187
154 } // namespace v11 188 if (auto mmeta = co_await allow_element(
189 e, qname("metadata"), xml::hohalo<parse_metadata>()))
190 f.meta = *mmeta;
191 co_await xml::ignore_contents(e, /* until */ qname("trk"));
192 while (auto mtrk =
193 co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>()))
194 f.tracks.push_back(std::move(*mtrk));
195 co_await allow_element(
196 e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>());
155 197
156 auto parse_file(xml::executor_ref e) -> xml::parser<file> { 198 co_return std::move(f);
157 auto decl = co_await expect_event<xml::xml_decl_event>(e); 199}
158 if (decl.version != "1.0"sv)
159 throw std::runtime_error{std::format("unsupported XML version, got {}", std::string_view{decl.version})};
160 if (decl.encoding != "UTF-8"sv)
161 throw std::runtime_error{"unsupported encoding"};
162 if (auto mf = co_await allow_element(e, v10::qname("gpx"), xml::hohalo<v10::parse_gpx>()))
163 co_return std::move(*mf);
164 if (auto mf = co_await allow_element(e, v11::qname("gpx"), xml::hohalo<v11::parse_gpx>()))
165 co_return std::move(*mf);
166 throw std::runtime_error{"no supported GPX document found"};
167 }
168 200
169 reader::reader() 201} // namespace v11
170 : p_{parse_file(util::not_null{&e_})}
171 { e_.set_continuation(p_.promise().base_handle()); }
172 202
173 auto reader::init() -> void { 203auto parse_file(xml::executor_ref e) -> xml::parser<file>
174 e_.start(); 204{
175 } 205 auto decl = co_await expect_event<xml::xml_decl_event>(e);
206 if (decl.version != "1.0"sv)
207 throw std::runtime_error{std::format(
208 "unsupported XML version, got {}", std::string_view{decl.version})};
209 if (decl.encoding != "UTF-8"sv)
210 throw std::runtime_error{"unsupported encoding"};
211 if (auto mf = co_await allow_element(
212 e, v10::qname("gpx"), xml::hohalo<v10::parse_gpx>()))
213 co_return std::move(*mf);
214 if (auto mf = co_await allow_element(
215 e, v11::qname("gpx"), xml::hohalo<v11::parse_gpx>()))
216 co_return std::move(*mf);
217 throw std::runtime_error{"no supported GPX document found"};
218}
176 219
177 auto reader::put(std::string_view buf) -> void { 220reader::reader() : p_{parse_file(util::not_null{&e_})}
178 e_.read(buf, false); 221{
179 } 222 e_.set_continuation(p_.promise().base_handle());
223}
180 224
181 auto reader::finish() -> gpx::file { 225auto reader::init() -> void { e_.start(); }
182 e_.read(std::string_view{}, true); 226
183 e_.end(); 227auto reader::put(std::string_view buf) -> void { e_.read(buf, false); }
184 // Promise is still alive since the last coroutine performs a 228
185 // symmetric transfer to std::noop_coroutine() in final_suspend(). 229auto reader::finish() -> gpx::file
186 return std::move(p_.promise().returned_value()); 230{
187 } 231 e_.read(std::string_view{}, true);
232 e_.end();
233 // Promise is still alive since the last coroutine performs a
234 // symmetric transfer to std::noop_coroutine() in final_suspend().
235 return std::move(p_.promise().returned_value());
236}
188 237
189} // namespace routemon::gpx 238} // namespace routemon::gpx