Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * Project: PROJ |
3 | | * Purpose: proj_info() and proj_pj_info() |
4 | | * |
5 | | * Author: Thomas Knudsen, thokn@sdfe.dk, 2016-06-09/2016-11-06 |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2016, 2017 Thomas Knudsen/SDFE |
9 | | * |
10 | | * Permission is hereby granted, free of charge, to any person obtaining a |
11 | | * copy of this software and associated documentation files (the "Software"), |
12 | | * to deal in the Software without restriction, including without limitation |
13 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
14 | | * and/or sell copies of the Software, and to permit persons to whom the |
15 | | * Software is furnished to do so, subject to the following conditions: |
16 | | * |
17 | | * The above copyright notice and this permission notice shall be included |
18 | | * in all copies or substantial portions of the Software. |
19 | | * |
20 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
21 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
22 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
23 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
24 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
25 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
26 | | * DEALINGS IN THE SOFTWARE. |
27 | | *****************************************************************************/ |
28 | | |
29 | | #define FROM_PROJ_CPP |
30 | | |
31 | | #include "proj.h" |
32 | | #include "proj_internal.h" |
33 | | |
34 | | #include "filemanager.hpp" |
35 | | |
36 | | /*****************************************************************************/ |
37 | 0 | static char *path_append(char *buf, const char *app, size_t *buf_size) { |
38 | | /****************************************************************************** |
39 | | Helper for proj_info() below. Append app to buf, separated by a |
40 | | semicolon. Also handle allocation of longer buffer if needed. |
41 | | |
42 | | Returns buffer and adjusts *buf_size through provided pointer arg. |
43 | | ******************************************************************************/ |
44 | 0 | char *p; |
45 | 0 | size_t len, applen = 0, buflen = 0; |
46 | | #ifdef _WIN32 |
47 | | const char *delim = ";"; |
48 | | #else |
49 | 0 | const char *delim = ":"; |
50 | 0 | #endif |
51 | | |
52 | | /* Nothing to do? */ |
53 | 0 | if (nullptr == app) |
54 | 0 | return buf; |
55 | 0 | applen = strlen(app); |
56 | 0 | if (0 == applen) |
57 | 0 | return buf; |
58 | | |
59 | | /* Start checking whether buf is long enough */ |
60 | 0 | if (nullptr != buf) |
61 | 0 | buflen = strlen(buf); |
62 | 0 | len = buflen + applen + strlen(delim) + 1; |
63 | | |
64 | | /* "pj_realloc", so to speak */ |
65 | 0 | if (*buf_size < len) { |
66 | 0 | p = static_cast<char *>(calloc(2 * len, sizeof(char))); |
67 | 0 | if (nullptr == p) { |
68 | 0 | free(buf); |
69 | 0 | return nullptr; |
70 | 0 | } |
71 | 0 | *buf_size = 2 * len; |
72 | 0 | if (buf != nullptr) |
73 | 0 | strcpy(p, buf); |
74 | 0 | free(buf); |
75 | 0 | buf = p; |
76 | 0 | } |
77 | 0 | assert(buf); |
78 | | |
79 | | /* Only append a delimiter if something's already there */ |
80 | 0 | if (0 != buflen) |
81 | 0 | strcat(buf, delim); |
82 | 0 | strcat(buf, app); |
83 | 0 | return buf; |
84 | 0 | } |
85 | | |
86 | | static const char *empty = {""}; |
87 | | static char version[64] = {""}; |
88 | | static PJ_INFO info = {0, 0, 0, nullptr, nullptr, nullptr, nullptr, 0}; |
89 | | |
90 | | /*****************************************************************************/ |
91 | 0 | PJ_INFO proj_info(void) { |
92 | | /****************************************************************************** |
93 | | Basic info about the current instance of the PROJ.4 library. |
94 | | |
95 | | Returns PJ_INFO struct. |
96 | | ******************************************************************************/ |
97 | 0 | size_t buf_size = 0; |
98 | 0 | char *buf = nullptr; |
99 | |
|
100 | 0 | pj_acquire_lock(); |
101 | |
|
102 | 0 | info.major = PROJ_VERSION_MAJOR; |
103 | 0 | info.minor = PROJ_VERSION_MINOR; |
104 | 0 | info.patch = PROJ_VERSION_PATCH; |
105 | | |
106 | | /* A normal version string is xx.yy.zz which is 8 characters |
107 | | long and there is room for 64 bytes in the version string. */ |
108 | 0 | snprintf(version, sizeof(version), "%d.%d.%d", info.major, info.minor, |
109 | 0 | info.patch); |
110 | |
|
111 | 0 | info.version = version; |
112 | 0 | info.release = pj_get_release(); |
113 | | |
114 | | /* build search path string */ |
115 | 0 | auto ctx = pj_get_default_ctx(); |
116 | 0 | if (ctx->search_paths.empty()) { |
117 | 0 | const auto searchpaths = pj_get_default_searchpaths(ctx); |
118 | 0 | for (const auto &path : searchpaths) { |
119 | 0 | buf = path_append(buf, path.c_str(), &buf_size); |
120 | 0 | } |
121 | 0 | } else { |
122 | 0 | for (const auto &path : ctx->search_paths) { |
123 | 0 | buf = path_append(buf, path.c_str(), &buf_size); |
124 | 0 | } |
125 | 0 | } |
126 | |
|
127 | 0 | if (info.searchpath != empty) |
128 | 0 | free(const_cast<char *>(info.searchpath)); |
129 | 0 | info.searchpath = buf ? buf : empty; |
130 | |
|
131 | 0 | info.paths = ctx->c_compat_paths; |
132 | 0 | info.path_count = static_cast<int>(ctx->search_paths.size()); |
133 | |
|
134 | 0 | pj_release_lock(); |
135 | 0 | return info; |
136 | 0 | } |
137 | | |
138 | | /*****************************************************************************/ |
139 | | void pj_clear_proj_info() |
140 | | /***************************************************************************** |
141 | | Release memory associated to "info" singleton |
142 | | *****************************************************************************/ |
143 | 11.4k | { |
144 | 11.4k | pj_acquire_lock(); |
145 | 11.4k | if (info.searchpath != empty) |
146 | 11.4k | free(const_cast<char *>(info.searchpath)); |
147 | 11.4k | info.searchpath = nullptr; |
148 | 11.4k | pj_release_lock(); |
149 | 11.4k | } |
150 | | |
151 | | /*****************************************************************************/ |
152 | 0 | PJ_PROJ_INFO proj_pj_info(PJ *P) { |
153 | | /****************************************************************************** |
154 | | Basic info about a particular instance of a projection object. |
155 | | |
156 | | Returns PJ_PROJ_INFO struct. |
157 | | ******************************************************************************/ |
158 | 0 | PJ_PROJ_INFO pjinfo; |
159 | 0 | char *def; |
160 | |
|
161 | 0 | memset(&pjinfo, 0, sizeof(PJ_PROJ_INFO)); |
162 | |
|
163 | 0 | pjinfo.accuracy = -1.0; |
164 | |
|
165 | 0 | if (nullptr == P) |
166 | 0 | return pjinfo; |
167 | | |
168 | | /* coordinate operation description */ |
169 | 0 | if (!P->alternativeCoordinateOperations.empty()) { |
170 | 0 | if (P->iCurCoordOp >= 0) { |
171 | 0 | P = P->alternativeCoordinateOperations[P->iCurCoordOp].pj; |
172 | 0 | } else { |
173 | 0 | PJ *candidateOp = nullptr; |
174 | | // If there's just a single coordinate operation which is |
175 | | // instantiable, use it. |
176 | 0 | for (const auto &op : P->alternativeCoordinateOperations) { |
177 | 0 | if (op.isInstantiable()) { |
178 | 0 | if (candidateOp == nullptr) { |
179 | 0 | candidateOp = op.pj; |
180 | 0 | } else { |
181 | 0 | candidateOp = nullptr; |
182 | 0 | break; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | } |
186 | 0 | if (candidateOp) { |
187 | 0 | P = candidateOp; |
188 | 0 | } else { |
189 | 0 | pjinfo.id = "unknown"; |
190 | 0 | pjinfo.description = "unavailable until proj_trans is called"; |
191 | 0 | pjinfo.definition = "unavailable until proj_trans is called"; |
192 | 0 | return pjinfo; |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | /* projection id */ |
198 | 0 | if (pj_param(P->ctx, P->params, "tproj").i) |
199 | 0 | pjinfo.id = pj_param(P->ctx, P->params, "sproj").s; |
200 | |
|
201 | 0 | pjinfo.description = P->descr; |
202 | 0 | if (P->iso_obj) { |
203 | 0 | auto identifiedObj = |
204 | 0 | dynamic_cast<NS_PROJ::common::IdentifiedObject *>(P->iso_obj.get()); |
205 | | // cppcheck-suppress knownConditionTrueFalse |
206 | 0 | if (identifiedObj) { |
207 | 0 | pjinfo.description = identifiedObj->nameStr().c_str(); |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | // accuracy |
212 | 0 | if (P->iso_obj) { |
213 | 0 | auto conv = dynamic_cast<const NS_PROJ::operation::Conversion *>( |
214 | 0 | P->iso_obj.get()); |
215 | | // cppcheck-suppress knownConditionTrueFalse |
216 | 0 | if (conv) { |
217 | 0 | pjinfo.accuracy = 0.0; |
218 | 0 | } else { |
219 | 0 | auto op = |
220 | 0 | dynamic_cast<const NS_PROJ::operation::CoordinateOperation *>( |
221 | 0 | P->iso_obj.get()); |
222 | | // cppcheck-suppress knownConditionTrueFalse |
223 | 0 | if (op) { |
224 | 0 | const auto &accuracies = op->coordinateOperationAccuracies(); |
225 | 0 | if (!accuracies.empty()) { |
226 | 0 | try { |
227 | 0 | pjinfo.accuracy = std::stod(accuracies[0]->value()); |
228 | 0 | } catch (const std::exception &) { |
229 | 0 | } |
230 | 0 | } |
231 | 0 | } |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | /* projection definition */ |
236 | 0 | if (P->def_full) |
237 | 0 | def = P->def_full; |
238 | 0 | else |
239 | 0 | def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ |
240 | 0 | if (nullptr == def) |
241 | 0 | pjinfo.definition = empty; |
242 | 0 | else |
243 | 0 | pjinfo.definition = pj_shrink(def); |
244 | | /* Make proj_destroy clean this up eventually */ |
245 | 0 | P->def_full = def; |
246 | |
|
247 | 0 | pjinfo.has_inverse = pj_has_inverse(P); |
248 | 0 | return pjinfo; |
249 | 0 | } |