Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * Project: PROJ |
3 | | * Purpose: pj_create_internal() related stuff |
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 | | #include <math.h> |
34 | | |
35 | | /*************************************************************************************/ |
36 | 71.8k | static PJ *skip_prep_fin(PJ *P) { |
37 | | /************************************************************************************** |
38 | | Skip prepare and finalize function for the various "helper operations" added |
39 | | to P when in cs2cs compatibility mode. |
40 | | **************************************************************************************/ |
41 | 71.8k | P->skip_fwd_prepare = 1; |
42 | 71.8k | P->skip_fwd_finalize = 1; |
43 | 71.8k | P->skip_inv_prepare = 1; |
44 | 71.8k | P->skip_inv_finalize = 1; |
45 | 71.8k | return P; |
46 | 71.8k | } |
47 | | |
48 | | /*************************************************************************************/ |
49 | 228k | static int cs2cs_emulation_setup(PJ *P) { |
50 | | /************************************************************************************** |
51 | | If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the |
52 | | 4D API equivalent operations, so the preparation and finalization steps in |
53 | | the pj_inv/pj_fwd invocators can emulate the behavior of pj_transform and |
54 | | the cs2cs app. |
55 | | |
56 | | Returns 1 on success, 0 on failure |
57 | | **************************************************************************************/ |
58 | 228k | PJ *Q; |
59 | 228k | paralist *p; |
60 | 228k | int do_cart = 0; |
61 | 228k | if (nullptr == P) |
62 | 29.1k | return 0; |
63 | | |
64 | | /* Don't recurse when calling proj_create (which calls us back) */ |
65 | 199k | if (pj_param_exists(P->params, "break_cs2cs_recursion")) |
66 | 71.8k | return 1; |
67 | | |
68 | | /* Swap axes? */ |
69 | 127k | p = pj_param_exists(P->params, "axis"); |
70 | | |
71 | 127k | const bool disable_grid_presence_check = |
72 | 127k | pj_param_exists(P->params, "disable_grid_presence_check") != nullptr; |
73 | | |
74 | | /* Don't axisswap if data are already in "enu" order */ |
75 | 127k | if (p && (0 != strcmp("enu", p->param))) { |
76 | 752 | size_t def_size = 100 + strlen(P->axis); |
77 | 752 | char *def = static_cast<char *>(malloc(def_size)); |
78 | 752 | if (nullptr == def) |
79 | 0 | return 0; |
80 | 752 | snprintf(def, def_size, |
81 | 752 | "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); |
82 | 752 | Q = pj_create_internal(P->ctx, def); |
83 | 752 | free(def); |
84 | 752 | if (nullptr == Q) |
85 | 6 | return 0; |
86 | 746 | P->axisswap = skip_prep_fin(Q); |
87 | 746 | } |
88 | | |
89 | | /* Geoid grid(s) given? */ |
90 | 127k | p = pj_param_exists(P->params, "geoidgrids"); |
91 | 127k | if (!disable_grid_presence_check && p && |
92 | 1.60k | strlen(p->param) > strlen("geoidgrids=")) { |
93 | 1.53k | char *gridnames = p->param + strlen("geoidgrids="); |
94 | 1.53k | size_t def_size = 100 + 2 * strlen(gridnames); |
95 | 1.53k | char *def = static_cast<char *>(malloc(def_size)); |
96 | 1.53k | if (nullptr == def) |
97 | 0 | return 0; |
98 | 1.53k | snprintf(def, def_size, |
99 | 1.53k | "break_cs2cs_recursion proj=vgridshift grids=%s", |
100 | 1.53k | pj_double_quote_string_param_if_needed(gridnames).c_str()); |
101 | 1.53k | Q = pj_create_internal(P->ctx, def); |
102 | 1.53k | free(def); |
103 | 1.53k | if (nullptr == Q) |
104 | 1.36k | return 0; |
105 | 169 | P->vgridshift = skip_prep_fin(Q); |
106 | 169 | } |
107 | | |
108 | | /* Datum shift grid(s) given? */ |
109 | 126k | p = pj_param_exists(P->params, "nadgrids"); |
110 | 126k | if (!disable_grid_presence_check && p && |
111 | 3.63k | strlen(p->param) > strlen("nadgrids=")) { |
112 | 3.14k | char *gridnames = p->param + strlen("nadgrids="); |
113 | 3.14k | size_t def_size = 100 + 2 * strlen(gridnames); |
114 | 3.14k | char *def = static_cast<char *>(malloc(def_size)); |
115 | 3.14k | if (nullptr == def) |
116 | 0 | return 0; |
117 | 3.14k | snprintf(def, def_size, |
118 | 3.14k | "break_cs2cs_recursion proj=hgridshift grids=%s", |
119 | 3.14k | pj_double_quote_string_param_if_needed(gridnames).c_str()); |
120 | 3.14k | Q = pj_create_internal(P->ctx, def); |
121 | 3.14k | free(def); |
122 | 3.14k | if (nullptr == Q) |
123 | 85 | return 0; |
124 | 3.06k | P->hgridshift = skip_prep_fin(Q); |
125 | 3.06k | } |
126 | | |
127 | | /* We ignore helmert if we have grid shift */ |
128 | 126k | p = P->hgridshift ? nullptr : pj_param_exists(P->params, "towgs84"); |
129 | 126k | while (p) { |
130 | 23.8k | const char *const s = p->param; |
131 | 23.8k | const double *const d = P->datum_params; |
132 | | |
133 | | /* We ignore null helmert shifts (common in auto-translated resource |
134 | | * files, e.g. epsg) */ |
135 | 23.8k | if (0 == d[0] && 0 == d[1] && 0 == d[2] && 0 == d[3] && 0 == d[4] && |
136 | 2.47k | 0 == d[5] && 0 == d[6]) { |
137 | | /* If the current ellipsoid is not WGS84, then make sure the */ |
138 | | /* change in ellipsoid is still done. */ |
139 | 2.45k | if (!(fabs(P->a_orig - 6378137.0) < 1e-8 && |
140 | 1.91k | fabs(P->es_orig - 0.0066943799901413) < 1e-15)) { |
141 | 1.80k | do_cart = 1; |
142 | 1.80k | } |
143 | 2.45k | break; |
144 | 2.45k | } |
145 | | |
146 | 21.3k | const size_t n = strlen(s); |
147 | 21.3k | if (n <= 8) /* 8==strlen ("towgs84=") */ |
148 | 0 | return 0; |
149 | | |
150 | 21.3k | const size_t def_max_size = 100 + n; |
151 | 21.3k | std::string def; |
152 | 21.3k | def.reserve(def_max_size); |
153 | 21.3k | def += "break_cs2cs_recursion proj=helmert exact "; |
154 | 21.3k | def += s; |
155 | 21.3k | def += " convention=position_vector"; |
156 | 21.3k | Q = pj_create_internal(P->ctx, def.c_str()); |
157 | 21.3k | if (nullptr == Q) |
158 | 0 | return 0; |
159 | 21.3k | pj_inherit_ellipsoid_def(P, Q); |
160 | 21.3k | P->helmert = skip_prep_fin(Q); |
161 | | |
162 | 21.3k | break; |
163 | 21.3k | } |
164 | | |
165 | | /* We also need cartesian/geographical transformations if we are working in |
166 | | */ |
167 | | /* geocentric/cartesian space or we need to do a Helmert transform. */ |
168 | 126k | if (P->is_geocent || P->helmert || do_cart) { |
169 | 23.2k | char def[150]; |
170 | 23.2k | snprintf(def, sizeof(def), |
171 | 23.2k | "break_cs2cs_recursion proj=cart a=%40.20g es=%40.20g", |
172 | 23.2k | P->a_orig, P->es_orig); |
173 | 23.2k | { |
174 | | /* In case the current locale does not use dot but comma as decimal |
175 | | */ |
176 | | /* separator, replace it with dot, so that proj_atof() behaves */ |
177 | | /* correctly. */ |
178 | | /* TODO later: use C++ ostringstream with |
179 | | * imbue(std::locale::classic()) */ |
180 | | /* to be locale unaware */ |
181 | 23.2k | char *next_pos; |
182 | 23.2k | for (next_pos = def; (next_pos = strchr(next_pos, ',')) != nullptr; |
183 | 23.2k | next_pos++) { |
184 | 0 | *next_pos = '.'; |
185 | 0 | } |
186 | 23.2k | } |
187 | 23.2k | Q = pj_create_internal(P->ctx, def); |
188 | 23.2k | if (nullptr == Q) |
189 | 0 | return 0; |
190 | 23.2k | P->cart = skip_prep_fin(Q); |
191 | | |
192 | 23.2k | if (!P->is_geocent) { |
193 | 23.1k | snprintf(def, sizeof(def), |
194 | 23.1k | "break_cs2cs_recursion proj=cart ellps=WGS84"); |
195 | 23.1k | Q = pj_create_internal(P->ctx, def); |
196 | 23.1k | if (nullptr == Q) |
197 | 0 | return 0; |
198 | 23.1k | P->cart_wgs84 = skip_prep_fin(Q); |
199 | 23.1k | } |
200 | 23.2k | } |
201 | | |
202 | 126k | return 1; |
203 | 126k | } |
204 | | |
205 | | /*************************************************************************************/ |
206 | 108k | PJ *pj_create_internal(PJ_CONTEXT *ctx, const char *definition) { |
207 | | /*************************************************************************************/ |
208 | | |
209 | | /************************************************************************************** |
210 | | Create a new PJ object in the context ctx, using the given definition. |
211 | | If ctx==0, the default context is used, if definition==0, or invalid, a |
212 | | null-pointer is returned. The definition may use '+' as argument start |
213 | | indicator, as in |
214 | | "+proj=utm +zone=32", or leave it out, as in "proj=utm zone=32". |
215 | | |
216 | | It may even use free formatting "proj = utm; zone =32 ellps= |
217 | | GRS80". Note that the semicolon separator is allowed, but not required. |
218 | | **************************************************************************************/ |
219 | 108k | char *args, **argv; |
220 | 108k | size_t argc, n; |
221 | | |
222 | 108k | if (nullptr == ctx) |
223 | 0 | ctx = pj_get_default_ctx(); |
224 | | |
225 | | /* Make a copy that we can manipulate */ |
226 | 108k | n = strlen(definition); |
227 | 108k | args = (char *)malloc(n + 1); |
228 | 108k | if (nullptr == args) { |
229 | 0 | proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/); |
230 | 0 | return nullptr; |
231 | 0 | } |
232 | 108k | strcpy(args, definition); |
233 | | |
234 | 108k | argc = pj_trim_argc(args); |
235 | 108k | if (argc == 0) { |
236 | 5 | free(args); |
237 | 5 | proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG); |
238 | 5 | return nullptr; |
239 | 5 | } |
240 | | |
241 | 108k | argv = pj_trim_argv(argc, args); |
242 | 108k | if (!argv) { |
243 | 0 | free(args); |
244 | 0 | proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/); |
245 | 0 | return nullptr; |
246 | 0 | } |
247 | | |
248 | 108k | PJ *P = pj_create_argv_internal(ctx, (int)argc, argv); |
249 | | |
250 | 108k | free(argv); |
251 | 108k | free(args); |
252 | | |
253 | 108k | return P; |
254 | 108k | } |
255 | | |
256 | | /*************************************************************************************/ |
257 | 0 | PJ *proj_create_argv(PJ_CONTEXT *ctx, int argc, char **argv) { |
258 | | /************************************************************************************** |
259 | | Create a new PJ object in the context ctx, using the given definition |
260 | | argument array argv. If ctx==0, the default context is used, if |
261 | | definition==0, or invalid, a null-pointer is returned. The definition |
262 | | arguments may use '+' as argument start indicator, as in {"+proj=utm", |
263 | | "+zone=32"}, or leave it out, as in {"proj=utm", "zone=32"}. |
264 | | **************************************************************************************/ |
265 | |
|
266 | 0 | if (nullptr == ctx) |
267 | 0 | ctx = pj_get_default_ctx(); |
268 | 0 | if (nullptr == argv) { |
269 | 0 | proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG); |
270 | 0 | return nullptr; |
271 | 0 | } |
272 | | |
273 | | /* We assume that free format is used, and build a full proj_create |
274 | | * compatible string */ |
275 | 0 | char *c = pj_make_args(argc, argv); |
276 | 0 | if (nullptr == c) { |
277 | 0 | proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP /* ENOMEM */); |
278 | 0 | return nullptr; |
279 | 0 | } |
280 | | |
281 | 0 | PJ *P = proj_create(ctx, c); |
282 | |
|
283 | 0 | free((char *)c); |
284 | 0 | return P; |
285 | 0 | } |
286 | | |
287 | | /*************************************************************************************/ |
288 | 228k | PJ *pj_create_argv_internal(PJ_CONTEXT *ctx, int argc, char **argv) { |
289 | | /************************************************************************************** |
290 | | For use by pipeline init function. |
291 | | **************************************************************************************/ |
292 | 228k | if (nullptr == ctx) |
293 | 0 | ctx = pj_get_default_ctx(); |
294 | 228k | if (nullptr == argv) { |
295 | 0 | proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG); |
296 | 0 | return nullptr; |
297 | 0 | } |
298 | | |
299 | | /* ...and let pj_init_ctx do the hard work */ |
300 | | /* New interface: forbid init=epsg:XXXX syntax by default */ |
301 | 228k | const int allow_init_epsg = |
302 | 228k | proj_context_get_use_proj4_init_rules(ctx, FALSE); |
303 | 228k | PJ *P = pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg); |
304 | | |
305 | | /* Support cs2cs-style modifiers */ |
306 | 228k | int ret = cs2cs_emulation_setup(P); |
307 | 228k | if (0 == ret) |
308 | 30.5k | return proj_destroy(P); |
309 | | |
310 | 198k | return P; |
311 | 228k | } |