source: git/src/gpx.cc @ 4975bf8

debug-cidebug-ci-sanitisersfaster-cavernloglog-selectwalls-datawalls-data-hanging-as-warning
Last change on this file since 4975bf8 was 47b2b81, checked in by Olly Betts <olly@…>, 9 months ago

Fix warnings from clang 18

  • Property mode set to 100644
File size: 5.5 KB
Line 
1/* gpx.cc
2 * Export from Aven as GPX.
3 */
4/* Copyright (C) 2012 Olaf Kähler
5 * Copyright (C) 2012,2013,2014,2015,2016 Olly Betts
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 St, Fifth Floor, Boston, MA  02110-1301 USA
20 */
21
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26#include "gpx.h"
27
28#include "export.h" // For LABELS, etc
29
30#include <stdio.h>
31#include <string>
32#include <time.h>
33#include <math.h>
34
35#include "useful.h"
36#include <proj.h>
37
38#include "aven.h"
39#include "message.h"
40
41using namespace std;
42
43#define WGS84_DATUM_STRING "EPSG:4326"
44
45static void
46html_escape(FILE *fh, const char *s)
47{
48    while (*s) {
49        switch (*s) {
50            case '<':
51                fputs("&lt;", fh);
52                break;
53            case '>':
54                fputs("&gt;", fh);
55                break;
56            case '&':
57                fputs("&amp;", fh);
58                break;
59            default:
60                PUTC(*s, fh);
61        }
62        ++s;
63    }
64}
65
66static void discarding_proj_logger(void *, int, const char *) { }
67
68GPX::GPX(const char * input_datum)
69{
70    /* Prevent stderr spew from PROJ. */
71    proj_log_func(PJ_DEFAULT_CTX, nullptr, discarding_proj_logger);
72
73    pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
74                                input_datum, WGS84_DATUM_STRING,
75                                NULL);
76
77    if (pj) {
78        // Normalise the output order so x is longitude and y latitude - by
79        // default new PROJ has them switched for EPSG:4326 which just seems
80        // confusing.
81        PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, pj);
82        proj_destroy(pj);
83        pj = pj_norm;
84    }
85
86    if (!pj) {
87        wxString m = wmsg(/*Failed to initialise input coordinate system “%s”*/287);
88        m = wxString::Format(m.c_str(), input_datum);
89        throw m;
90    }
91}
92
93GPX::~GPX()
94{
95    if (pj)
96        proj_destroy(pj);
97    free((void*)trk_name);
98}
99
100const int *
101GPX::passes() const
102{
103    static const int default_passes[] = { LABELS|ENTS|FIXES|EXPORTS, LEGS|SURF, 0 };
104    return default_passes;
105}
106
107/* Initialise GPX routines. */
108void GPX::header(const char * title, const char *, time_t datestamp_numeric,
109                 double, double, double, double, double, double)
110{
111    fputs(
112"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
113"<gpx version=\"1.0\" creator=\"" PACKAGE_STRING " (aven) - https://survex.com/\""
114" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
115" xmlns=\"http://www.topografix.com/GPX/1/0\""
116" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0"
117" http://www.topografix.com/GPX/1/0/gpx.xsd\">\n", fh);
118    if (title) {
119        fputs("<name>", fh);
120        html_escape(fh, title);
121        fputs("</name>\n", fh);
122        trk_name = strdup(title);
123    }
124    if (datestamp_numeric != time_t(-1)) {
125        struct tm * tm = gmtime(&datestamp_numeric);
126        if (tm) {
127            char buf[32];
128            if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm)) {
129                fputs("<time>", fh);
130                fputs(buf, fh);
131                fputs("</time>\n", fh);
132            }
133        }
134    }
135    // FIXME: optional in GPX, but perhaps useful:
136    // <bounds minlat="..." minlon="..." maxlat="..." maxlon="..." />
137    // NB Not necessarily the same as the bounds in survex coords translated
138    // to WGS84 lat+long...
139}
140
141void
142GPX::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
143{
144    if (fPendingMove) {
145        if (in_trkseg) {
146            fputs("</trkseg><trkseg>\n", fh);
147        } else {
148            fputs("<trk>", fh);
149            if (trk_name) {
150                fputs("<name>", fh);
151                html_escape(fh, trk_name);
152                fputs("</name>", fh);
153            }
154            fputs("<trkseg>\n", fh);
155            in_trkseg = true;
156        }
157        PJ_COORD coord{{p1->x, p1->y, p1->z, HUGE_VAL}};
158        coord = proj_trans(pj, PJ_FWD, coord);
159        if (coord.xyzt.x == HUGE_VAL ||
160            coord.xyzt.y == HUGE_VAL ||
161            coord.xyzt.z == HUGE_VAL) {
162            // FIXME report errors
163        }
164        // %.8f is at worst just over 1mm.
165        fprintf(fh, "<trkpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele></trkpt>\n",
166                coord.xyzt.x,
167                coord.xyzt.y,
168                coord.xyzt.z);
169    }
170
171    PJ_COORD coord{{p->x, p->y, p->z, HUGE_VAL}};
172    coord = proj_trans(pj, PJ_FWD, coord);
173    if (coord.xyzt.x == HUGE_VAL ||
174        coord.xyzt.y == HUGE_VAL ||
175        coord.xyzt.z == HUGE_VAL) {
176        // FIXME report errors
177    }
178    // %.8f is at worst just over 1mm.
179    fprintf(fh, "<trkpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele></trkpt>\n",
180            coord.xyzt.x,
181            coord.xyzt.y,
182            coord.xyzt.z);
183}
184
185void
186GPX::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
187{
188    PJ_COORD coord{{p->x, p->y, p->z, HUGE_VAL}};
189    coord = proj_trans(pj, PJ_FWD, coord);
190    if (coord.xyzt.x == HUGE_VAL ||
191        coord.xyzt.y == HUGE_VAL ||
192        coord.xyzt.z == HUGE_VAL) {
193        // FIXME report errors
194    }
195    // %.8f is at worst just over 1mm.
196    fprintf(fh, "<wpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele><name>",
197            coord.xyzt.x,
198            coord.xyzt.y,
199            coord.xyzt.z);
200    html_escape(fh, s);
201    fputs("</name>", fh);
202    // Add a "pin" symbol with colour matching what aven shows.
203    switch (type) {
204        case FIXES:
205            fputs("<sym>Pin, Red</sym>", fh);
206            break;
207        case EXPORTS:
208            fputs("<sym>Pin, Blue</sym>", fh);
209            break;
210        case ENTS:
211            fputs("<sym>Pin, Green</sym>", fh);
212            break;
213    }
214    fputs("</wpt>\n", fh);
215}
216
217void
218GPX::footer()
219{
220    if (in_trkseg)
221        fputs("</trkseg></trk>\n", fh);
222    fputs("</gpx>\n", fh);
223}
Note: See TracBrowser for help on using the repository browser.