1 /*
2 	Support for Google Earth & Keyhole "kml" format.
3 
4 	Copyright (C) 2005-2013 Robert Lipe, robertlipe+source@gpsbabel.org
5 	Updates by Andrew Kirmse, akirmse at google.com
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 2 of the License, or
10 	(at your option) any later version.
11 
12 	This program 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
15 	GNU General Public License for more details.
16 
17 	You should have received a copy of the GNU General Public License
18 	along with this program; if not, write to the Free Software
19 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 
21  */
22 #ifndef KML_H_INCLUDED_
23 #define KML_H_INCLUDED_
24 
25 #include <tuple>                        // for tuple, make_tuple, tie
26 
27 #include <QtCore/QList>                 // for QList
28 #include <QtCore/QString>               // for QString, QStringLiteral, operator+, operator!=
29 #include <QtCore/QVector>               // for QVector
30 #include <QtCore/QXmlStreamAttributes>  // for QXmlStreamAttributes
31 
32 #include "defs.h"
33 #include "format.h"
34 #include "src/core/datetime.h"          // for DateTime
35 #include "src/core/file.h"              // for File
36 #include "src/core/xmlstreamwriter.h"   // for XmlStreamWriter
37 #include "xmlgeneric.h"                 // for cb_cdata, cb_end, cb_start, xg_callback, xg_string, xg_cb_type, xml_deinit, xml_ignore_tags, xml_init, xml_read, xg_tag_mapping
38 
39 
40 class KmlFormat : public Format
41 {
42 public:
get_args()43   QVector<arglist_t>* get_args() override
44   {
45     return &kml_args;
46   }
47 
get_type()48   ff_type get_type() const override
49   {
50     return ff_type_file;
51   }
52 
get_cap()53   QVector<ff_cap> get_cap() const override
54   {
55     return FF_CAP_RW_ALL;
56   }
57 
get_encode()58   QString get_encode() const override
59   {
60     return CET_CHARSET_UTF8;
61   }
62 
get_fixed_encode()63   int get_fixed_encode() const override
64   {
65     return 1;
66   }
67 
68   void rd_init(const QString& fname) override;
69   void read() override;
70   void rd_deinit() override;
71   void wr_init(const QString& fname) override;
72   void write() override;
73   void wr_deinit() override;
74   void wr_position_init(const QString& fname) override;
75   void wr_position(Waypoint* wpt) override;
76   void wr_position_deinit() override;
77 
78 private:
79   /* Types */
80 
81   enum kml_point_type {
82     kmlpt_unknown,
83     kmlpt_waypoint,
84     kmlpt_track,
85     kmlpt_route,
86     kmlpt_multitrack,
87     kmlpt_other
88   };
89 
90 // Helper to write gx:SimpleList, iterating over a route queue and writing out.
91 
92   enum wp_field {
93     fld_cadence,
94     fld_depth,
95     fld_heartrate,
96     fld_temperature,
97     fld_power
98   };
99 
100   /* Constants */
101   static constexpr const char* default_precision = "6";
102   static constexpr int kml_color_limit = 204;	/* allowed range [0,255] */
103 
104   // Multitrack ids to correlate Schema to SchemaData
105   static constexpr const char* kmt_heartrate = "heartrate";
106   static constexpr const char* kmt_cadence = "cadence";
107   static constexpr const char* kmt_temperature = "temperature";
108   static constexpr const char* kmt_depth = "depth";
109   static constexpr const char* kmt_power = "power";
110 
111   /* Member Functions */
112 
113   void kml_init_color_sequencer(unsigned int steps_per_rev);
114   void kml_step_color();
115   void wpt_s(const QString& args, const QXmlStreamAttributes* attrs);
116   void wpt_e(const QString& args, const QXmlStreamAttributes* attrs);
117   void wpt_name(const QString& args, const QXmlStreamAttributes* attrs);
118   void wpt_desc(const QString& args, const QXmlStreamAttributes* attrs);
119   void wpt_coord(const QString& args, const QXmlStreamAttributes* attrs);
120   void wpt_icon(const QString& args, const QXmlStreamAttributes* attrs);
121   void trk_coord(const QString& args, const QXmlStreamAttributes* attrs);
122   void wpt_time(const QString& args, const QXmlStreamAttributes* attrs);
123   void wpt_ts_begin(const QString& args, const QXmlStreamAttributes* attrs);
124   void wpt_ts_end(const QString& args, const QXmlStreamAttributes* attrs);
125   void gx_trk_s(const QString& args, const QXmlStreamAttributes* attrs);
126   void gx_trk_e(const QString& args, const QXmlStreamAttributes* attrs);
127   void gx_trk_when(const QString& args, const QXmlStreamAttributes* attrs);
128   void gx_trk_coord(const QString& args, const QXmlStreamAttributes* attrs);
129   void kml_output_linestyle(char* color, int width) const;
130   void kml_write_bitmap_style_(const QString& style, const QString& bitmap, int highlighted, int force_heading) const;
131   void kml_write_bitmap_style(kml_point_type pt_type, const QString& bitmap, const QString& customstyle) const;
132   void kml_output_timestamp(const Waypoint* waypointp) const;
133   static void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& boldData, const QString& data);
134   static void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& data);
135   void kml_output_trkdescription(const route_head* header, const computed_trkdata* td) const;
136   void kml_output_header(const route_head* header, const computed_trkdata* td) const;
137   int kml_altitude_known(const Waypoint* waypoint) const;
138   void kml_write_coordinates(const Waypoint* waypointp) const;
139   void kml_output_lookat(const Waypoint* waypointp) const;
140   void kml_output_positioning(bool tessellate) const;
141   void kml_output_description(const Waypoint* pt) const;
142   void kml_recompute_time_bounds(const Waypoint* waypointp);
143   void kml_add_to_bounds(const Waypoint* waypointp);
144   void kml_output_point(const Waypoint* waypointp, kml_point_type pt_type) const;
145   void kml_output_tailer(const route_head* header);
146   static void kml_gc_all_tabs_text(QString& cdataStr);
147   void kml_gc_make_balloonstyletext() const;
148   void kml_gc_make_balloonstyle() const;
149   static QString kml_lookup_gc_icon(const Waypoint* waypointp);
150   static const char* kml_lookup_gc_container(const Waypoint* waypointp);
151   static QString kml_gc_mkstar(int rating);
152   QString kml_geocache_get_logs(const Waypoint* wpt) const;
153   void kml_write_data_element(const QString& name, const QString& value) const;
154   void kml_write_data_element(const QString& name, int value) const;
155   void kml_write_data_element(const QString& name, double value) const;
156   void kml_write_cdata_element(const QString& name, const QString& value) const;
157   void kml_geocache_pr(const Waypoint* waypointp) const;
158   void kml_waypt_pr(const Waypoint* waypointp) const;
159   void kml_track_hdr(const route_head* header) const;
160   void kml_track_disp(const Waypoint* waypointp) const;
161   void kml_track_tlr(const route_head* header);
162   void kml_mt_simple_array(const route_head* header, const char* name, wp_field member) const;
163   static int track_has_time(const route_head* header);
164   void write_as_linestring(const route_head* header);
165   void kml_mt_hdr(const route_head* header);
166   void kml_mt_tlr(const route_head* header) const;
167   void kml_route_hdr(const route_head* header) const;
168   void kml_route_disp(const Waypoint* waypointp) const;
169   void kml_route_tlr(const route_head* header);
170   void kml_write_AbstractView();
171   void kml_mt_array_schema(const char* field_name, const char* display_name, const char* type) const;
172   static QString kml_get_posn_icon(int freshness);
173 
174   /* Data Members */
175 
176   // options
177   char* opt_deficon{nullptr};
178   char* opt_export_lines{nullptr};
179   char* opt_export_points{nullptr};
180   char* opt_export_track{nullptr};
181   char* opt_line_width{nullptr};
182   char* opt_line_color{nullptr};
183   char* opt_floating{nullptr};
184   char* opt_extrude{nullptr};
185   char* opt_trackdata{nullptr};
186   char* opt_trackdirection{nullptr};
187   char* opt_units{nullptr};
188   char* opt_labels{nullptr};
189   char* opt_max_position_points{nullptr};
190   char* opt_rotate_colors{nullptr};
191   char* opt_precision{nullptr};
192 
193   int export_lines{};
194   int export_points{};
195   int export_track{};
196   int floating{};
197   int extrude{};
198   int trackdata{};
199   int trackdirection{};
200   int max_position_points{};
201   int rotate_colors{};
202   int line_width{};
203   int html_encrypt{};
204   int precision{};
205 
206   Waypoint* wpt_tmp{nullptr};
207   bool wpt_tmp_queued{false};
208   QString posnfilename;
209   QString posnfilenametmp;
210 
211   route_head* gx_trk_head{nullptr};
212   QList<gpsbabel::DateTime>* gx_trk_times{nullptr};
213   QList<std::tuple<int, double, double, double>>* gx_trk_coords{nullptr};
214 
215   gpsbabel::File* oqfile{nullptr};
216   gpsbabel::XmlStreamWriter* writer{nullptr};
217 
218   int realtime_positioning{};
219   bounds kml_bounds{};
220   gpsbabel::DateTime kml_time_max;
221   gpsbabel::DateTime kml_time_min;
222 
223   QVector<arglist_t> kml_args = {
224     {"deficon", &opt_deficon, "Default icon name", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr },
225     {
226       "lines", &opt_export_lines,
227       "Export linestrings for tracks and routes",
228       "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr,
229     },
230     {
231       "points", &opt_export_points,
232       "Export placemarks for tracks and routes",
233       "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
234     },
235     {
236       "line_width", &opt_line_width,
237       "Width of lines, in pixels",
238       "6", ARGTYPE_INT, ARG_NOMINMAX, nullptr
239     },
240     {
241       "line_color", &opt_line_color,
242       "Line color, specified in hex AABBGGRR",
243       "99ffac59", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
244     },
245     {
246       "floating", &opt_floating,
247       "Altitudes are absolute and not clamped to ground",
248       "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
249     },
250     {
251       "extrude", &opt_extrude,
252       "Draw extrusion line from trackpoint to ground",
253       "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
254     },
255     {
256       "track", &opt_export_track,
257       "Write KML track (default = 0)",
258       "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
259     },
260     {
261       "trackdata", &opt_trackdata,
262       "Include extended data for trackpoints (default = 1)",
263       "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
264     },
265     {
266       "trackdirection", &opt_trackdirection,
267       "Indicate direction of travel in track icons (default = 0)",
268       "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
269     },
270     {
271       "units", &opt_units,
272       "Units used when writing comments ('s'tatute, 'm'etric,' 'n'autical, 'a'viation)",
273       "s", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
274     },
275     {
276       "labels", &opt_labels,
277       "Display labels on track and routepoints  (default = 1)",
278       "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
279     },
280     {
281       "max_position_points", &opt_max_position_points,
282       "Retain at most this number of position points  (0 = unlimited)",
283       "0", ARGTYPE_INT, ARG_NOMINMAX, nullptr
284     },
285     {
286       "rotate_colors", &opt_rotate_colors,
287       "Rotate colors for tracks and routes (default automatic)",
288       nullptr, ARGTYPE_FLOAT, "0", "360", nullptr
289     },
290     {
291       "prec", &opt_precision,
292       "Precision of coordinates, number of decimals",
293       default_precision, ARGTYPE_INT, ARG_NOMINMAX, nullptr
294     },
295   };
296 
297   struct {
298     float seq{0.0f};
299     float step{0.0f};
300     gb_color color;
301   } kml_color_sequencer;
302 
303   QList<xg_functor_map_entry<KmlFormat>> kml_map = {
304     {&KmlFormat::wpt_s, cb_start, "/Placemark"},
305     {&KmlFormat::wpt_e, cb_end, "/Placemark"},
306     {&KmlFormat::wpt_name, cb_cdata, "/Placemark/name"},
307     {&KmlFormat::wpt_desc, cb_cdata, "/Placemark/description"},
308     {&KmlFormat::wpt_ts_begin, cb_cdata,"/Placemark/TimeSpan/begin"},
309     {&KmlFormat::wpt_ts_end, cb_cdata, "/Placemark/TimeSpan/end"},
310     {&KmlFormat::wpt_time, cb_cdata, "/Placemark/TimeStamp/when"},
311     // Alias for above used in KML 2.0
312     {&KmlFormat::wpt_time, cb_cdata, "/Placemark/TimeInstant/timePosition"},
313     {&KmlFormat::wpt_coord, cb_cdata, "/Placemark/Point/coordinates"},
314     {&KmlFormat::wpt_icon, cb_cdata, "/Placemark/Style/Icon/href"},
315     {&KmlFormat::trk_coord, cb_cdata, "/Placemark/MultiGeometry/LineString/coordinates"},
316     {&KmlFormat::trk_coord, cb_cdata, "/Placemark/GeometryCollection/LineString/coordinates"},
317     {&KmlFormat::trk_coord, cb_cdata, "/Placemark/Polygon/outerBoundaryIs/LinearRing/coordinates"},
318     {&KmlFormat::trk_coord, cb_cdata, "/Placemark/LineString/coordinates"},
319     {&KmlFormat::gx_trk_s,  cb_start, "/Placemark/*gx:Track"},
320     {&KmlFormat::gx_trk_e,  cb_end, "/Placemark/*gx:Track"},
321     {&KmlFormat::gx_trk_when,  cb_cdata, "/Placemark/*gx:Track/when"},
322     {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/*gx:Track/gx:coord"},
323     {&KmlFormat::gx_trk_s,  cb_start, "/Placemark/Track"}, // KML 2.3
324     {&KmlFormat::gx_trk_e,  cb_end, "/Placemark/Track"}, // KML 2.3
325     {&KmlFormat::gx_trk_when,  cb_cdata, "/Placemark/Track/when"}, // KML 2.3
326     {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/Track/coord"}, // KML 2.3
327     {&KmlFormat::gx_trk_s,  cb_start, "/Placemark/MultiTrack/Track"}, // KML 2.3
328     {&KmlFormat::gx_trk_e,  cb_end, "/Placemark/MultiTrack/Track"}, // KML 2.3
329     {&KmlFormat::gx_trk_when,  cb_cdata, "/Placemark/MultiTrack/Track/when"}, // KML 2.3
330     {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/MultiTrack/Track/coord"} // KML 2.3
331   };
332 
333   static const char* kml_tags_to_ignore[];
334   static const char* kml_tags_to_skip[];
335 
336   // The TimeSpan/begin and TimeSpan/end DateTimes:
337   gpsbabel::DateTime wpt_timespan_begin, wpt_timespan_end;
338 
339   static const QString map_templates[];
340 
341   route_head* posn_trk_head{nullptr};
342 };
343 
344 #endif // KML_H_INCLUDED_
345