source: git/src/kml.cc @ 8aea905a

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 8aea905a was 32a040e, checked in by Olly Betts <olly@…>, 7 years ago

Support "clamp to ground" for KML export

The default altitude mode for KML is "clampToGround", which renders
data on the surface of the terrain. This is useful with KML viewers
which render the terrain as opaque so underground data isn't visible.
Rendering cave passages on the surface isn't great, but is better
than not being able to see them at all.

This option may also be helpful if you want to see where to look on the
surface for new entrances.

  • Property mode set to 100644
File size: 8.2 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    // Draw along one side and push the other onto a stack, then at the end pop
221    // the stack and write out those points to give one polygon, fewer points,
222    // and a smaller file.
223    double x1 = p->x + c * d1;
224    double y1 = p->y + s * d1;
225    double z1 = p->z;
226    pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1);
227    x1 = deg(x1);
228    y1 = deg(y1);
229
230    double x2 = p->x - c * d2;
231    double y2 = p->y - s * d2;
232    double z2 = p->z;
233    pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2);
234    x2 = deg(x2);
235    y2 = deg(y2);
236
237    if (psg.empty()) {
238        if (clamp_to_ground) {
239            fputs("<Placemark><name></name><Polygon>"
240                  "<outerBoundaryIs><LinearRing><coordinates>\n", fh);
241        } else {
242            fputs("<Placemark><name></name><Polygon><altitudeMode>absolute</altitudeMode>"
243                  "<outerBoundaryIs><LinearRing><coordinates>\n", fh);
244        }
245    }
246    // NB - order of vertices should be anti-clockwise in a KML file, so go
247    // along the right wall now, and put the left wall points on a stack to
248    // come back along at the end.
249    fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2);
250    psg.push_back(Vector3(x1, y1, z1));
251}
252
253void
254KML::tube_end()
255{
256    if (!psg.empty()) {
257        vector<Vector3>::const_reverse_iterator i;
258        for (i = psg.rbegin(); i != psg.rend(); ++i) {
259            fprintf(fh, "%.8f,%.8f,%.2f\n", i->GetX(), i->GetY(), i->GetZ());
260        }
261        psg.clear();
262        fputs("</coordinates></LinearRing></outerBoundaryIs>"
263              "</Polygon></Placemark>\n", fh);
264    }
265    if (in_wall) {
266        fputs("</coordinates></LineString></Placemark>\n", fh);
267        in_wall = false;
268    }
269}
270
271void
272KML::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
273{
274    double X = p->x, Y = p->y, Z = p->z;
275    pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
276    X = deg(X);
277    Y = deg(Y);
278    // %.8f is at worst just over 1mm.
279    fprintf(fh, "<Placemark><Point><coordinates>%.8f,%.8f,%.2f</coordinates></Point><name>", X, Y, Z);
280    html_escape(fh, s);
281    fputs("</name>", fh);
282    // Add a "pin" symbol with colour matching what aven shows.
283    switch (type) {
284        case FIXES:
285            fputs("<styleUrl>#fix</styleUrl>", fh);
286            break;
287        case EXPORTS:
288            fputs("<styleUrl>#exp</styleUrl>", fh);
289            break;
290        case ENTS:
291            fputs("<styleUrl>#ent</styleUrl>", fh);
292            break;
293    }
294    fputs("</Placemark>\n", fh);
295}
296
297void
298KML::footer()
299{
300    if (in_linestring)
301        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
302    fputs("</Document></kml>\n", fh);
303}
Note: See TracBrowser for help on using the repository browser.