Fix rotated exports
[survex.git] / src / kml.cc
blobd9e88c62bfa8e72a63075d767dfc1e81da1b2381
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
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.
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.
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
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
26 #include "kml.h"
28 #include "export.h" // For LABELS, etc
30 #include <stdio.h>
31 #include <string>
32 #include <math.h>
34 #include "useful.h"
35 #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1
36 #include <proj_api.h>
38 #include "aven.h"
39 #include "message.h"
41 using namespace std;
43 #define WGS84_DATUM_STRING "+proj=longlat +ellps=WGS84 +datum=WGS84"
45 static void
46 html_escape(FILE *fh, const char *s)
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);
62 ++s;
66 KML::KML(const char * input_datum, bool clamp_to_ground_)
67 : clamp_to_ground(clamp_to_ground_)
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;
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;
81 KML::~KML()
83 if (pj_input)
84 pj_free(pj_input);
85 if (pj_output)
86 pj_free(pj_output);
89 const int *
90 KML::passes() const
92 static const int default_passes[] = {
93 PASG, XSECT, WALL1, WALL2, LEGS|SURF, LABELS|ENTS|FIXES|EXPORTS, 0
95 return default_passes;
98 /* Initialise KML routines. */
99 void KML::header(const char * title, const char *, time_t,
100 double, double, double, double, double, double)
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...
123 void
124 KML::start_pass(int)
126 if (in_linestring) {
127 fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
128 in_linestring = false;
132 void
133 KML::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
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);
142 if (clamp_to_ground) {
143 fputs("<LineString><coordinates>\n", fh);
144 } else {
145 fputs("<LineString><altitudeMode>absolute</altitudeMode><coordinates>\n", fh);
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);
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);
162 void
163 KML::xsect(const img_point *p, double angle, double d1, double d2)
165 double s = sin(rad(angle));
166 double c = cos(rad(angle));
168 double x1 = p->x + s * d1;
169 double y1 = p->y + c * 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);
175 double x2 = p->x - s * d2;
176 double y2 = p->y - c * 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);
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);
187 fprintf(fh, "%.8f,%.8f,%.2f %.8f,%.8f,%.2f", x1, y1, z1, x2, y2, z2);
188 fputs("</coordinates></LineString></Placemark>\n", fh);
191 void
192 KML::wall(const img_point *p, double angle, double d)
194 double s = sin(rad(angle));
195 double c = cos(rad(angle));
197 double x = p->x + s * d;
198 double y = p->y + c * 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);
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);
210 in_wall = true;
212 fprintf(fh, "%.8f,%.8f,%.2f\n", x, y, z);
215 void
216 KML::passage(const img_point *p, double angle, double d1, double d2)
218 double s = sin(rad(angle));
219 double c = cos(rad(angle));
221 double x1 = p->x + s * d1;
222 double y1 = p->y + c * 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);
228 double x2 = p->x - s * d2;
229 double y2 = p->y - c * 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);
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.
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);
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());
255 fprintf(fh, "%.8f,%.8f,%.2f\n", x1, y1, z1);
256 fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2);
258 // Close the ring.
259 fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ());
261 fputs("</coordinates></LinearRing></outerBoundaryIs>"
262 "</Polygon>\n", fh);
265 v2 = Vector3(x2, y2, z2);
266 v1 = Vector3(x1, y1, z1);
269 void
270 KML::tube_end()
272 if (in_passage){
273 fputs("</MultiGeometry></Placemark>\n", fh);
274 in_passage = false;
276 if (in_wall) {
277 fputs("</coordinates></LineString></Placemark>\n", fh);
278 in_wall = false;
282 void
283 KML::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
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;
305 fputs("</Placemark>\n", fh);
308 void
309 KML::footer()
311 if (in_linestring)
312 fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
313 fputs("</Document></kml>\n", fh);