Reliably disable scale bar in perspective view
[survex.git] / src / kml.cc
blob738293ab5d3d196208cbf675210b91ebb632a92a
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 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 #include <proj_api.h>
37 #include "aven.h"
38 #include "message.h"
40 using namespace std;
42 #define WGS84_DATUM_STRING "+proj=longlat +ellps=WGS84 +datum=WGS84"
44 static void
45 html_escape(FILE *fh, const char *s)
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);
61 ++s;
65 KML::KML(const char * input_datum)
66 : pj_input(NULL), pj_output(NULL), in_linestring(false), in_wall(false)
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;
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;
80 KML::~KML()
82 if (pj_input)
83 pj_free(pj_input);
84 if (pj_output)
85 pj_free(pj_output);
88 const int *
89 KML::passes() const
91 static const int default_passes[] = {
92 PASG, XSECT, WALL1, WALL2, LEGS|SURF, LABELS|ENTS|FIXES|EXPORTS, 0
94 return default_passes;
97 /* Initialise KML routines. */
98 void KML::header(const char * title, const char *, time_t,
99 double, double, double, double, double, double)
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...
122 void
123 KML::start_pass(int)
125 if (in_linestring) {
126 fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
127 in_linestring = false;
131 void
132 KML::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove)
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);
141 fputs("<LineString><altitudeMode>absolute</altitudeMode><coordinates>\n", fh);
142 double X = p1->x, Y = p1->y, Z = p1->z;
143 pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
144 X = deg(X);
145 Y = deg(Y);
146 // %.8f is at worst just over 1mm.
147 fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z);
149 double X = p->x, Y = p->y, Z = p->z;
150 pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
151 X = deg(X);
152 Y = deg(Y);
153 // %.8f is at worst just over 1mm.
154 fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z);
157 void
158 KML::xsect(const img_point *p, double angle, double d1, double d2)
160 double s = sin(rad(angle));
161 double c = cos(rad(angle));
163 double x1 = p->x + c * d1;
164 double y1 = p->y + s * d1;
165 double z1 = p->z;
166 pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1);
167 x1 = deg(x1);
168 y1 = deg(y1);
170 double x2 = p->x - c * d2;
171 double y2 = p->y - s * d2;
172 double z2 = p->z;
173 pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2);
174 x2 = deg(x2);
175 y2 = deg(y2);
177 fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh);
178 fprintf(fh, "%.8f,%.8f,%.2f %.8f,%.8f,%.2f", x1, y1, z1, x2, y2, z2);
179 fputs("</coordinates></LineString></Placemark>\n", fh);
182 void
183 KML::wall(const img_point *p, double angle, double d)
185 double s = sin(rad(angle));
186 double c = cos(rad(angle));
188 double x = p->x + c * d;
189 double y = p->y + s * d;
190 double z = p->z;
191 pj_transform(pj_input, pj_output, 1, 1, &x, &y, &z);
192 x = deg(x);
193 y = deg(y);
195 if (!in_wall) {
196 fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh);
197 in_wall = true;
199 fprintf(fh, "%.8f,%.8f,%.2f\n", x, y, z);
202 void
203 KML::passage(const img_point *p, double angle, double d1, double d2)
205 double s = sin(rad(angle));
206 double c = cos(rad(angle));
208 // Draw along one side and push the other onto a stack, then at the end pop
209 // the stack and write out those points to give one polygon, fewer points,
210 // and a smaller file.
211 double x1 = p->x + c * d1;
212 double y1 = p->y + s * d1;
213 double z1 = p->z;
214 pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1);
215 x1 = deg(x1);
216 y1 = deg(y1);
218 double x2 = p->x - c * d2;
219 double y2 = p->y - s * d2;
220 double z2 = p->z;
221 pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2);
222 x2 = deg(x2);
223 y2 = deg(y2);
225 if (psg.empty()) {
226 fprintf(fh, "<Placemark><name></name><Polygon><altitudeMode>absolute</altitudeMode>"
227 "<outerBoundaryIs><LinearRing><coordinates>\n");
229 // NB - order of vertices should be anti-clockwise in a KML file, so go
230 // along the right wall now, and put the left wall points on a stack to
231 // come back along at the end.
232 fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2);
233 psg.push_back(Vector3(x1, y1, z1));
236 void
237 KML::tube_end()
239 if (!psg.empty()) {
240 vector<Vector3>::const_reverse_iterator i;
241 for (i = psg.rbegin(); i != psg.rend(); ++i) {
242 fprintf(fh, "%.8f,%.8f,%.2f\n", i->GetX(), i->GetY(), i->GetZ());
244 psg.clear();
245 fputs("</coordinates></LinearRing></outerBoundaryIs>"
246 "</Polygon></Placemark>\n", fh);
248 if (in_wall) {
249 fputs("</coordinates></LineString></Placemark>\n", fh);
250 in_wall = false;
254 void
255 KML::label(const img_point *p, const char *s, bool /*fSurface*/, int type)
257 double X = p->x, Y = p->y, Z = p->z;
258 pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z);
259 X = deg(X);
260 Y = deg(Y);
261 // %.8f is at worst just over 1mm.
262 fprintf(fh, "<Placemark><Point><coordinates>%.8f,%.8f,%.2f</coordinates></Point><name>", X, Y, Z);
263 html_escape(fh, s);
264 fputs("</name>", fh);
265 // Add a "pin" symbol with colour matching what aven shows.
266 switch (type) {
267 case FIXES:
268 fputs("<styleUrl>#fix</styleUrl>", fh);
269 break;
270 case EXPORTS:
271 fputs("<styleUrl>#exp</styleUrl>", fh);
272 break;
273 case ENTS:
274 fputs("<styleUrl>#ent</styleUrl>", fh);
275 break;
277 fputs("</Placemark>\n", fh);
280 void
281 KML::footer()
283 if (in_linestring)
284 fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh);
285 fputs("</Document></kml>\n", fh);