source: git/src/kml.cc @ ded6cfa

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

Revert change to connect start and end of KML walls

This results in spurious lines.

  • Property mode set to 100644
File size: 8.3 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 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_api.h>
36
37#include "aven.h"
38#include "message.h"
39
40using namespace std;
41
42#define WGS84_DATUM_STRING "+proj=longlat +ellps=WGS84 +datum=WGS84"
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
65KML::KML(const char * input_datum, bool clamp_to_ground_)
66    : clamp_to_ground(clamp_to_ground_)
67{
68    if (!(pj_input = pj_init_plus(input_datum))) {
69        wxString m = wmsg(/*Failed to initialise input coordinate system “%s”*/287);
70        m = wxString::Format(m.c_str(), input_datum);
71        throw m;
72    }
73    if (!(pj_output = pj_init_plus(WGS84_DATUM_STRING))) {
74        wxString m = wmsg(/*Failed to initialise output coordinate system “%s”*/288);
75        m = wxString::Format(m.c_str(), WGS84_DATUM_STRING);
76        throw m;
77    }
78}
79
80KML::~KML()
81{
82    if (pj_input)
83        pj_free(pj_input);
84    if (pj_output)
85        pj_free(pj_output);
86}
87
88const int *
89KML::passes() const
90{
91    static const int default_passes[] = {
92        PASG, XSECT, WALL1, WALL2, LEGS|SURF, LABELS|ENTS|FIXES|EXPORTS, 0
93    };
94    return default_passes;
95}
96
97/* Initialise KML routines. */
98void KML::header(const char * title, const char *, time_t,
99                 double, double, double, double, double, double)
100{
101    fputs(
102"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
103"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n", fh);
104    fputs("<Document><name>", fh);
105    html_escape(fh, title);
106    fputs("</name>\n", fh);
107    // Set up styles for the icons to reduce the file size.
108    fputs("<Style id=\"fix\"><IconStyle>"
109          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/red-blank.png</href></Icon>"
110          "</IconStyle></Style>\n", fh);
111    fputs("<Style id=\"exp\"><IconStyle>"
112          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/blu-blank.png</href></Icon>"
113          "</IconStyle></Style>\n", fh);
114    fputs("<Style id=\"ent\"><IconStyle>"
115          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-blank.png</href></Icon>"
116          "</IconStyle></Style>\n", fh);
117    // FIXME: does KML allow bounds?
118    // NB Lat+long bounds are not necessarily the same as the bounds in survex
119    // coords translated to WGS84 lat+long...
120}
121
122void
123KML::start_pass(int)
124{
125    if (in_linestring) {
126        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
127        in_linestring = false;
128    }
129}
130
131void
132KML::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
133{
134    if (fPendingMove) {
135        if (!in_linestring) {
136            in_linestring = true;
137            fputs("<Placemark><MultiGeometry>\n", fh);
138        } else {
139            fputs("</coordinates></LineString>\n", fh);
140        }
141        if (clamp_to_ground) {
142            fputs("<LineString><coordinates>\n", fh);
143        } else {
144            fputs("<LineString><altitudeMode>absolute</altitudeMode><coordinates>\n", fh);
145        }
146        double X = p1->x, Y = p1->y, Z = p1->z;
147        pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
148        X = deg(X);
149        Y = deg(Y);
150        // %.8f is at worst just over 1mm.
151        fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z);
152    }
153    double X = p->x, Y = p->y, Z = p->z;
154    pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
155    X = deg(X);
156    Y = deg(Y);
157    // %.8f is at worst just over 1mm.
158    fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z);
159}
160
161void
162KML::xsect(const img_point *p, double angle, double d1, double d2)
163{
164    double s = sin(rad(angle));
165    double c = cos(rad(angle));
166
167    double x1 = p->x + c * d1;
168    double y1 = p->y + s * d1;
169    double z1 = p->z;
170    pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1);
171    x1 = deg(x1);
172    y1 = deg(y1);
173
174    double x2 = p->x - c * d2;
175    double y2 = p->y - s * d2;
176    double z2 = p->z;
177    pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2);
178    x2 = deg(x2);
179    y2 = deg(y2);
180
181    if (clamp_to_ground) {
182        fputs("<Placemark><name></name><LineString><coordinates>", fh);
183    } else {
184        fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh);
185    }
186    fprintf(fh, "%.8f,%.8f,%.2f %.8f,%.8f,%.2f", x1, y1, z1, x2, y2, z2);
187    fputs("</coordinates></LineString></Placemark>\n", fh);
188}
189
190void
191KML::wall(const img_point *p, double angle, double d)
192{
193    double s = sin(rad(angle));
194    double c = cos(rad(angle));
195
196    double x = p->x + c * d;
197    double y = p->y + s * d;
198    double z = p->z;
199    pj_transform(pj_input, pj_output, 1, 1, &x, &y, &z);
200    x = deg(x);
201    y = deg(y);
202
203    if (!in_wall) {
204        if (clamp_to_ground) {
205            fputs("<Placemark><name></name><LineString><coordinates>", fh);
206        } else {
207            fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh);
208        }
209        in_wall = true;
210    }
211    fprintf(fh, "%.8f,%.8f,%.2f\n", x, y, z);
212}
213
214void
215KML::passage(const img_point *p, double angle, double d1, double d2)
216{
217    double s = sin(rad(angle));
218    double c = cos(rad(angle));
219
220    double x1 = p->x + c * d1;
221    double y1 = p->y + s * d1;
222    double z1 = p->z;
223    pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1);
224    x1 = deg(x1);
225    y1 = deg(y1);
226
227    double x2 = p->x - c * d2;
228    double y2 = p->y - s * d2;
229    double z2 = p->z;
230    pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2);
231    x2 = deg(x2);
232    y2 = deg(y2);
233
234    // Define each passage as a multigeometry comprising of one quadrilateral
235    // per section.  This prevents invalid geometry (such as self-intersecting
236    // polygons) being created.
237
238    if (!in_passage){
239        in_passage = true;
240        fputs("<Placemark><name></name><MultiGeometry>\n", fh);
241    } else {
242        if (clamp_to_ground) {
243            fputs("<Polygon>"
244                  "<outerBoundaryIs><LinearRing><coordinates>\n", fh);
245        } else {
246            fputs("<Polygon><altitudeMode>absolute</altitudeMode>"
247                  "<outerBoundaryIs><LinearRing><coordinates>\n", fh);
248        }
249
250        // Draw anti-clockwise around the ring.
251        fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ());
252        fprintf(fh, "%.8f,%.8f,%.2f\n", v1.GetX(), v1.GetY(), v1.GetZ());
253
254        fprintf(fh, "%.8f,%.8f,%.2f\n", x1, y1, z1);
255        fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2);
256
257        // Close the ring.
258        fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ());
259
260        fputs("</coordinates></LinearRing></outerBoundaryIs>"
261              "</Polygon>\n", fh);
262    }
263
264    v2 = Vector3(x2, y2, z2);
265    v1 = Vector3(x1, y1, z1);
266}
267
268void
269KML::tube_end()
270{
271    if (in_passage){
272        fputs("</MultiGeometry></Placemark>\n", fh);
273        in_passage = false;
274    }
275    if (in_wall) {
276        fputs("</coordinates></LineString></Placemark>\n", fh);
277        in_wall = false;
278    }
279}
280
281void
282KML::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
283{
284    double X = p->x, Y = p->y, Z = p->z;
285    pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
286    X = deg(X);
287    Y = deg(Y);
288    // %.8f is at worst just over 1mm.
289    fprintf(fh, "<Placemark><Point><coordinates>%.8f,%.8f,%.2f</coordinates></Point><name>", X, Y, Z);
290    html_escape(fh, s);
291    fputs("</name>", fh);
292    // Add a "pin" symbol with colour matching what aven shows.
293    switch (type) {
294        case FIXES:
295            fputs("<styleUrl>#fix</styleUrl>", fh);
296            break;
297        case EXPORTS:
298            fputs("<styleUrl>#exp</styleUrl>", fh);
299            break;
300        case ENTS:
301            fputs("<styleUrl>#ent</styleUrl>", fh);
302            break;
303    }
304    fputs("</Placemark>\n", fh);
305}
306
307void
308KML::footer()
309{
310    if (in_linestring)
311        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
312    fputs("</Document></kml>\n", fh);
313}
Note: See TracBrowser for help on using the repository browser.