/* kml.cc * Export from Aven as KML. */ /* Copyright (C) 2012 Olaf Kähler * Copyright (C) 2012,2013,2014,2015,2016,2017,2018 Olly Betts * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H # include #endif #include "kml.h" #include "export.h" // For LABELS, etc #include #include #include #include "useful.h" #define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H 1 #include #include "aven.h" #include "message.h" using namespace std; #define WGS84_DATUM_STRING "+proj=longlat +ellps=WGS84 +datum=WGS84" static void html_escape(FILE *fh, const char *s) { while (*s) { switch (*s) { case '<': fputs("<", fh); break; case '>': fputs(">", fh); break; case '&': fputs("&", fh); break; default: PUTC(*s, fh); } ++s; } } KML::KML(const char * input_datum, bool clamp_to_ground_) : clamp_to_ground(clamp_to_ground_) { if (!(pj_input = pj_init_plus(input_datum))) { wxString m = wmsg(/*Failed to initialise input coordinate system “%s”*/287); m = wxString::Format(m.c_str(), input_datum); throw m; } if (!(pj_output = pj_init_plus(WGS84_DATUM_STRING))) { wxString m = wmsg(/*Failed to initialise output coordinate system “%s”*/288); m = wxString::Format(m.c_str(), WGS84_DATUM_STRING); throw m; } } KML::~KML() { if (pj_input) pj_free(pj_input); if (pj_output) pj_free(pj_output); } const int * KML::passes() const { static const int default_passes[] = { PASG, XSECT, WALL1, WALL2, LEGS|SURF, LABELS|ENTS|FIXES|EXPORTS, 0 }; return default_passes; } /* Initialise KML routines. */ void KML::header(const char * title, const char *, time_t, double, double, double, double, double, double) { fputs( "\n" "\n", fh); fputs("", fh); html_escape(fh, title); fputs("\n", fh); // Set up styles for the icons to reduce the file size. fputs("\n", fh); fputs("\n", fh); fputs("\n", fh); // FIXME: does KML allow bounds? // NB Lat+long bounds are not necessarily the same as the bounds in survex // coords translated to WGS84 lat+long... } void KML::start_pass(int) { if (in_linestring) { fputs("\n", fh); in_linestring = false; } } void KML::line(const img_point *p1, const img_point *p, unsigned /*flags*/, bool fPendingMove) { if (fPendingMove) { if (!in_linestring) { in_linestring = true; fputs("\n", fh); } else { fputs("\n", fh); } if (clamp_to_ground) { fputs("\n", fh); } else { fputs("absolute\n", fh); } double X = p1->x, Y = p1->y, Z = p1->z; pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z); X = deg(X); Y = deg(Y); // %.8f is at worst just over 1mm. fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z); } double X = p->x, Y = p->y, Z = p->z; pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z); X = deg(X); Y = deg(Y); // %.8f is at worst just over 1mm. fprintf(fh, "%.8f,%.8f,%.2f\n", X, Y, Z); } void KML::xsect(const img_point *p, double angle, double d1, double d2) { double s = sin(rad(angle)); double c = cos(rad(angle)); double x1 = p->x + c * d1; double y1 = p->y + s * d1; double z1 = p->z; pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1); x1 = deg(x1); y1 = deg(y1); double x2 = p->x - c * d2; double y2 = p->y - s * d2; double z2 = p->z; pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2); x2 = deg(x2); y2 = deg(y2); if (clamp_to_ground) { fputs("", fh); } else { fputs("absolute", fh); } fprintf(fh, "%.8f,%.8f,%.2f %.8f,%.8f,%.2f", x1, y1, z1, x2, y2, z2); fputs("\n", fh); } void KML::wall(const img_point *p, double angle, double d) { double s = sin(rad(angle)); double c = cos(rad(angle)); double x = p->x + c * d; double y = p->y + s * d; double z = p->z; pj_transform(pj_input, pj_output, 1, 1, &x, &y, &z); x = deg(x); y = deg(y); if (!in_wall) { if (clamp_to_ground) { fputs("", fh); } else { fputs("absolute", fh); } in_wall = true; } fprintf(fh, "%.8f,%.8f,%.2f\n", x, y, z); } void KML::passage(const img_point *p, double angle, double d1, double d2) { double s = sin(rad(angle)); double c = cos(rad(angle)); double x1 = p->x + c * d1; double y1 = p->y + s * d1; double z1 = p->z; pj_transform(pj_input, pj_output, 1, 1, &x1, &y1, &z1); x1 = deg(x1); y1 = deg(y1); double x2 = p->x - c * d2; double y2 = p->y - s * d2; double z2 = p->z; pj_transform(pj_input, pj_output, 1, 1, &x2, &y2, &z2); x2 = deg(x2); y2 = deg(y2); // Define each passage as a multigeometry comprising of one quadrilateral // per section. This prevents invalid geometry (such as self-intersecting // polygons) being created. if (!in_passage){ in_passage = true; fputs("\n", fh); } else { if (clamp_to_ground) { fputs("" "\n", fh); } else { fputs("absolute" "\n", fh); } // Draw anti-clockwise around the ring. fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ()); fprintf(fh, "%.8f,%.8f,%.2f\n", v1.GetX(), v1.GetY(), v1.GetZ()); fprintf(fh, "%.8f,%.8f,%.2f\n", x1, y1, z1); fprintf(fh, "%.8f,%.8f,%.2f\n", x2, y2, z2); // Close the ring. fprintf(fh, "%.8f,%.8f,%.2f\n", v2.GetX(), v2.GetY(), v2.GetZ()); fputs("" "\n", fh); } v2 = Vector3(x2, y2, z2); v1 = Vector3(x1, y1, z1); } void KML::tube_end() { if (in_passage){ fputs("\n", fh); in_passage = false; } if (in_wall) { fputs("\n", fh); in_wall = false; } } void KML::label(const img_point *p, const char *s, bool /*fSurface*/, int type) { double X = p->x, Y = p->y, Z = p->z; pj_transform(pj_input, pj_output, 1, 1, &X, &Y, &Z); X = deg(X); Y = deg(Y); // %.8f is at worst just over 1mm. fprintf(fh, "%.8f,%.8f,%.2f", X, Y, Z); html_escape(fh, s); fputs("", fh); // Add a "pin" symbol with colour matching what aven shows. switch (type) { case FIXES: fputs("#fix", fh); break; case EXPORTS: fputs("#exp", fh); break; case ENTS: fputs("#ent", fh); break; } fputs("\n", fh); } void KML::footer() { if (in_linestring) fputs("\n", fh); fputs("\n", fh); }