source: git/src/gpx.cc

walls-data
Last change on this file was 4c83f84, checked in by Olly Betts <olly@…>, 6 days ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

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