source: git/src/kml.cc @ 8719711

Last change on this file since 8719711 was 8719711, checked in by Olly Betts <olly@…>, 4 months ago

Support PROJ 5.x

PROJ 6.x won't work yet. See https://trac.survex.com/ticket/102 for
details.

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