2 * Export from Aven as KML.
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
28 #include "export.h" // For LABELS, etc
42 #define WGS84_DATUM_STRING "EPSG:4326"
45 html_escape(FILE *fh
, const char *s
)
65 static void discarding_proj_logger(void *, int, const char *) { }
67 KML::KML(const char * input_datum
, bool clamp_to_ground_
)
68 : clamp_to_ground(clamp_to_ground_
)
70 /* Prevent stderr spew from PROJ. */
71 proj_log_func(PJ_DEFAULT_CTX
, nullptr, discarding_proj_logger
);
73 pj
= proj_create_crs_to_crs(PJ_DEFAULT_CTX
,
74 input_datum
, WGS84_DATUM_STRING
,
78 // Normalise the output order so x is longitude and y latitude - by
79 // default new PROJ has them switched for EPSG:4326 which just seems
81 PJ
* pj_norm
= proj_normalize_for_visualization(PJ_DEFAULT_CTX
, pj
);
87 wxString m
= wmsg(/*Failed to initialise input coordinate system “%s”*/287);
88 m
= wxString::Format(m
.c_str(), input_datum
);
102 static const int default_passes
[] = {
103 PASG
, XSECT
, WALL1
, WALL2
, LEGS
|SURF
, LABELS
|ENTS
|FIXES
|EXPORTS
, 0
105 return default_passes
;
108 /* Initialise KML routines. */
109 void KML::header(const char * title
, const char *, time_t,
110 double, double, double, double, double, double)
113 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
114 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n", fh
);
115 fputs("<Document><name>", fh
);
116 html_escape(fh
, title
);
117 fputs("</name>\n", fh
);
118 // Set up styles for the icons to reduce the file size.
119 fputs("<Style id=\"fix\"><IconStyle>"
120 "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/red-blank.png</href></Icon>"
121 "</IconStyle></Style>\n", fh
);
122 fputs("<Style id=\"exp\"><IconStyle>"
123 "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/blu-blank.png</href></Icon>"
124 "</IconStyle></Style>\n", fh
);
125 fputs("<Style id=\"ent\"><IconStyle>"
126 "<Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-blank.png</href></Icon>"
127 "</IconStyle></Style>\n", fh
);
128 // FIXME: does KML allow bounds?
129 // NB Lat+long bounds are not necessarily the same as the bounds in survex
130 // coords translated to WGS84 lat+long...
137 fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh
);
138 in_linestring
= false;
143 KML::line(const img_point
*p1
, const img_point
*p
, unsigned /*flags*/, bool fPendingMove
)
146 if (!in_linestring
) {
147 in_linestring
= true;
148 fputs("<Placemark><MultiGeometry>\n", fh
);
150 fputs("</coordinates></LineString>\n", fh
);
152 if (clamp_to_ground
) {
153 fputs("<LineString><coordinates>\n", fh
);
155 fputs("<LineString><altitudeMode>absolute</altitudeMode><coordinates>\n", fh
);
158 PJ_COORD coord
= {p1
->x
, p1
->y
, p1
->z
, HUGE_VAL
};
159 coord
= proj_trans(pj
, PJ_FWD
, coord
);
160 if (coord
.xyzt
.x
== HUGE_VAL
||
161 coord
.xyzt
.y
== HUGE_VAL
||
162 coord
.xyzt
.z
== HUGE_VAL
) {
163 // FIXME report errors
165 // %.8f is at worst just over 1mm.
166 fprintf(fh
, "%.8f,%.8f,%.2f\n",
172 PJ_COORD coord
= {p
->x
, p
->y
, p
->z
, HUGE_VAL
};
173 coord
= proj_trans(pj
, PJ_FWD
, coord
);
174 if (coord
.xyzt
.x
== HUGE_VAL
||
175 coord
.xyzt
.y
== HUGE_VAL
||
176 coord
.xyzt
.z
== HUGE_VAL
) {
177 // FIXME report errors
179 // %.8f is at worst just over 1mm.
180 fprintf(fh
, "%.8f,%.8f,%.2f\n",
187 KML::xsect(const img_point
*p
, double angle
, double d1
, double d2
)
189 if (clamp_to_ground
) {
190 fputs("<Placemark><name></name><LineString><coordinates>", fh
);
192 fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh
);
195 double s
= sin(rad(angle
));
196 double c
= cos(rad(angle
));
199 PJ_COORD coord
= {p
->x
+ s
* d1
, p
->y
+ c
* d1
, p
->z
, HUGE_VAL
};
200 coord
= proj_trans(pj
, PJ_FWD
, coord
);
201 if (coord
.xyzt
.x
== HUGE_VAL
||
202 coord
.xyzt
.y
== HUGE_VAL
||
203 coord
.xyzt
.z
== HUGE_VAL
) {
204 // FIXME report errors
206 // %.8f is at worst just over 1mm.
207 fprintf(fh
, "%.8f,%.8f,%.2f ",
214 PJ_COORD coord
= {p
->x
- s
* d2
, p
->y
- c
* d2
, p
->z
, HUGE_VAL
};
215 coord
= proj_trans(pj
, PJ_FWD
, coord
);
216 if (coord
.xyzt
.x
== HUGE_VAL
||
217 coord
.xyzt
.y
== HUGE_VAL
||
218 coord
.xyzt
.z
== HUGE_VAL
) {
219 // FIXME report errors
221 // %.8f is at worst just over 1mm.
222 fprintf(fh
, "%.8f,%.8f,%.2f\n",
228 fputs("</coordinates></LineString></Placemark>\n", fh
);
232 KML::wall(const img_point
*p
, double angle
, double d
)
235 if (clamp_to_ground
) {
236 fputs("<Placemark><name></name><LineString><coordinates>", fh
);
238 fputs("<Placemark><name></name><LineString><altitudeMode>absolute</altitudeMode><coordinates>", fh
);
243 double s
= sin(rad(angle
));
244 double c
= cos(rad(angle
));
246 PJ_COORD coord
= {p
->x
+ s
* d
, p
->y
+ c
* d
, p
->z
, HUGE_VAL
};
247 coord
= proj_trans(pj
, PJ_FWD
, coord
);
248 if (coord
.xyzt
.x
== HUGE_VAL
||
249 coord
.xyzt
.y
== HUGE_VAL
||
250 coord
.xyzt
.z
== HUGE_VAL
) {
251 // FIXME report errors
253 // %.8f is at worst just over 1mm.
254 fprintf(fh
, "%.8f,%.8f,%.2f\n",
261 KML::passage(const img_point
*p
, double angle
, double d1
, double d2
)
263 double s
= sin(rad(angle
));
264 double c
= cos(rad(angle
));
266 PJ_COORD coord1
= {p
->x
+ s
* d1
, p
->y
+ c
* d1
, p
->z
, HUGE_VAL
};
267 coord1
= proj_trans(pj
, PJ_FWD
, coord1
);
268 if (coord1
.xyzt
.x
== HUGE_VAL
||
269 coord1
.xyzt
.y
== HUGE_VAL
||
270 coord1
.xyzt
.z
== HUGE_VAL
) {
271 // FIXME report errors
273 double x1
= coord1
.xyzt
.x
;
274 double y1
= coord1
.xyzt
.y
;
275 double z1
= coord1
.xyzt
.z
;
277 PJ_COORD coord2
= {p
->x
- s
* d2
, p
->y
- c
* d2
, p
->z
, HUGE_VAL
};
278 coord2
= proj_trans(pj
, PJ_FWD
, coord2
);
279 if (coord2
.xyzt
.x
== HUGE_VAL
||
280 coord2
.xyzt
.y
== HUGE_VAL
||
281 coord2
.xyzt
.z
== HUGE_VAL
) {
282 // FIXME report errors
284 double x2
= coord2
.xyzt
.x
;
285 double y2
= coord2
.xyzt
.y
;
286 double z2
= coord2
.xyzt
.z
;
288 // Define each passage as a multigeometry comprising of one quadrilateral
289 // per section. This prevents invalid geometry (such as self-intersecting
290 // polygons) being created.
294 fputs("<Placemark><name></name><MultiGeometry>\n", fh
);
296 if (clamp_to_ground
) {
298 "<outerBoundaryIs><LinearRing><coordinates>\n", fh
);
300 fputs("<Polygon><altitudeMode>absolute</altitudeMode>"
301 "<outerBoundaryIs><LinearRing><coordinates>\n", fh
);
304 // Draw anti-clockwise around the ring.
305 fprintf(fh
, "%.8f,%.8f,%.2f\n", v2
.GetX(), v2
.GetY(), v2
.GetZ());
306 fprintf(fh
, "%.8f,%.8f,%.2f\n", v1
.GetX(), v1
.GetY(), v1
.GetZ());
308 fprintf(fh
, "%.8f,%.8f,%.2f\n", x1
, y1
, z1
);
309 fprintf(fh
, "%.8f,%.8f,%.2f\n", x2
, y2
, z2
);
312 fprintf(fh
, "%.8f,%.8f,%.2f\n", v2
.GetX(), v2
.GetY(), v2
.GetZ());
314 fputs("</coordinates></LinearRing></outerBoundaryIs>"
318 v2
= Vector3(x2
, y2
, z2
);
319 v1
= Vector3(x1
, y1
, z1
);
326 fputs("</MultiGeometry></Placemark>\n", fh
);
330 fputs("</coordinates></LineString></Placemark>\n", fh
);
336 KML::label(const img_point
*p
, const char *s
, bool /*fSurface*/, int type
)
338 PJ_COORD coord
= {p
->x
, p
->y
, p
->z
, HUGE_VAL
};
339 coord
= proj_trans(pj
, PJ_FWD
, coord
);
340 if (coord
.xyzt
.x
== HUGE_VAL
||
341 coord
.xyzt
.y
== HUGE_VAL
||
342 coord
.xyzt
.z
== HUGE_VAL
) {
343 // FIXME report errors
345 // %.8f is at worst just over 1mm.
346 fprintf(fh
, "<Placemark><Point><coordinates>%.8f,%.8f,%.2f</coordinates></Point><name>",
351 fputs("</name>", fh
);
352 // Add a "pin" symbol with colour matching what aven shows.
355 fputs("<styleUrl>#fix</styleUrl>", fh
);
358 fputs("<styleUrl>#exp</styleUrl>", fh
);
361 fputs("<styleUrl>#ent</styleUrl>", fh
);
364 fputs("</Placemark>\n", fh
);
371 fputs("</coordinates></LineString></MultiGeometry></Placemark>\n", fh
);
372 fputs("</Document></kml>\n", fh
);