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

debug-cidebug-ci-sanitisersfaster-cavernlogwalls-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: 9.6 KB
Line 
1/* kml.cc
2 * Export from Aven as KML.
3 */
4/* Copyright (C) 2012 Olaf Kähler
5 * Copyright (C) 2012,2013,2014,2015,2016,2017,2018,2019 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 "kml.h"
27
28#include "export.h" // For LABELS, etc
29
30#include <stdio.h>
31#include <string>
32#include <math.h>
33
34#include "useful.h"
35#include <proj.h>
36
37#include "aven.h"
38#include "message.h"
39
40using namespace std;
41
42#define WGS84_DATUM_STRING "EPSG:4326"
43
44static void
45html_escape(FILE *fh, const char *s)
46{
47    while (*s) {
48        switch (*s) {
49            case '<':
50                fputs("&lt;", fh);
51                break;
52            case '>':
53                fputs("&gt;", fh);
54                break;
55            case '&':
56                fputs("&amp;", fh);
57                break;
58            default:
59                PUTC(*s, fh);
60        }
61        ++s;
62    }
63}
64
65static void discarding_proj_logger(void *, int, const char *) { }
66
67KML::KML(const char * input_datum, bool clamp_to_ground_)
68    : clamp_to_ground(clamp_to_ground_)
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
93KML::~KML()
94{
95    if (pj)
96        proj_destroy(pj);
97}
98
99const int *
100KML::passes() const
101{
102    static const int default_passes[] = {
103        PASG, XSECT, WALL1, WALL2, LEGS|SURF, LABELS|ENTS|FIXES|EXPORTS, 0
104    };
105    return default_passes;
106}
107
108/* Initialise KML routines. */
109void KML::header(const char * title, const char *, time_t,
110                 double, double, double, double, double, double)
111{
112    fputs(
113"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
114"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n", fh);
115    fputs("<Document><name>", fh);
116    html_escape(fh, title);
117    fputs("</name>\n", fh);
118    // Set up styles for the icons to reduce the file size.
119    fputs("<Style id=\"fix\"><IconStyle>"
120          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/red-blank.png</href></Icon>"
121          "</IconStyle></Style>\n", fh);
122    fputs("<Style id=\"exp\"><IconStyle>"
123          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/blu-blank.png</href></Icon>"
124          "</IconStyle></Style>\n", fh);
125    fputs("<Style id=\"ent\"><IconStyle>"
126          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-blank.png</href></Icon>"
127          "</IconStyle></Style>\n", fh);
128    // FIXME: does KML allow bounds?
129    // NB Lat+long bounds are not necessarily the same as the bounds in survex
130    // coords translated to WGS84 lat+long...
131}
132
133void
134KML::start_pass(int)
135{
136    if (in_linestring) {
137        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
138        in_linestring = false;
139    }
140}
141
142void
143KML::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
144{
145    if (fPendingMove) {
146        if (!in_linestring) {
147            in_linestring = true;
148            fputs("<Placemark><MultiGeometry>\n", fh);
149        } else {
150            fputs("</coordinates></LineString>\n", fh);
151        }
152        if (clamp_to_ground) {
153            fputs("<LineString><coordinates>\n", fh);
154        } else {
155            fputs("<LineString><altitudeMode>absolute</altitudeMode><coordinates>\n", fh);
156        }
157
158        PJ_COORD coord{{p1->x, p1->y, p1->z, HUGE_VAL}};
159        coord = proj_trans(pj, PJ_FWD, coord);
160        if (coord.xyzt.x == HUGE_VAL ||
161            coord.xyzt.y == HUGE_VAL ||
162            coord.xyzt.z == HUGE_VAL) {
163            // FIXME report errors
164        }
165        // %.8f is at worst just over 1mm.
166        fprintf(fh, "%.8f,%.8f,%.2f\n",
167                coord.xyzt.x,
168                coord.xyzt.y,
169                coord.xyzt.z);
170    }
171
172    PJ_COORD coord{{p->x, p->y, p->z, HUGE_VAL}};
173    coord = proj_trans(pj, PJ_FWD, coord);
174    if (coord.xyzt.x == HUGE_VAL ||
175        coord.xyzt.y == HUGE_VAL ||
176        coord.xyzt.z == HUGE_VAL) {
177        // FIXME report errors
178    }
179    // %.8f is at worst just over 1mm.
180    fprintf(fh, "%.8f,%.8f,%.2f\n",
181            coord.xyzt.x,
182            coord.xyzt.y,
183            coord.xyzt.z);
184}
185
186void
187KML::xsect(const img_point *p, double angle, double d1, double d2)
188{
189    if (clamp_to_ground) {
190        fputs("<Placemark><name></name><LineString><coordinates>", fh);
191    } else {
192        fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh);
193    }
194
195    double s = sin(rad(angle));
196    double c = cos(rad(angle));
197
198    {
199        PJ_COORD coord{{p->x + s * d1, p->y + c * d1, p->z, HUGE_VAL}};
200        coord = proj_trans(pj, PJ_FWD, coord);
201        if (coord.xyzt.x == HUGE_VAL ||
202            coord.xyzt.y == HUGE_VAL ||
203            coord.xyzt.z == HUGE_VAL) {
204            // FIXME report errors
205        }
206        // %.8f is at worst just over 1mm.
207        fprintf(fh, "%.8f,%.8f,%.2f ",
208                coord.xyzt.x,
209                coord.xyzt.y,
210                coord.xyzt.z);
211    }
212
213    {
214        PJ_COORD coord{{p->x - s * d2, p->y - c * d2, p->z, HUGE_VAL}};
215        coord = proj_trans(pj, PJ_FWD, coord);
216        if (coord.xyzt.x == HUGE_VAL ||
217            coord.xyzt.y == HUGE_VAL ||
218            coord.xyzt.z == HUGE_VAL) {
219            // FIXME report errors
220        }
221        // %.8f is at worst just over 1mm.
222        fprintf(fh, "%.8f,%.8f,%.2f\n",
223                coord.xyzt.x,
224                coord.xyzt.y,
225                coord.xyzt.z);
226    }
227
228    fputs("</coordinates></LineString></Placemark>\n", fh);
229}
230
231void
232KML::wall(const img_point *p, double angle, double d)
233{
234    if (!in_wall) {
235        if (clamp_to_ground) {
236            fputs("<Placemark><name></name><LineString><coordinates>", fh);
237        } else {
238            fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh);
239        }
240        in_wall = true;
241    }
242
243    double s = sin(rad(angle));
244    double c = cos(rad(angle));
245
246    PJ_COORD coord{{p->x + s * d, p->y + c * d, p->z, HUGE_VAL}};
247    coord = proj_trans(pj, PJ_FWD, coord);
248    if (coord.xyzt.x == HUGE_VAL ||
249        coord.xyzt.y == HUGE_VAL ||
250        coord.xyzt.z == HUGE_VAL) {
251        // FIXME report errors
252    }
253    // %.8f is at worst just over 1mm.
254    fprintf(fh, "%.8f,%.8f,%.2f\n",
255            coord.xyzt.x,
256            coord.xyzt.y,
257            coord.xyzt.z);
258}
259
260void
261KML::passage(const img_point *p, double angle, double d1, double d2)
262{
263    double s = sin(rad(angle));
264    double c = cos(rad(angle));
265
266    PJ_COORD coord1{{p->x + s * d1, p->y + c * d1, p->z, HUGE_VAL}};
267    coord1 = proj_trans(pj, PJ_FWD, coord1);
268    if (coord1.xyzt.x == HUGE_VAL ||
269        coord1.xyzt.y == HUGE_VAL ||
270        coord1.xyzt.z == HUGE_VAL) {
271        // FIXME report errors
272    }
273    double x1 = coord1.xyzt.x;
274    double y1 = coord1.xyzt.y;
275    double z1 = coord1.xyzt.z;
276
277    PJ_COORD coord2{{p->x - s * d2, p->y - c * d2, p->z, HUGE_VAL}};
278    coord2 = proj_trans(pj, PJ_FWD, coord2);
279    if (coord2.xyzt.x == HUGE_VAL ||
280        coord2.xyzt.y == HUGE_VAL ||
281        coord2.xyzt.z == HUGE_VAL) {
282        // FIXME report errors
283    }
284    double x2 = coord2.xyzt.x;
285    double y2 = coord2.xyzt.y;
286    double z2 = coord2.xyzt.z;
287
288    // Define each passage as a multigeometry comprising of one quadrilateral
289    // per section.  This prevents invalid geometry (such as self-intersecting
290    // polygons) being created.
291
292    if (!in_passage){
293        in_passage = true;
294        fputs("<Placemark><name></name><MultiGeometry>\n", fh);
295    } else {
296        if (clamp_to_ground) {
297            fputs("<Polygon>"
298                  "<outerBoundaryIs><LinearRing><coordinates>\n", fh);
299        } else {
300            fputs("<Polygon><altitudeMode>absolute</altitudeMode>"
301                  "<outerBoundaryIs><LinearRing><coordinates>\n", fh);
302        }
303
304        // Draw anti-clockwise around the ring.
305        fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ());
306        fprintf(fh, "%.8f,%.8f,%.2f\n", v1.GetX(), v1.GetY(), v1.GetZ());
307
308        fprintf(fh, "%.8f,%.8f,%.2f\n", x1, y1, z1);
309        fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2);
310
311        // Close the ring.
312        fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ());
313
314        fputs("</coordinates></LinearRing></outerBoundaryIs>"
315              "</Polygon>\n", fh);
316    }
317
318    v2 = Vector3(x2, y2, z2);
319    v1 = Vector3(x1, y1, z1);
320}
321
322void
323KML::tube_end()
324{
325    if (in_passage){
326        fputs("</MultiGeometry></Placemark>\n", fh);
327        in_passage = false;
328    }
329    if (in_wall) {
330        fputs("</coordinates></LineString></Placemark>\n", fh);
331        in_wall = false;
332    }
333}
334
335void
336KML::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
337{
338    PJ_COORD coord{{p->x, p->y, p->z, HUGE_VAL}};
339    coord = proj_trans(pj, PJ_FWD, coord);
340    if (coord.xyzt.x == HUGE_VAL ||
341        coord.xyzt.y == HUGE_VAL ||
342        coord.xyzt.z == HUGE_VAL) {
343        // FIXME report errors
344    }
345    // %.8f is at worst just over 1mm.
346    fprintf(fh, "<Placemark><Point><coordinates>%.8f,%.8f,%.2f</coordinates></Point><name>",
347            coord.xyzt.x,
348            coord.xyzt.y,
349            coord.xyzt.z);
350    html_escape(fh, s);
351    fputs("</name>", fh);
352    // Add a "pin" symbol with colour matching what aven shows.
353    switch (type) {
354        case FIXES:
355            fputs("<styleUrl>#fix</styleUrl>", fh);
356            break;
357        case EXPORTS:
358            fputs("<styleUrl>#exp</styleUrl>", fh);
359            break;
360        case ENTS:
361            fputs("<styleUrl>#ent</styleUrl>", fh);
362            break;
363    }
364    fputs("</Placemark>\n", fh);
365}
366
367void
368KML::footer()
369{
370    if (in_linestring)
371        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
372    fputs("</Document></kml>\n", fh);
373}
Note: See TracBrowser for help on using the repository browser.