[1686083] | 1 | /* findentrances.cc |
---|
| 2 | * Simple converter from survex .3d files to a list of entrances in GPX format |
---|
[ac71f8a] | 3 | * |
---|
[1686083] | 4 | * Copyright (C) 2012 Olaf Kähler |
---|
[ceea8c5] | 5 | * Copyright (C) 2012,2013,2015 Olly Betts |
---|
[1686083] | 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 | /* |
---|
[ac71f8a] | 23 | * This program parses the survex file, creates a list of entrances and does |
---|
| 24 | * the coordinate transformations from almost arbitrary formats into WGS84 |
---|
| 25 | * using the PROJ.4 library. The output is then written as a GPX file ready |
---|
| 26 | * for use in your favourite GPS software. Everything else is kept as simple |
---|
| 27 | * and minimalistic as possible. |
---|
| 28 | * |
---|
| 29 | * Usage: |
---|
[ceea8c5] | 30 | * findentrances [-d <+proj +datum +string>] <input.3d> |
---|
[ac71f8a] | 31 | * |
---|
| 32 | * Example for data given in BMN M31 (Totes Gebirge, Austria): |
---|
[e34a704b] | 33 | * findentrances -d '+proj=tmerc +lat_0=0 +lon_0=13d20 +k=1 +x_0=0 +y_0=-5200000 +ellps=bessel +towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232' cucc_austria.3d > ent.gpx |
---|
[ac71f8a] | 34 | * |
---|
| 35 | * Example for data given in british grid SD (Yorkshire): |
---|
[e34a704b] | 36 | * findentrances -d '+proj=tmerc +lat_0=49d +lon_0=-2d +k=0.999601 +x_0=100000 +y_0=-500000 +ellps=airy +towgs84=375,-111,431,0,0,0,0' yorkshire/all.3d > ent.gpx |
---|
[ac71f8a] | 37 | * |
---|
| 38 | * Example for data given as proper british grid reference: |
---|
[e34a704b] | 39 | * findentrances -d '+proj=tmerc +lat_0=49d +lon_0=-2d +k=0.999601 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=375,-111,431,0,0,0,0' all.3d > ent.gpx |
---|
[ac71f8a] | 40 | * |
---|
| 41 | */ |
---|
| 42 | |
---|
| 43 | #include <stdio.h> |
---|
| 44 | #include <string> |
---|
| 45 | #include <vector> |
---|
| 46 | #include <algorithm> |
---|
| 47 | #include <math.h> |
---|
| 48 | |
---|
| 49 | #include <proj_api.h> |
---|
| 50 | |
---|
| 51 | #include "message.h" |
---|
| 52 | #include "cmdline.h" |
---|
[a405bc1] | 53 | #include "img_hosted.h" |
---|
[ac71f8a] | 54 | |
---|
[8ec699d] | 55 | using namespace std; |
---|
| 56 | |
---|
[4d34e1f] | 57 | #define WGS84_DATUM_STRING "+proj=longlat +ellps=WGS84 +datum=WGS84" |
---|
| 58 | |
---|
[ac71f8a] | 59 | struct Point { |
---|
[8ec699d] | 60 | string label; |
---|
[23dc8cb] | 61 | double x, y, z; |
---|
[ac71f8a] | 62 | }; |
---|
| 63 | |
---|
[4d34e1f] | 64 | static void |
---|
[ceea8c5] | 65 | read_survey(const char *filename, vector<Point> & points, const char ** p_proj) |
---|
[ac71f8a] | 66 | { |
---|
[23dc8cb] | 67 | img *survey = img_open_survey(filename, NULL); |
---|
| 68 | if (!survey) { |
---|
[a405bc1] | 69 | fatalerror(img_error2msg(img_error()), filename); |
---|
[23dc8cb] | 70 | } |
---|
| 71 | |
---|
[ceea8c5] | 72 | if (survey->cs) { |
---|
| 73 | if (*p_proj) { |
---|
| 74 | fprintf(stderr, "3d file specifies the coordinate system, ignoring datum specified on command line\n"); |
---|
| 75 | } |
---|
| 76 | *p_proj = osstrdup(survey->cs); |
---|
| 77 | } else if (!*p_proj) { |
---|
| 78 | cmdline_syntax(); |
---|
| 79 | fprintf(stderr, "3d file does not specify the coordinate system - you need to specify the datum on the command line\n"); |
---|
| 80 | exit(1); |
---|
| 81 | } |
---|
| 82 | |
---|
[23dc8cb] | 83 | int result; |
---|
| 84 | do { |
---|
| 85 | img_point pt; |
---|
| 86 | result = img_read_item(survey, &pt); |
---|
| 87 | |
---|
| 88 | if (result == img_LABEL) { |
---|
| 89 | if (survey->flags & img_SFLAG_ENTRANCE) { |
---|
| 90 | Point newPt; |
---|
[ea788a3] | 91 | newPt.label.assign(survey->label); |
---|
[23dc8cb] | 92 | newPt.x = pt.x; |
---|
| 93 | newPt.y = pt.y; |
---|
| 94 | newPt.z = pt.z; |
---|
| 95 | points.push_back(newPt); |
---|
| 96 | } |
---|
| 97 | } else if (result == img_BAD) { |
---|
[4d34e1f] | 98 | img_close(survey); |
---|
[a405bc1] | 99 | fatalerror(img_error2msg(img_error()), filename); |
---|
[ac71f8a] | 100 | } |
---|
[23dc8cb] | 101 | } while (result != img_STOP); |
---|
[ac71f8a] | 102 | |
---|
[23dc8cb] | 103 | img_close(survey); |
---|
[ac71f8a] | 104 | } |
---|
| 105 | |
---|
[4d34e1f] | 106 | static void |
---|
| 107 | convert_coordinates(vector<Point> & points, const char *inputDatum, const char *outputDatum) |
---|
[ac71f8a] | 108 | { |
---|
[23dc8cb] | 109 | projPJ pj_input, pj_output; |
---|
| 110 | if (!(pj_input = pj_init_plus(inputDatum))) { |
---|
[4d34e1f] | 111 | fatalerror(/*Failed to initialise input coordinate system “%s”*/287, inputDatum); |
---|
[23dc8cb] | 112 | } |
---|
| 113 | if (!(pj_output = pj_init_plus(outputDatum))) { |
---|
[4d34e1f] | 114 | fatalerror(/*Failed to initialise output coordinate system “%s”*/288, outputDatum); |
---|
[23dc8cb] | 115 | } |
---|
| 116 | |
---|
| 117 | for (size_t i=0; i<points.size(); ++i) { |
---|
| 118 | pj_transform(pj_input, pj_output, 1, 1, &(points[i].x), &(points[i].y), &(points[i].z)); |
---|
| 119 | } |
---|
[ac71f8a] | 120 | } |
---|
| 121 | |
---|
| 122 | struct SortPointsByLabel { |
---|
[23dc8cb] | 123 | bool operator()(const Point & a, const Point & b) |
---|
| 124 | { return a.label < b.label; } |
---|
[ac71f8a] | 125 | } SortPointsByLabel; |
---|
| 126 | |
---|
[4d34e1f] | 127 | static void |
---|
| 128 | sort_points(vector<Point> & points) |
---|
[ac71f8a] | 129 | { |
---|
[8ec699d] | 130 | sort(points.begin(), points.end(), SortPointsByLabel); |
---|
[ac71f8a] | 131 | } |
---|
| 132 | |
---|
[4d34e1f] | 133 | static void |
---|
| 134 | write_gpx(const vector<Point> & points, FILE *file) |
---|
[ac71f8a] | 135 | { |
---|
[34b9d54] | 136 | fprintf(file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"1.0\" creator=\"survex - findentrances\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n"); |
---|
[23dc8cb] | 137 | for (size_t i=0; i<points.size(); ++i) { |
---|
| 138 | const Point & pt = points[i]; |
---|
[48e82bf] | 139 | // %.8f is at worst just over 1mm. |
---|
[34b9d54] | 140 | fprintf(file, "<wpt lon=\"%.8f\" lat=\"%.8f\"><ele>%.2f</ele><name>%s</name></wpt>\n", pt.x*180.0/M_PI, pt.y*180.0/M_PI, pt.z, pt.label.c_str()); |
---|
[23dc8cb] | 141 | } |
---|
| 142 | |
---|
[34b9d54] | 143 | fprintf(file, "</gpx>\n"); |
---|
[ac71f8a] | 144 | } |
---|
| 145 | |
---|
| 146 | static const struct option long_opts[] = { |
---|
[23dc8cb] | 147 | {"datum", required_argument, 0, 'd'}, |
---|
[56c01a3] | 148 | {"help", no_argument, 0, HLP_HELP}, |
---|
| 149 | {"version", no_argument, 0, HLP_VERSION}, |
---|
[23dc8cb] | 150 | {0, 0, 0, 0} |
---|
[ac71f8a] | 151 | }; |
---|
| 152 | |
---|
[375de6a] | 153 | static const char *short_opts = "d:"; |
---|
[ac71f8a] | 154 | |
---|
[4d34e1f] | 155 | static struct help_msg help[] = { |
---|
| 156 | /* <-- */ |
---|
[087c0ad] | 157 | /* TRANSLATORS: The PROJ library is used to do coordinate transformations |
---|
| 158 | * (https://trac.osgeo.org/proj/) - the user passes a string to tell PROJ |
---|
| 159 | * what the input datum is. */ |
---|
[4d34e1f] | 160 | {HLP_ENCODELONG(0), /*input datum as string to pass to PROJ*/389, 0}, |
---|
| 161 | {0, 0, 0} |
---|
| 162 | }; |
---|
| 163 | |
---|
[ac71f8a] | 164 | int main(int argc, char **argv) |
---|
| 165 | { |
---|
[23dc8cb] | 166 | msg_init(argv); |
---|
| 167 | |
---|
[dcc50a4] | 168 | const char *datum_string = NULL; |
---|
[4d34e1f] | 169 | cmdline_set_syntax_message(/*-d PROJ_DATUM 3D_FILE*/388, 0, NULL); |
---|
| 170 | cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, 1); |
---|
[23dc8cb] | 171 | while (1) { |
---|
| 172 | int opt = cmdline_getopt(); |
---|
| 173 | if (opt == EOF) break; |
---|
| 174 | else if (opt == 'd') datum_string = optarg; |
---|
| 175 | } |
---|
[dcc50a4] | 176 | |
---|
[375de6a] | 177 | const char *survey_filename = argv[optind]; |
---|
[23dc8cb] | 178 | |
---|
[8ec699d] | 179 | vector<Point> points; |
---|
[ceea8c5] | 180 | read_survey(survey_filename, points, &datum_string); |
---|
[4d34e1f] | 181 | convert_coordinates(points, datum_string, WGS84_DATUM_STRING); |
---|
| 182 | sort_points(points); |
---|
| 183 | write_gpx(points, stdout); |
---|
[ac71f8a] | 184 | } |
---|