source: git/src/kml.cc @ 8719711

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-data
Last change on this file since 8719711 was 8719711, checked in by Olly Betts <olly@…>, 5 years 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
RevLine 
[1534ed9]1/* kml.cc
2 * Export from Aven as KML.
3 */
4/* Copyright (C) 2012 Olaf Kähler
[d01b55a]5 * Copyright (C) 2012,2013,2014,2015,2016,2017,2018 Olly Betts
[1534ed9]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"
[8719711]35#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1
[1534ed9]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
[32a040e]66KML::KML(const char * input_datum, bool clamp_to_ground_)
67    : clamp_to_ground(clamp_to_ground_)
[1534ed9]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{
[f2cb101]92    static const int default_passes[] = {
[e63994c]93        PASG, XSECT, WALL1, WALL2, LEGS|SURF, LABELS|ENTS|FIXES|EXPORTS, 0
[f2cb101]94    };
[1534ed9]95    return default_passes;
96}
97
98/* Initialise KML routines. */
[55a861a]99void KML::header(const char * title, const char *, time_t,
100                 double, double, double, double, double, double)
[1534ed9]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);
[7aed359]108    // Set up styles for the icons to reduce the file size.
109    fputs("<Style id=\"fix\"><IconStyle>"
[72e6593]110          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/red-blank.png</href></Icon>"
[7aed359]111          "</IconStyle></Style>\n", fh);
112    fputs("<Style id=\"exp\"><IconStyle>"
[72e6593]113          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/blu-blank.png</href></Icon>"
[7aed359]114          "</IconStyle></Style>\n", fh);
115    fputs("<Style id=\"ent\"><IconStyle>"
[72e6593]116          "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-blank.png</href></Icon>"
[7aed359]117          "</IconStyle></Style>\n", fh);
[1534ed9]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
[f03d89e]123void
[0fe63d3]124KML::start_pass(int)
[f03d89e]125{
126    if (in_linestring) {
127        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
128        in_linestring = false;
129    }
130}
131
[1534ed9]132void
[a2c29c1]133KML::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
[1534ed9]134{
135    if (fPendingMove) {
[3b23819]136        if (!in_linestring) {
137            in_linestring = true;
138            fputs("<Placemark><MultiGeometry>\n", fh);
139        } else {
140            fputs("</coordinates></LineString>\n", fh);
141        }
[32a040e]142        if (clamp_to_ground) {
143            fputs("<LineString><coordinates>\n", fh);
144        } else {
145            fputs("<LineString><altitudeMode>absolute</altitudeMode><coordinates>\n", fh);
146        }
[1534ed9]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.
[ae6a68c]152        fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z);
[1534ed9]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.
[ae6a68c]159    fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z);
[1534ed9]160}
161
[e63994c]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
[32a040e]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    }
[9ada71a]187    fprintf(fh, "%.8f,%.8f,%.2f %.8f,%.8f,%.2f", x1, y1, z1, x2, y2, z2);
[e63994c]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) {
[6028fff]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;
[e63994c]211    }
[9ada71a]212    fprintf(fh, "%.8f,%.8f,%.2f\n", x, y, z);
[e63994c]213}
214
[f2cb101]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
[6028fff]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.
[0939dcd]238
239    if (!in_passage){
[6028fff]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        }
[0939dcd]250
[6028fff]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());
[0939dcd]254
[6028fff]255        fprintf(fh, "%.8f,%.8f,%.2f\n", x1, y1, z1);
256        fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2);
[0939dcd]257
[6028fff]258        // Close the ring.
259        fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ());
[0939dcd]260
[6028fff]261        fputs("</coordinates></LinearRing></outerBoundaryIs>"
262              "</Polygon>\n", fh);
[0939dcd]263    }
264
[6028fff]265    v2 = Vector3(x2, y2, z2);
266    v1 = Vector3(x1, y1, z1);
[f2cb101]267}
268
269void
270KML::tube_end()
271{
[0939dcd]272    if (in_passage){
[6028fff]273        fputs("</MultiGeometry></Placemark>\n", fh);
274        in_passage = false;
[e63994c]275    }
276    if (in_wall) {
[6028fff]277        fputs("</coordinates></LineString></Placemark>\n", fh);
278        in_wall = false;
[f2cb101]279    }
280}
281
[1534ed9]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:
[7aed359]296            fputs("<styleUrl>#fix</styleUrl>", fh);
[1534ed9]297            break;
298        case EXPORTS:
[7aed359]299            fputs("<styleUrl>#exp</styleUrl>", fh);
[1534ed9]300            break;
301        case ENTS:
[7aed359]302            fputs("<styleUrl>#ent</styleUrl>", fh);
[1534ed9]303            break;
304    }
305    fputs("</Placemark>\n", fh);
306}
307
308void
309KML::footer()
310{
[ae6a68c]311    if (in_linestring)
[3b23819]312        fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
[1534ed9]313    fputs("</Document></kml>\n", fh);
314}
Note: See TracBrowser for help on using the repository browser.