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

debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 4975bf8 was 47b2b81, checked in by Olly Betts <olly@…>, 22 months ago

Fix warnings from clang 18

  • Property mode set to 100644
File size: 5.5 KB
RevLine 
[4734106]1/* gpx.cc
2 * Export from Aven as GPX.
3 */
4/* Copyright (C) 2012 Olaf Kähler
[d417499]5 * Copyright (C) 2012,2013,2014,2015,2016 Olly Betts
[4734106]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
[fdea415]28#include "export.h" // For LABELS, etc
29
[4734106]30#include <stdio.h>
31#include <string>
[f10cf8f]32#include <time.h>
[4734106]33#include <math.h>
34
35#include "useful.h"
[c2ea0c5]36#include <proj.h>
[4734106]37
38#include "aven.h"
39#include "message.h"
40
41using namespace std;
42
[c2ea0c5]43#define WGS84_DATUM_STRING "EPSG:4326"
[4734106]44
[fdea415]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
[c2ea0c5]66static void discarding_proj_logger(void *, int, const char *) { }
67
[9b5a5fd]68GPX::GPX(const char * input_datum)
[4734106]69{
[c2ea0c5]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
[ae8194f]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    }
[c2ea0c5]85
86    if (!pj) {
[4734106]87        wxString m = wmsg(/*Failed to initialise input coordinate system “%s”*/287);
88        m = wxString::Format(m.c_str(), input_datum);
[6d3938b]89        throw m;
[4734106]90    }
91}
92
93GPX::~GPX()
94{
[c2ea0c5]95    if (pj)
96        proj_destroy(pj);
[ddb5153]97    free((void*)trk_name);
[4734106]98}
99
[fdea415]100const int *
101GPX::passes() const
102{
[a6dddd1]103    static const int default_passes[] = { LABELS|ENTS|FIXES|EXPORTS, LEGS|SURF, 0 };
[fdea415]104    return default_passes;
105}
106
[4734106]107/* Initialise GPX routines. */
[55a861a]108void GPX::header(const char * title, const char *, time_t datestamp_numeric,
109                 double, double, double, double, double, double)
[4734106]110{
[79137712]111    fputs(
[4734106]112"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
[d417499]113"<gpx version=\"1.0\" creator=\"" PACKAGE_STRING " (aven) - https://survex.com/\""
[4734106]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"
[79137712]117" http://www.topografix.com/GPX/1/0/gpx.xsd\">\n", fh);
[6a1c384]118    if (title) {
119        fputs("<name>", fh);
120        html_escape(fh, title);
121        fputs("</name>\n", fh);
[ddb5153]122        trk_name = strdup(title);
[6a1c384]123    }
[f10cf8f]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    }
[fdea415]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
[110f59c]138    // to WGS84 lat+long...
[4734106]139}
140
141void
[a2c29c1]142GPX::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
[4734106]143{
[a6dddd1]144    if (fPendingMove) {
145        if (in_trkseg) {
146            fputs("</trkseg><trkseg>\n", fh);
147        } else {
[ddb5153]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);
[a6dddd1]155            in_trkseg = true;
156        }
[47b2b81]157        PJ_COORD coord{{p1->x, p1->y, p1->z, HUGE_VAL}};
[c2ea0c5]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        }
[a6dddd1]164        // %.8f is at worst just over 1mm.
[c2ea0c5]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
[47b2b81]171    PJ_COORD coord{{p->x, p->y, p->z, HUGE_VAL}};
[c2ea0c5]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
[a6dddd1]177    }
178    // %.8f is at worst just over 1mm.
[c2ea0c5]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);
[4734106]183}
184
185void
[fdea415]186GPX::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
[4734106]187{
[47b2b81]188    PJ_COORD coord{{p->x, p->y, p->z, HUGE_VAL}};
[c2ea0c5]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    }
[4734106]195    // %.8f is at worst just over 1mm.
[c2ea0c5]196    fprintf(fh, "<wpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele><name>",
197            coord.xyzt.x,
198            coord.xyzt.y,
199            coord.xyzt.z);
[fdea415]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;
[4734106]213    }
[fdea415]214    fputs("</wpt>\n", fh);
[4734106]215}
216
217void
218GPX::footer()
219{
[a6dddd1]220    if (in_trkseg)
221        fputs("</trkseg></trk>\n", fh);
222    fputs("</gpx>\n", fh);
[4734106]223}
Note: See TracBrowser for help on using the repository browser.