/src/PROJ/src/conversions/unitconvert.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /*********************************************************************** |
2 | | |
3 | | Unit conversion pseudo-projection for use with |
4 | | transformation pipelines. |
5 | | |
6 | | Kristian Evers, 2017-05-16 |
7 | | |
8 | | ************************************************************************ |
9 | | |
10 | | A pseudo-projection that can be used to convert units of input and |
11 | | output data. Primarily useful in pipelines. |
12 | | |
13 | | Unit conversion is performed by means of a pivot unit. The pivot unit |
14 | | for distance units are the meter and for time we use the modified julian |
15 | | date. A time unit conversion is performed like |
16 | | |
17 | | Unit A -> Modified Julian date -> Unit B |
18 | | |
19 | | distance units are converted in the same manner, with meter being the |
20 | | central unit. |
21 | | |
22 | | The modified Julian date is chosen as the pivot unit since it has a |
23 | | fairly high precision, goes sufficiently long backwards in time, has no |
24 | | danger of hitting the upper limit in the near future and it is a fairly |
25 | | common time unit in astronomy and geodesy. Note that we are using the |
26 | | Julian date and not day. The difference being that the latter is defined |
27 | | as an integer and is thus limited to days in resolution. This approach |
28 | | has been extended wherever it makes sense, e.g. the GPS week unit also |
29 | | has a fractional part that makes it possible to determine the day, hour |
30 | | and minute of an observation. |
31 | | |
32 | | In- and output units are controlled with the parameters |
33 | | |
34 | | +xy_in, +xy_out, +z_in, +z_out, +t_in and +t_out |
35 | | |
36 | | where xy denotes horizontal units, z vertical units and t time units. |
37 | | |
38 | | ************************************************************************ |
39 | | |
40 | | Kristian Evers, kreve@sdfe.dk, 2017-05-09 |
41 | | Last update: 2017-05-16 |
42 | | |
43 | | ************************************************************************ |
44 | | * Copyright (c) 2017, Kristian Evers / SDFE |
45 | | * |
46 | | * Permission is hereby granted, free of charge, to any person obtaining a |
47 | | * copy of this software and associated documentation files (the "Software"), |
48 | | * to deal in the Software without restriction, including without limitation |
49 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
50 | | * and/or sell copies of the Software, and to permit persons to whom the |
51 | | * Software is furnished to do so, subject to the following conditions: |
52 | | * |
53 | | * The above copyright notice and this permission notice shall be included |
54 | | * in all copies or substantial portions of the Software. |
55 | | * |
56 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
57 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
58 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
59 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
60 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
61 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
62 | | * DEALINGS IN THE SOFTWARE. |
63 | | * |
64 | | ***********************************************************************/ |
65 | | |
66 | | #include <errno.h> |
67 | | #include <math.h> |
68 | | #include <string.h> |
69 | | #include <time.h> |
70 | | |
71 | | #include "proj_internal.h" |
72 | | #include <math.h> |
73 | | |
74 | | PROJ_HEAD(unitconvert, "Unit conversion"); |
75 | | |
76 | | typedef double (*tconvert)(double); |
77 | | |
78 | | namespace { // anonymous namespace |
79 | | struct TIME_UNITS { |
80 | | const char *id; /* units keyword */ |
81 | | tconvert t_in; /* unit -> mod. julian date function pointer */ |
82 | | tconvert t_out; /* mod. julian date > unit function pointer */ |
83 | | const char *name; /* comments */ |
84 | | }; |
85 | | } // anonymous namespace |
86 | | |
87 | | namespace { // anonymous namespace |
88 | | struct pj_opaque_unitconvert { |
89 | | int t_in_id; /* time unit id for the time input unit */ |
90 | | int t_out_id; /* time unit id for the time output unit */ |
91 | | double xy_factor; /* unit conversion factor for horizontal components */ |
92 | | double z_factor; /* unit conversion factor for vertical components */ |
93 | | }; |
94 | | } // anonymous namespace |
95 | | |
96 | | /***********************************************************************/ |
97 | 0 | static int is_leap_year(long year) { |
98 | | /***********************************************************************/ |
99 | 0 | return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); |
100 | 0 | } |
101 | | |
102 | | /***********************************************************************/ |
103 | 0 | static int days_in_year(long year) { |
104 | | /***********************************************************************/ |
105 | 0 | return is_leap_year(year) ? 366 : 365; |
106 | 0 | } |
107 | | |
108 | | /***********************************************************************/ |
109 | 0 | static unsigned int days_in_month(unsigned long year, unsigned long month) { |
110 | | /***********************************************************************/ |
111 | 0 | const unsigned int month_table[] = {31, 28, 31, 30, 31, 30, |
112 | 0 | 31, 31, 30, 31, 30, 31}; |
113 | 0 | unsigned int days; |
114 | |
|
115 | 0 | if (month > 12) |
116 | 0 | month = 12; |
117 | 0 | if (month == 0) |
118 | 0 | month = 1; |
119 | |
|
120 | 0 | days = month_table[month - 1]; |
121 | 0 | if (is_leap_year(year) && month == 2) |
122 | 0 | days++; |
123 | |
|
124 | 0 | return days; |
125 | 0 | } |
126 | | |
127 | | /***********************************************************************/ |
128 | | static int daynumber_in_year(unsigned long year, unsigned long month, |
129 | 0 | unsigned long day) { |
130 | | /***********************************************************************/ |
131 | 0 | unsigned int daynumber = 0, i; |
132 | |
|
133 | 0 | if (month > 12) |
134 | 0 | month = 12; |
135 | 0 | if (month == 0) |
136 | 0 | month = 1; |
137 | 0 | if (day > days_in_month(year, month)) |
138 | 0 | day = days_in_month(year, month); |
139 | |
|
140 | 0 | for (i = 1; i < month; i++) |
141 | 0 | daynumber += days_in_month(year, i); |
142 | |
|
143 | 0 | daynumber += day; |
144 | |
|
145 | 0 | return daynumber; |
146 | 0 | } |
147 | | |
148 | | /***********************************************************************/ |
149 | 0 | static double mjd_to_mjd(double mjd) { |
150 | | /*********************************************************************** |
151 | | Modified julian date no-op function. |
152 | | |
153 | | The Julian date is defined as (fractional) days since midnight |
154 | | on 16th of November in 1858. |
155 | | ************************************************************************/ |
156 | 0 | return mjd; |
157 | 0 | } |
158 | | |
159 | | /***********************************************************************/ |
160 | 0 | static double decimalyear_to_mjd(double decimalyear) { |
161 | | /*********************************************************************** |
162 | | Epoch of modified julian date is 1858-11-16 00:00 |
163 | | ************************************************************************/ |
164 | 0 | long year; |
165 | 0 | double fractional_year; |
166 | 0 | double mjd; |
167 | | |
168 | | // Written this way to deal with NaN input |
169 | 0 | if (!(decimalyear >= -10000 && decimalyear <= 10000)) |
170 | 0 | return 0; |
171 | | |
172 | 0 | year = lround(floor(decimalyear)); |
173 | 0 | fractional_year = decimalyear - year; |
174 | 0 | mjd = (year - 1859) * 365 + 14 + 31; |
175 | 0 | mjd += (double)fractional_year * (double)days_in_year(year); |
176 | | |
177 | | /* take care of leap days */ |
178 | 0 | year--; |
179 | 0 | for (; year > 1858; year--) |
180 | 0 | if (is_leap_year(year)) |
181 | 0 | mjd++; |
182 | |
|
183 | 0 | return mjd; |
184 | 0 | } |
185 | | |
186 | | /***********************************************************************/ |
187 | 0 | static double mjd_to_decimalyear(double mjd) { |
188 | | /*********************************************************************** |
189 | | Epoch of modified julian date is 1858-11-16 00:00 |
190 | | ************************************************************************/ |
191 | 0 | double decimalyear = mjd; |
192 | 0 | double mjd_iter = 14 + 31; |
193 | 0 | int year = 1859; |
194 | | |
195 | | /* a smarter brain than mine could probably to do this more elegantly |
196 | | - I'll just brute-force my way out of this... */ |
197 | 0 | for (; mjd >= mjd_iter; year++) { |
198 | 0 | mjd_iter += days_in_year(year); |
199 | 0 | } |
200 | 0 | year--; |
201 | 0 | mjd_iter -= days_in_year(year); |
202 | |
|
203 | 0 | decimalyear = year + (mjd - mjd_iter) / days_in_year(year); |
204 | 0 | return decimalyear; |
205 | 0 | } |
206 | | |
207 | | /***********************************************************************/ |
208 | 0 | static double gps_week_to_mjd(double gps_week) { |
209 | | /*********************************************************************** |
210 | | GPS weeks are defined as the number of weeks since January the 6th |
211 | | 1980. |
212 | | |
213 | | Epoch of gps weeks is 1980-01-06 00:00, which in modified Julian |
214 | | date is 44244. |
215 | | ************************************************************************/ |
216 | 0 | return 44244.0 + gps_week * 7.0; |
217 | 0 | } |
218 | | |
219 | | /***********************************************************************/ |
220 | 0 | static double mjd_to_gps_week(double mjd) { |
221 | | /*********************************************************************** |
222 | | GPS weeks are defined as the number of weeks since January the 6th |
223 | | 1980. |
224 | | |
225 | | Epoch of gps weeks is 1980-01-06 00:00, which in modified Julian |
226 | | date is 44244. |
227 | | ************************************************************************/ |
228 | 0 | return (mjd - 44244.0) / 7.0; |
229 | 0 | } |
230 | | |
231 | | /***********************************************************************/ |
232 | 0 | static double yyyymmdd_to_mjd(double yyyymmdd) { |
233 | | /************************************************************************ |
234 | | Date given in YYYY-MM-DD format. |
235 | | ************************************************************************/ |
236 | |
|
237 | 0 | long year = lround(floor(yyyymmdd / 10000)); |
238 | 0 | long month = lround(floor((yyyymmdd - year * 10000) / 100)); |
239 | 0 | long day = lround(floor(yyyymmdd - year * 10000 - month * 100)); |
240 | 0 | double mjd = daynumber_in_year(year, month, day); |
241 | |
|
242 | 0 | for (year -= 1; year > 1858; year--) |
243 | 0 | mjd += days_in_year(year); |
244 | |
|
245 | 0 | return mjd + 13 + 31; |
246 | 0 | } |
247 | | |
248 | | /***********************************************************************/ |
249 | 0 | static double mjd_to_yyyymmdd(double mjd) { |
250 | | /************************************************************************ |
251 | | Date returned in YYYY-MM-DD format. |
252 | | ************************************************************************/ |
253 | 0 | unsigned int date_iter = 14 + 31; |
254 | 0 | unsigned int year = 1859, month = 0, day = 0; |
255 | 0 | unsigned int date = (int)lround(mjd); |
256 | |
|
257 | 0 | for (; date >= date_iter; year++) { |
258 | 0 | date_iter += days_in_year(year); |
259 | 0 | } |
260 | 0 | year--; |
261 | 0 | date_iter -= days_in_year(year); |
262 | |
|
263 | 0 | for (month = 1; date_iter + days_in_month(year, month) <= date; month++) |
264 | 0 | date_iter += days_in_month(year, month); |
265 | |
|
266 | 0 | day = date - date_iter + 1; |
267 | |
|
268 | 0 | return year * 10000.0 + month * 100.0 + day; |
269 | 0 | } |
270 | | |
271 | | static const struct TIME_UNITS time_units[] = { |
272 | | {"mjd", mjd_to_mjd, mjd_to_mjd, "Modified julian date"}, |
273 | | {"decimalyear", decimalyear_to_mjd, mjd_to_decimalyear, "Decimal year"}, |
274 | | {"gps_week", gps_week_to_mjd, mjd_to_gps_week, "GPS Week"}, |
275 | | {"yyyymmdd", yyyymmdd_to_mjd, mjd_to_yyyymmdd, "YYYYMMDD date"}, |
276 | | {nullptr, nullptr, nullptr, nullptr}}; |
277 | | |
278 | | /***********************************************************************/ |
279 | 1.76M | static PJ_XY forward_2d(PJ_LP lp, PJ *P) { |
280 | | /************************************************************************ |
281 | | Forward unit conversions in the plane |
282 | | ************************************************************************/ |
283 | 1.76M | struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *)P->opaque; |
284 | 1.76M | PJ_COORD point = {{0, 0, 0, 0}}; |
285 | 1.76M | point.lp = lp; |
286 | | |
287 | 1.76M | point.xy.x *= Q->xy_factor; |
288 | 1.76M | point.xy.y *= Q->xy_factor; |
289 | | |
290 | 1.76M | return point.xy; |
291 | 1.76M | } |
292 | | |
293 | | /***********************************************************************/ |
294 | 336 | static PJ_LP reverse_2d(PJ_XY xy, PJ *P) { |
295 | | /************************************************************************ |
296 | | Reverse unit conversions in the plane |
297 | | ************************************************************************/ |
298 | 336 | struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *)P->opaque; |
299 | 336 | PJ_COORD point = {{0, 0, 0, 0}}; |
300 | 336 | point.xy = xy; |
301 | | |
302 | 336 | point.xy.x /= Q->xy_factor; |
303 | 336 | point.xy.y /= Q->xy_factor; |
304 | | |
305 | 336 | return point.lp; |
306 | 336 | } |
307 | | |
308 | | /***********************************************************************/ |
309 | 1.76M | static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { |
310 | | /************************************************************************ |
311 | | Forward unit conversions the vertical component |
312 | | ************************************************************************/ |
313 | 1.76M | struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *)P->opaque; |
314 | 1.76M | PJ_COORD point = {{0, 0, 0, 0}}; |
315 | 1.76M | point.lpz = lpz; |
316 | | |
317 | | /* take care of the horizontal components in the 2D function */ |
318 | | |
319 | | // Assigning in 2 steps avoids cppcheck warning |
320 | | // "Overlapping read/write of union is undefined behavior" |
321 | | // Cf https://github.com/OSGeo/PROJ/pull/3527#pullrequestreview-1233332710 |
322 | 1.76M | const auto xy = forward_2d(point.lp, P); |
323 | 1.76M | point.xy = xy; |
324 | | |
325 | 1.76M | point.xyz.z *= Q->z_factor; |
326 | | |
327 | 1.76M | return point.xyz; |
328 | 1.76M | } |
329 | | |
330 | | /***********************************************************************/ |
331 | 336 | static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) { |
332 | | /************************************************************************ |
333 | | Reverse unit conversions the vertical component |
334 | | ************************************************************************/ |
335 | 336 | struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *)P->opaque; |
336 | 336 | PJ_COORD point = {{0, 0, 0, 0}}; |
337 | 336 | point.xyz = xyz; |
338 | | |
339 | | /* take care of the horizontal components in the 2D function */ |
340 | | |
341 | | // Assigning in 2 steps avoids cppcheck warning |
342 | | // "Overlapping read/write of union is undefined behavior" |
343 | | // Cf https://github.com/OSGeo/PROJ/pull/3527#pullrequestreview-1233332710 |
344 | 336 | const auto lp = reverse_2d(point.xy, P); |
345 | 336 | point.lp = lp; |
346 | | |
347 | 336 | point.xyz.z /= Q->z_factor; |
348 | | |
349 | 336 | return point.lpz; |
350 | 336 | } |
351 | | |
352 | | /***********************************************************************/ |
353 | 1.76M | static void forward_4d(PJ_COORD &coo, PJ *P) { |
354 | | /************************************************************************ |
355 | | Forward conversion of time units |
356 | | ************************************************************************/ |
357 | 1.76M | struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *)P->opaque; |
358 | | |
359 | | /* delegate unit conversion of physical dimensions to the 3D function */ |
360 | | |
361 | | // Assigning in 2 steps avoids cppcheck warning |
362 | | // "Overlapping read/write of union is undefined behavior" |
363 | | // Cf https://github.com/OSGeo/PROJ/pull/3527#pullrequestreview-1233332710 |
364 | 1.76M | const auto xyz = forward_3d(coo.lpz, P); |
365 | 1.76M | coo.xyz = xyz; |
366 | | |
367 | 1.76M | if (Q->t_in_id >= 0) |
368 | 0 | coo.xyzt.t = time_units[Q->t_in_id].t_in(coo.xyzt.t); |
369 | 1.76M | if (Q->t_out_id >= 0) |
370 | 0 | coo.xyzt.t = time_units[Q->t_out_id].t_out(coo.xyzt.t); |
371 | 1.76M | } |
372 | | |
373 | | /***********************************************************************/ |
374 | 336 | static void reverse_4d(PJ_COORD &coo, PJ *P) { |
375 | | /************************************************************************ |
376 | | Reverse conversion of time units |
377 | | ************************************************************************/ |
378 | 336 | struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *)P->opaque; |
379 | | |
380 | | /* delegate unit conversion of physical dimensions to the 3D function */ |
381 | | |
382 | | // Assigning in 2 steps avoids cppcheck warning |
383 | | // "Overlapping read/write of union is undefined behavior" |
384 | | // Cf https://github.com/OSGeo/PROJ/pull/3527#pullrequestreview-1233332710 |
385 | 336 | const auto lpz = reverse_3d(coo.xyz, P); |
386 | 336 | coo.lpz = lpz; |
387 | | |
388 | 336 | if (Q->t_out_id >= 0) |
389 | 0 | coo.xyzt.t = time_units[Q->t_out_id].t_in(coo.xyzt.t); |
390 | 336 | if (Q->t_in_id >= 0) |
391 | 0 | coo.xyzt.t = time_units[Q->t_in_id].t_out(coo.xyzt.t); |
392 | 336 | } |
393 | | |
394 | | /***********************************************************************/ |
395 | | static double get_unit_conversion_factor(const char *name, int *p_is_linear, |
396 | 35.5k | const char **p_normalized_name) { |
397 | | /***********************************************************************/ |
398 | 35.5k | int i; |
399 | 35.5k | const char *s; |
400 | 35.5k | const PJ_UNITS *units = pj_list_linear_units(); |
401 | | |
402 | | /* Try first with linear units */ |
403 | 708k | for (i = 0; (s = units[i].id); ++i) { |
404 | 676k | if (strcmp(s, name) == 0) { |
405 | 3.90k | if (p_normalized_name) { |
406 | 3.90k | *p_normalized_name = units[i].name; |
407 | 3.90k | } |
408 | 3.90k | if (p_is_linear) { |
409 | 3.90k | *p_is_linear = 1; |
410 | 3.90k | } |
411 | 3.90k | return units[i].factor; |
412 | 3.90k | } |
413 | 676k | } |
414 | | |
415 | | /* And then angular units */ |
416 | 31.6k | units = pj_list_angular_units(); |
417 | 48.6k | for (i = 0; (s = units[i].id); ++i) { |
418 | 48.1k | if (strcmp(s, name) == 0) { |
419 | 31.1k | if (p_normalized_name) { |
420 | 31.1k | *p_normalized_name = units[i].name; |
421 | 31.1k | } |
422 | 31.1k | if (p_is_linear) { |
423 | 31.1k | *p_is_linear = 0; |
424 | 31.1k | } |
425 | 31.1k | return units[i].factor; |
426 | 31.1k | } |
427 | 48.1k | } |
428 | 490 | if (p_normalized_name) { |
429 | 490 | *p_normalized_name = nullptr; |
430 | 490 | } |
431 | 490 | if (p_is_linear) { |
432 | 490 | *p_is_linear = -1; |
433 | 490 | } |
434 | 490 | return 0.0; |
435 | 31.6k | } |
436 | | |
437 | | /***********************************************************************/ |
438 | 16.9k | PJ *PJ_CONVERSION(unitconvert, 0) { |
439 | | /***********************************************************************/ |
440 | 16.9k | struct pj_opaque_unitconvert *Q = |
441 | 16.9k | static_cast<struct pj_opaque_unitconvert *>( |
442 | 16.9k | calloc(1, sizeof(struct pj_opaque_unitconvert))); |
443 | 16.9k | const char *s, *name; |
444 | 16.9k | int i; |
445 | 16.9k | double f; |
446 | 16.9k | int xy_in_is_linear = -1; /* unknown */ |
447 | 16.9k | int xy_out_is_linear = -1; /* unknown */ |
448 | 16.9k | int z_in_is_linear = -1; /* unknown */ |
449 | 16.9k | int z_out_is_linear = -1; /* unknown */ |
450 | | |
451 | 16.9k | if (nullptr == Q) |
452 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
453 | 16.9k | P->opaque = (void *)Q; |
454 | | |
455 | 16.9k | P->fwd4d = forward_4d; |
456 | 16.9k | P->inv4d = reverse_4d; |
457 | 16.9k | P->fwd3d = forward_3d; |
458 | 16.9k | P->inv3d = reverse_3d; |
459 | 16.9k | P->fwd = forward_2d; |
460 | 16.9k | P->inv = reverse_2d; |
461 | | |
462 | 16.9k | P->left = PJ_IO_UNITS_WHATEVER; |
463 | 16.9k | P->right = PJ_IO_UNITS_WHATEVER; |
464 | 16.9k | P->skip_fwd_prepare = 1; |
465 | 16.9k | P->skip_inv_prepare = 1; |
466 | | |
467 | | /* if no time input/output unit is specified we can skip them */ |
468 | 16.9k | Q->t_in_id = -1; |
469 | 16.9k | Q->t_out_id = -1; |
470 | | |
471 | 16.9k | Q->xy_factor = 1.0; |
472 | 16.9k | Q->z_factor = 1.0; |
473 | | |
474 | 16.9k | if ((name = pj_param(P->ctx, P->params, "sxy_in").s) != nullptr) { |
475 | 16.3k | const char *normalized_name = nullptr; |
476 | 16.3k | f = get_unit_conversion_factor(name, &xy_in_is_linear, |
477 | 16.3k | &normalized_name); |
478 | 16.3k | if (f != 0.0) { |
479 | 16.2k | proj_log_trace(P, "xy_in unit: %s", normalized_name); |
480 | 16.2k | } else { |
481 | 143 | f = pj_param(P->ctx, P->params, "dxy_in").f; |
482 | 143 | if (f == 0.0 || 1.0 / f == 0.0) { |
483 | 46 | proj_log_error(P, _("unknown xy_in unit")); |
484 | 46 | return pj_default_destructor( |
485 | 46 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
486 | 46 | } |
487 | 143 | } |
488 | 16.3k | Q->xy_factor = f; |
489 | 16.3k | if (normalized_name != nullptr) { |
490 | 16.2k | if (strcmp(normalized_name, "Radian") == 0) |
491 | 8.45k | P->left = PJ_IO_UNITS_RADIANS; |
492 | 16.2k | if (strcmp(normalized_name, "Degree") == 0) |
493 | 7.18k | P->left = PJ_IO_UNITS_DEGREES; |
494 | 16.2k | } |
495 | 16.3k | } |
496 | | |
497 | 16.8k | if ((name = pj_param(P->ctx, P->params, "sxy_out").s) != nullptr) { |
498 | 16.2k | const char *normalized_name = nullptr; |
499 | 16.2k | f = get_unit_conversion_factor(name, &xy_out_is_linear, |
500 | 16.2k | &normalized_name); |
501 | 16.2k | if (f != 0.0) { |
502 | 16.1k | proj_log_trace(P, "xy_out unit: %s", normalized_name); |
503 | 16.1k | } else { |
504 | 138 | f = pj_param(P->ctx, P->params, "dxy_out").f; |
505 | 138 | if (f == 0.0 || 1.0 / f == 0.0) { |
506 | 124 | proj_log_error(P, _("unknown xy_out unit")); |
507 | 124 | return pj_default_destructor( |
508 | 124 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
509 | 124 | } |
510 | 138 | } |
511 | 16.1k | Q->xy_factor /= f; |
512 | 16.1k | if (normalized_name != nullptr) { |
513 | 16.1k | if (strcmp(normalized_name, "Radian") == 0) |
514 | 7.20k | P->right = PJ_IO_UNITS_RADIANS; |
515 | 16.1k | if (strcmp(normalized_name, "Degree") == 0) |
516 | 8.33k | P->right = PJ_IO_UNITS_DEGREES; |
517 | 16.1k | } |
518 | 16.1k | } |
519 | | |
520 | 16.7k | if (xy_in_is_linear >= 0 && xy_out_is_linear >= 0 && |
521 | 16.7k | xy_in_is_linear != xy_out_is_linear) { |
522 | 2 | proj_log_error(P, _("inconsistent unit type between xy_in and xy_out")); |
523 | 2 | return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
524 | 2 | } |
525 | | |
526 | 16.7k | if ((name = pj_param(P->ctx, P->params, "sz_in").s) != nullptr) { |
527 | 1.48k | const char *normalized_name = nullptr; |
528 | 1.48k | f = get_unit_conversion_factor(name, &z_in_is_linear, &normalized_name); |
529 | 1.48k | if (f != 0.0) { |
530 | 1.37k | proj_log_trace(P, "z_in unit: %s", normalized_name); |
531 | 1.37k | } else { |
532 | 105 | f = pj_param(P->ctx, P->params, "dz_in").f; |
533 | 105 | if (f == 0.0 || 1.0 / f == 0.0) { |
534 | 12 | proj_log_error(P, _("unknown z_in unit")); |
535 | 12 | return pj_default_destructor( |
536 | 12 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
537 | 12 | } |
538 | 105 | } |
539 | 1.46k | Q->z_factor = f; |
540 | 1.46k | } |
541 | | |
542 | 16.7k | if ((name = pj_param(P->ctx, P->params, "sz_out").s) != nullptr) { |
543 | 1.47k | const char *normalized_name = nullptr; |
544 | 1.47k | f = get_unit_conversion_factor(name, &z_out_is_linear, |
545 | 1.47k | &normalized_name); |
546 | 1.47k | if (f != 0.0) { |
547 | 1.37k | proj_log_trace(P, "z_out unit: %s", normalized_name); |
548 | 1.37k | } else { |
549 | 104 | f = pj_param(P->ctx, P->params, "dz_out").f; |
550 | 104 | if (f == 0.0 || 1.0 / f == 0.0) { |
551 | 59 | proj_log_error(P, _("unknown z_out unit")); |
552 | 59 | return pj_default_destructor( |
553 | 59 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
554 | 59 | } |
555 | 104 | } |
556 | 1.41k | Q->z_factor /= f; |
557 | 1.41k | } |
558 | | |
559 | 16.6k | if (z_in_is_linear >= 0 && z_out_is_linear >= 0 && |
560 | 16.6k | z_in_is_linear != z_out_is_linear) { |
561 | 0 | proj_log_error(P, _("inconsistent unit type between z_in and z_out")); |
562 | 0 | return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
563 | 0 | } |
564 | | |
565 | 16.6k | if ((name = pj_param(P->ctx, P->params, "st_in").s) != nullptr) { |
566 | 30 | for (i = 0; (s = time_units[i].id) && strcmp(name, s); ++i) |
567 | 23 | ; |
568 | | |
569 | 7 | if (!s) { |
570 | 2 | proj_log_error(P, _("unknown t_in unit")); |
571 | 2 | return pj_default_destructor(P, |
572 | 2 | PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
573 | 2 | } |
574 | | |
575 | 5 | Q->t_in_id = i; |
576 | 5 | proj_log_trace(P, "t_in unit: %s", time_units[i].name); |
577 | 5 | } |
578 | | |
579 | 16.6k | s = nullptr; |
580 | 16.6k | if ((name = pj_param(P->ctx, P->params, "st_out").s) != nullptr) { |
581 | 89 | for (i = 0; (s = time_units[i].id) && strcmp(name, s); ++i) |
582 | 62 | ; |
583 | | |
584 | 27 | if (!s) { |
585 | 2 | proj_log_error(P, _("unknown t_out unit")); |
586 | 2 | return pj_default_destructor(P, |
587 | 2 | PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
588 | 2 | } |
589 | | |
590 | 25 | Q->t_out_id = i; |
591 | 25 | proj_log_trace(P, "t_out unit: %s", time_units[i].name); |
592 | 25 | } |
593 | | |
594 | 16.6k | return P; |
595 | 16.6k | } |