source: git/src/gpx.cc @ 9fcc81a

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-data
Last change on this file since 9fcc81a was 8719711, checked in by Olly Betts <olly@…>, 5 years ago

Support PROJ 5.x

PROJ 6.x won't work yet. See https://trac.survex.com/ticket/102 for
details.

  • Property mode set to 100644
File size: 5.0 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#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1
37#include <proj_api.h>
38
39#include "aven.h"
40#include "message.h"
41
42using namespace std;
43
44#define WGS84_DATUM_STRING "+proj=longlat +ellps=WGS84 +datum=WGS84"
45
46static void
47html_escape(FILE *fh, const char *s)
48{
49    while (*s) {
50        switch (*s) {
51            case '<':
52                fputs("&lt;", fh);
53                break;
54            case '>':
55                fputs("&gt;", fh);
56                break;
57            case '&':
58                fputs("&amp;", fh);
59                break;
60            default:
61                PUTC(*s, fh);
62        }
63        ++s;
64    }
65}
66
67GPX::GPX(const char * input_datum)
68    : pj_input(NULL), pj_output(NULL), in_trkseg(false), trk_name(NULL)
69{
70    if (!(pj_input = pj_init_plus(input_datum))) {
71        wxString m = wmsg(/*Failed to initialise input coordinate system “%s”*/287);
72        m = wxString::Format(m.c_str(), input_datum);
73        throw m;
74    }
75    if (!(pj_output = pj_init_plus(WGS84_DATUM_STRING))) {
76        wxString m = wmsg(/*Failed to initialise output coordinate system “%s”*/288);
77        m = wxString::Format(m.c_str(), WGS84_DATUM_STRING);
78        throw m;
79    }
80}
81
82GPX::~GPX()
83{
84    if (pj_input)
85        pj_free(pj_input);
86    if (pj_output)
87        pj_free(pj_output);
88    free((void*)trk_name);
89}
90
91const int *
92GPX::passes() const
93{
94    static const int default_passes[] = { LABELS|ENTS|FIXES|EXPORTS, LEGS|SURF, 0 };
95    return default_passes;
96}
97
98/* Initialise GPX routines. */
99void GPX::header(const char * title, const char *, time_t datestamp_numeric,
100                 double, double, double, double, double, double)
101{
102    fputs(
103"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
104"<gpx version=\"1.0\" creator=\"" PACKAGE_STRING " (aven) - https://survex.com/\""
105" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
106" xmlns=\"http://www.topografix.com/GPX/1/0\""
107" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0"
108" http://www.topografix.com/GPX/1/0/gpx.xsd\">\n", fh);
109    if (title) {
110        fputs("<name>", fh);
111        html_escape(fh, title);
112        fputs("</name>\n", fh);
113        trk_name = strdup(title);
114    }
115    if (datestamp_numeric != time_t(-1)) {
116        struct tm * tm = gmtime(&datestamp_numeric);
117        if (tm) {
118            char buf[32];
119            if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm)) {
120                fputs("<time>", fh);
121                fputs(buf, fh);
122                fputs("</time>\n", fh);
123            }
124        }
125    }
126    // FIXME: optional in GPX, but perhaps useful:
127    // <bounds minlat="..." minlon="..." maxlat="..." maxlon="..." />
128    // NB Not necessarily the same as the bounds in survex coords translated
129    // to WGS84 lat+long...
130}
131
132void
133GPX::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
134{
135    if (fPendingMove) {
136        if (in_trkseg) {
137            fputs("</trkseg><trkseg>\n", fh);
138        } else {
139            fputs("<trk>", fh);
140            if (trk_name) {
141                fputs("<name>", fh);
142                html_escape(fh, trk_name);
143                fputs("</name>", fh);
144            }
145            fputs("<trkseg>\n", fh);
146            in_trkseg = true;
147        }
148        double X = p1->x, Y = p1->y, Z = p1->z;
149        pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
150        X = deg(X);
151        Y = deg(Y);
152        // %.8f is at worst just over 1mm.
153        fprintf(fh, "<trkpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele></trkpt>\n", X, Y, Z);
154    }
155    double X = p->x, Y = p->y, Z = p->z;
156    pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
157    X = deg(X);
158    Y = deg(Y);
159    // %.8f is at worst just over 1mm.
160    fprintf(fh, "<trkpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele></trkpt>\n", X, Y, Z);
161}
162
163void
164GPX::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
165{
166    double X = p->x, Y = p->y, Z = p->z;
167    pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
168    X = deg(X);
169    Y = deg(Y);
170    // %.8f is at worst just over 1mm.
171    fprintf(fh, "<wpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele><name>", X, Y, Z);
172    html_escape(fh, s);
173    fputs("</name>", fh);
174    // Add a "pin" symbol with colour matching what aven shows.
175    switch (type) {
176        case FIXES:
177            fputs("<sym>Pin, Red</sym>", fh);
178            break;
179        case EXPORTS:
180            fputs("<sym>Pin, Blue</sym>", fh);
181            break;
182        case ENTS:
183            fputs("<sym>Pin, Green</sym>", fh);
184            break;
185    }
186    fputs("</wpt>\n", fh);
187}
188
189void
190GPX::footer()
191{
192    if (in_trkseg)
193        fputs("</trkseg></trk>\n", fh);
194    fputs("</gpx>\n", fh);
195}
Note: See TracBrowser for help on using the repository browser.