/src/gdal/gcore/gdal_misc.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL Core |
4 | | * Purpose: Free standing functions for GDAL. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Frank Warmerdam |
9 | | * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | |
16 | | #include <cctype> |
17 | | #include <cerrno> |
18 | | #include <clocale> |
19 | | #include <cmath> |
20 | | #include <cstddef> |
21 | | #include <cstdio> |
22 | | #include <cstdlib> |
23 | | #include <cstring> |
24 | | #include <fcntl.h> |
25 | | |
26 | | #include <algorithm> |
27 | | #include <iostream> |
28 | | #include <limits> |
29 | | #include <string> |
30 | | |
31 | | #include "cpl_conv.h" |
32 | | #include "cpl_error.h" |
33 | | #include "cpl_float.h" |
34 | | #include "cpl_json.h" |
35 | | #include "cpl_minixml.h" |
36 | | #include "cpl_multiproc.h" |
37 | | #include "cpl_string.h" |
38 | | #include "cpl_vsi.h" |
39 | | #ifdef EMBED_RESOURCE_FILES |
40 | | #include "embedded_resources.h" |
41 | | #endif |
42 | | #include "gdal_version_full/gdal_version.h" |
43 | | #include "gdal.h" |
44 | | #include "gdal_mdreader.h" |
45 | | #include "gdal_priv.h" |
46 | | #include "gdal_priv_templates.hpp" |
47 | | #include "ogr_core.h" |
48 | | #include "ogr_spatialref.h" |
49 | | #include "ogr_geos.h" |
50 | | |
51 | | #include "proj.h" |
52 | | |
53 | | #ifdef HAVE_CURL |
54 | | #include "cpl_curl_priv.h" |
55 | | #endif |
56 | | |
57 | | static int GetMinBitsForPair(const bool pabSigned[], const bool pabFloating[], |
58 | | const int panBits[]) |
59 | 0 | { |
60 | 0 | if (pabFloating[0] != pabFloating[1]) |
61 | 0 | { |
62 | 0 | const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0; |
63 | 0 | const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1; |
64 | |
|
65 | 0 | return std::max(panBits[nFloatingTypeIndex], |
66 | 0 | 2 * panBits[nNotFloatingTypeIndex]); |
67 | 0 | } |
68 | | |
69 | 0 | if (pabSigned[0] != pabSigned[1]) |
70 | 0 | { |
71 | 0 | if (!pabSigned[0] && panBits[0] < panBits[1]) |
72 | 0 | return panBits[1]; |
73 | 0 | if (!pabSigned[1] && panBits[1] < panBits[0]) |
74 | 0 | return panBits[0]; |
75 | | |
76 | 0 | const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0; |
77 | 0 | const int nSignedTypeIndex = pabSigned[0] ? 0 : 1; |
78 | |
|
79 | 0 | return std::max(panBits[nSignedTypeIndex], |
80 | 0 | 2 * panBits[nUnsignedTypeIndex]); |
81 | 0 | } |
82 | | |
83 | 0 | return std::max(panBits[0], panBits[1]); |
84 | 0 | } |
85 | | |
86 | | static int GetNonComplexDataTypeElementSizeBits(GDALDataType eDataType) |
87 | 0 | { |
88 | 0 | switch (eDataType) |
89 | 0 | { |
90 | 0 | case GDT_Byte: |
91 | 0 | case GDT_Int8: |
92 | 0 | return 8; |
93 | | |
94 | 0 | case GDT_UInt16: |
95 | 0 | case GDT_Int16: |
96 | 0 | case GDT_Float16: |
97 | 0 | case GDT_CInt16: |
98 | 0 | case GDT_CFloat16: |
99 | 0 | return 16; |
100 | | |
101 | 0 | case GDT_UInt32: |
102 | 0 | case GDT_Int32: |
103 | 0 | case GDT_Float32: |
104 | 0 | case GDT_CInt32: |
105 | 0 | case GDT_CFloat32: |
106 | 0 | return 32; |
107 | | |
108 | 0 | case GDT_Float64: |
109 | 0 | case GDT_CFloat64: |
110 | 0 | case GDT_UInt64: |
111 | 0 | case GDT_Int64: |
112 | 0 | return 64; |
113 | | |
114 | 0 | case GDT_Unknown: |
115 | 0 | case GDT_TypeCount: |
116 | 0 | break; |
117 | 0 | } |
118 | 0 | return 0; |
119 | 0 | } |
120 | | |
121 | | /************************************************************************/ |
122 | | /* GDALDataTypeUnion() */ |
123 | | /************************************************************************/ |
124 | | |
125 | | /** |
126 | | * \brief Return the smallest data type that can fully express both input data |
127 | | * types. |
128 | | * |
129 | | * @param eType1 first data type. |
130 | | * @param eType2 second data type. |
131 | | * |
132 | | * @return a data type able to express eType1 and eType2. |
133 | | */ |
134 | | |
135 | | GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1, |
136 | | GDALDataType eType2) |
137 | | |
138 | 0 | { |
139 | 0 | if (eType1 == GDT_Unknown) |
140 | 0 | return eType2; |
141 | 0 | if (eType2 == GDT_Unknown) |
142 | 0 | return eType1; |
143 | | |
144 | 0 | const int panBits[] = {GetNonComplexDataTypeElementSizeBits(eType1), |
145 | 0 | GetNonComplexDataTypeElementSizeBits(eType2)}; |
146 | |
|
147 | 0 | if (panBits[0] == 0 || panBits[1] == 0) |
148 | 0 | return GDT_Unknown; |
149 | | |
150 | 0 | const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)), |
151 | 0 | CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))}; |
152 | |
|
153 | 0 | const bool bSigned = pabSigned[0] || pabSigned[1]; |
154 | 0 | const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)), |
155 | 0 | CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))}; |
156 | 0 | const bool bFloating = pabFloating[0] || pabFloating[1]; |
157 | 0 | const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits); |
158 | 0 | const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) || |
159 | 0 | CPL_TO_BOOL(GDALDataTypeIsComplex(eType2)); |
160 | |
|
161 | 0 | return GDALFindDataType(nBits, bSigned, bFloating, bIsComplex); |
162 | 0 | } |
163 | | |
164 | | /************************************************************************/ |
165 | | /* GDALDataTypeUnionWithValue() */ |
166 | | /************************************************************************/ |
167 | | |
168 | | /** |
169 | | * \brief Union a data type with the one found for a value |
170 | | * |
171 | | * @param eDT the first data type |
172 | | * @param dfValue the value for which to find a data type and union with eDT |
173 | | * @param bComplex if the value is complex |
174 | | * |
175 | | * @return a data type able to express eDT and dfValue. |
176 | | * @since GDAL 2.3 |
177 | | */ |
178 | | GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT, |
179 | | double dfValue, |
180 | | int bComplex) |
181 | 0 | { |
182 | 0 | if (!bComplex && !GDALDataTypeIsComplex(eDT) && eDT != GDT_Unknown) |
183 | 0 | { |
184 | | // Do not return `GDT_Float16` because that type is not supported everywhere |
185 | 0 | const auto eDTMod = eDT == GDT_Float16 ? GDT_Float32 : eDT; |
186 | 0 | if (GDALIsValueExactAs(dfValue, eDTMod)) |
187 | 0 | { |
188 | 0 | return eDTMod; |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | 0 | const GDALDataType eDT2 = GDALFindDataTypeForValue(dfValue, bComplex); |
193 | 0 | return GDALDataTypeUnion(eDT, eDT2); |
194 | 0 | } |
195 | | |
196 | | /************************************************************************/ |
197 | | /* GetMinBitsForValue() */ |
198 | | /************************************************************************/ |
199 | | static int GetMinBitsForValue(double dValue) |
200 | 0 | { |
201 | 0 | if (round(dValue) == dValue) |
202 | 0 | { |
203 | 0 | if (dValue <= cpl::NumericLimits<GByte>::max() && |
204 | 0 | dValue >= cpl::NumericLimits<GByte>::lowest()) |
205 | 0 | return 8; |
206 | | |
207 | 0 | if (dValue <= cpl::NumericLimits<GInt8>::max() && |
208 | 0 | dValue >= cpl::NumericLimits<GInt8>::lowest()) |
209 | 0 | return 8; |
210 | | |
211 | 0 | if (dValue <= cpl::NumericLimits<GInt16>::max() && |
212 | 0 | dValue >= cpl::NumericLimits<GInt16>::lowest()) |
213 | 0 | return 16; |
214 | | |
215 | 0 | if (dValue <= cpl::NumericLimits<GUInt16>::max() && |
216 | 0 | dValue >= cpl::NumericLimits<GUInt16>::lowest()) |
217 | 0 | return 16; |
218 | | |
219 | 0 | if (dValue <= cpl::NumericLimits<GInt32>::max() && |
220 | 0 | dValue >= cpl::NumericLimits<GInt32>::lowest()) |
221 | 0 | return 32; |
222 | | |
223 | 0 | if (dValue <= cpl::NumericLimits<GUInt32>::max() && |
224 | 0 | dValue >= cpl::NumericLimits<GUInt32>::lowest()) |
225 | 0 | return 32; |
226 | | |
227 | 0 | if (dValue <= |
228 | 0 | static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) && |
229 | 0 | dValue >= static_cast<double>( |
230 | 0 | cpl::NumericLimits<std::uint64_t>::lowest())) |
231 | 0 | return 64; |
232 | 0 | } |
233 | 0 | else if (static_cast<float>(dValue) == dValue) |
234 | 0 | { |
235 | 0 | return 32; |
236 | 0 | } |
237 | | |
238 | 0 | return 64; |
239 | 0 | } |
240 | | |
241 | | /************************************************************************/ |
242 | | /* GDALFindDataType() */ |
243 | | /************************************************************************/ |
244 | | |
245 | | /** |
246 | | * \brief Finds the smallest data type able to support the given |
247 | | * requirements |
248 | | * |
249 | | * @param nBits number of bits necessary |
250 | | * @param bSigned if negative values are necessary |
251 | | * @param bFloating if non-integer values necessary |
252 | | * @param bComplex if complex values are necessary |
253 | | * |
254 | | * @return a best fit GDALDataType for supporting the requirements |
255 | | * @since GDAL 2.3 |
256 | | */ |
257 | | GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating, |
258 | | int bComplex) |
259 | 0 | { |
260 | 0 | if (!bFloating) |
261 | 0 | { |
262 | 0 | if (!bComplex) |
263 | 0 | { |
264 | 0 | if (!bSigned) |
265 | 0 | { |
266 | 0 | if (nBits <= 8) |
267 | 0 | return GDT_Byte; |
268 | 0 | if (nBits <= 16) |
269 | 0 | return GDT_UInt16; |
270 | 0 | if (nBits <= 32) |
271 | 0 | return GDT_UInt32; |
272 | 0 | if (nBits <= 64) |
273 | 0 | return GDT_UInt64; |
274 | 0 | return GDT_Float64; |
275 | 0 | } |
276 | 0 | else // bSigned |
277 | 0 | { |
278 | 0 | if (nBits <= 8) |
279 | 0 | return GDT_Int8; |
280 | 0 | if (nBits <= 16) |
281 | 0 | return GDT_Int16; |
282 | 0 | if (nBits <= 32) |
283 | 0 | return GDT_Int32; |
284 | 0 | if (nBits <= 64) |
285 | 0 | return GDT_Int64; |
286 | 0 | return GDT_Float64; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | else // bComplex |
290 | 0 | { |
291 | 0 | if (!bSigned) |
292 | 0 | { |
293 | | // We don't have complex unsigned data types, so |
294 | | // return a large-enough complex signed type |
295 | | |
296 | | // Do not choose CInt16 for backward compatibility |
297 | | // if (nBits <= 15) |
298 | | // return GDT_CInt16; |
299 | 0 | if (nBits <= 31) |
300 | 0 | return GDT_CInt32; |
301 | 0 | return GDT_CFloat64; |
302 | 0 | } |
303 | 0 | else // bSigned |
304 | 0 | { |
305 | 0 | if (nBits <= 16) |
306 | 0 | return GDT_CInt16; |
307 | 0 | if (nBits <= 32) |
308 | 0 | return GDT_CInt32; |
309 | 0 | return GDT_CFloat64; |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | 0 | else // bFloating |
314 | 0 | { |
315 | 0 | if (!bComplex) |
316 | 0 | { |
317 | | // Do not choose Float16 since is not supported everywhere |
318 | | // if (nBits <= 16) |
319 | | // return GDT_Float16; |
320 | 0 | if (nBits <= 32) |
321 | 0 | return GDT_Float32; |
322 | 0 | return GDT_Float64; |
323 | 0 | } |
324 | 0 | else // bComplex |
325 | 0 | { |
326 | | // Do not choose Float16 since is not supported everywhere |
327 | | // if (nBits <= 16) |
328 | | // return GDT_CFloat16; |
329 | 0 | if (nBits <= 32) |
330 | 0 | return GDT_CFloat32; |
331 | 0 | return GDT_CFloat64; |
332 | 0 | } |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | | /************************************************************************/ |
337 | | /* GDALFindDataTypeForValue() */ |
338 | | /************************************************************************/ |
339 | | |
340 | | /** |
341 | | * \brief Finds the smallest data type able to support the provided value |
342 | | * |
343 | | * @param dValue value to support |
344 | | * @param bComplex is the value complex |
345 | | * |
346 | | * @return a best fit GDALDataType for supporting the value |
347 | | * @since GDAL 2.3 |
348 | | */ |
349 | | GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex) |
350 | 0 | { |
351 | 0 | const bool bFloating = |
352 | 0 | round(dValue) != dValue || |
353 | 0 | dValue > |
354 | 0 | static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) || |
355 | 0 | dValue < |
356 | 0 | static_cast<double>(cpl::NumericLimits<std::int64_t>::lowest()); |
357 | 0 | const bool bSigned = bFloating || dValue < 0; |
358 | 0 | const int nBits = GetMinBitsForValue(dValue); |
359 | |
|
360 | 0 | return GDALFindDataType(nBits, bSigned, bFloating, bComplex); |
361 | 0 | } |
362 | | |
363 | | /************************************************************************/ |
364 | | /* GDALGetDataTypeSizeBytes() */ |
365 | | /************************************************************************/ |
366 | | |
367 | | /** |
368 | | * \brief Get data type size in <b>bytes</b>. |
369 | | * |
370 | | * Returns the size of a GDT_* type in bytes. In contrast, |
371 | | * GDALGetDataTypeSize() returns the size in <b>bits</b>. |
372 | | * |
373 | | * @param eDataType type, such as GDT_Byte. |
374 | | * @return the number of bytes or zero if it is not recognised. |
375 | | */ |
376 | | |
377 | | int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType) |
378 | | |
379 | 0 | { |
380 | 0 | switch (eDataType) |
381 | 0 | { |
382 | 0 | case GDT_Byte: |
383 | 0 | case GDT_Int8: |
384 | 0 | return 1; |
385 | | |
386 | 0 | case GDT_UInt16: |
387 | 0 | case GDT_Int16: |
388 | 0 | case GDT_Float16: |
389 | 0 | return 2; |
390 | | |
391 | 0 | case GDT_UInt32: |
392 | 0 | case GDT_Int32: |
393 | 0 | case GDT_Float32: |
394 | 0 | case GDT_CInt16: |
395 | 0 | case GDT_CFloat16: |
396 | 0 | return 4; |
397 | | |
398 | 0 | case GDT_Float64: |
399 | 0 | case GDT_CInt32: |
400 | 0 | case GDT_CFloat32: |
401 | 0 | case GDT_UInt64: |
402 | 0 | case GDT_Int64: |
403 | 0 | return 8; |
404 | | |
405 | 0 | case GDT_CFloat64: |
406 | 0 | return 16; |
407 | | |
408 | 0 | case GDT_Unknown: |
409 | 0 | case GDT_TypeCount: |
410 | 0 | break; |
411 | 0 | } |
412 | 0 | return 0; |
413 | 0 | } |
414 | | |
415 | | /************************************************************************/ |
416 | | /* GDALGetDataTypeSizeBits() */ |
417 | | /************************************************************************/ |
418 | | |
419 | | /** |
420 | | * \brief Get data type size in <b>bits</b>. |
421 | | * |
422 | | * Returns the size of a GDT_* type in bits, <b>not bytes</b>! Use |
423 | | * GDALGetDataTypeSizeBytes() for bytes. |
424 | | * |
425 | | * @param eDataType type, such as GDT_Byte. |
426 | | * @return the number of bits or zero if it is not recognised. |
427 | | */ |
428 | | |
429 | | int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType) |
430 | | |
431 | 0 | { |
432 | 0 | return GDALGetDataTypeSizeBytes(eDataType) * 8; |
433 | 0 | } |
434 | | |
435 | | /************************************************************************/ |
436 | | /* GDALGetDataTypeSize() */ |
437 | | /************************************************************************/ |
438 | | |
439 | | /** |
440 | | * \brief Get data type size in bits. <b>Deprecated</b>. |
441 | | * |
442 | | * Returns the size of a GDT_* type in bits, <b>not bytes</b>! |
443 | | * |
444 | | * Use GDALGetDataTypeSizeBytes() for bytes. |
445 | | * Use GDALGetDataTypeSizeBits() for bits. |
446 | | * |
447 | | * @param eDataType type, such as GDT_Byte. |
448 | | * @return the number of bits or zero if it is not recognised. |
449 | | */ |
450 | | |
451 | | int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType) |
452 | | |
453 | 0 | { |
454 | 0 | return GDALGetDataTypeSizeBytes(eDataType) * 8; |
455 | 0 | } |
456 | | |
457 | | /************************************************************************/ |
458 | | /* GDALDataTypeIsComplex() */ |
459 | | /************************************************************************/ |
460 | | |
461 | | /** |
462 | | * \brief Is data type complex? |
463 | | * |
464 | | * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32, |
465 | | * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary |
466 | | * component. |
467 | | */ |
468 | | |
469 | | int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType) |
470 | | |
471 | 0 | { |
472 | 0 | switch (eDataType) |
473 | 0 | { |
474 | 0 | case GDT_CInt16: |
475 | 0 | case GDT_CInt32: |
476 | 0 | case GDT_CFloat16: |
477 | 0 | case GDT_CFloat32: |
478 | 0 | case GDT_CFloat64: |
479 | 0 | return TRUE; |
480 | | |
481 | 0 | case GDT_Byte: |
482 | 0 | case GDT_Int8: |
483 | 0 | case GDT_Int16: |
484 | 0 | case GDT_UInt16: |
485 | 0 | case GDT_Int32: |
486 | 0 | case GDT_UInt32: |
487 | 0 | case GDT_Int64: |
488 | 0 | case GDT_UInt64: |
489 | 0 | case GDT_Float16: |
490 | 0 | case GDT_Float32: |
491 | 0 | case GDT_Float64: |
492 | 0 | return FALSE; |
493 | | |
494 | 0 | case GDT_Unknown: |
495 | 0 | case GDT_TypeCount: |
496 | 0 | break; |
497 | 0 | } |
498 | 0 | return FALSE; |
499 | 0 | } |
500 | | |
501 | | /************************************************************************/ |
502 | | /* GDALDataTypeIsFloating() */ |
503 | | /************************************************************************/ |
504 | | |
505 | | /** |
506 | | * \brief Is data type floating? (might be complex) |
507 | | * |
508 | | * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float16, |
509 | | * GDT_Float64, GDT_CFloat16, GDT_CFloat32, GDT_CFloat64) |
510 | | * @since GDAL 2.3 |
511 | | */ |
512 | | |
513 | | int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType) |
514 | 0 | { |
515 | 0 | switch (eDataType) |
516 | 0 | { |
517 | 0 | case GDT_Float16: |
518 | 0 | case GDT_Float32: |
519 | 0 | case GDT_Float64: |
520 | 0 | case GDT_CFloat16: |
521 | 0 | case GDT_CFloat32: |
522 | 0 | case GDT_CFloat64: |
523 | 0 | return TRUE; |
524 | | |
525 | 0 | case GDT_Byte: |
526 | 0 | case GDT_Int8: |
527 | 0 | case GDT_Int16: |
528 | 0 | case GDT_UInt16: |
529 | 0 | case GDT_Int32: |
530 | 0 | case GDT_UInt32: |
531 | 0 | case GDT_Int64: |
532 | 0 | case GDT_UInt64: |
533 | 0 | case GDT_CInt16: |
534 | 0 | case GDT_CInt32: |
535 | 0 | return FALSE; |
536 | | |
537 | 0 | case GDT_Unknown: |
538 | 0 | case GDT_TypeCount: |
539 | 0 | break; |
540 | 0 | } |
541 | 0 | return FALSE; |
542 | 0 | } |
543 | | |
544 | | /************************************************************************/ |
545 | | /* GDALDataTypeIsInteger() */ |
546 | | /************************************************************************/ |
547 | | |
548 | | /** |
549 | | * \brief Is data type integer? (might be complex) |
550 | | * |
551 | | * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16, |
552 | | * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32). |
553 | | * @since GDAL 2.3 |
554 | | */ |
555 | | |
556 | | int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType) |
557 | | |
558 | 0 | { |
559 | 0 | switch (eDataType) |
560 | 0 | { |
561 | 0 | case GDT_Byte: |
562 | 0 | case GDT_Int8: |
563 | 0 | case GDT_Int16: |
564 | 0 | case GDT_UInt16: |
565 | 0 | case GDT_Int32: |
566 | 0 | case GDT_UInt32: |
567 | 0 | case GDT_CInt16: |
568 | 0 | case GDT_CInt32: |
569 | 0 | case GDT_UInt64: |
570 | 0 | case GDT_Int64: |
571 | 0 | return TRUE; |
572 | | |
573 | 0 | case GDT_Float16: |
574 | 0 | case GDT_Float32: |
575 | 0 | case GDT_Float64: |
576 | 0 | case GDT_CFloat16: |
577 | 0 | case GDT_CFloat32: |
578 | 0 | case GDT_CFloat64: |
579 | 0 | return FALSE; |
580 | | |
581 | 0 | case GDT_Unknown: |
582 | 0 | case GDT_TypeCount: |
583 | 0 | break; |
584 | 0 | } |
585 | 0 | return FALSE; |
586 | 0 | } |
587 | | |
588 | | /************************************************************************/ |
589 | | /* GDALDataTypeIsSigned() */ |
590 | | /************************************************************************/ |
591 | | |
592 | | /** |
593 | | * \brief Is data type signed? |
594 | | * |
595 | | * @return TRUE if the passed type is signed. |
596 | | * @since GDAL 2.3 |
597 | | */ |
598 | | |
599 | | int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType) |
600 | 0 | { |
601 | 0 | switch (eDataType) |
602 | 0 | { |
603 | 0 | case GDT_Byte: |
604 | 0 | case GDT_UInt16: |
605 | 0 | case GDT_UInt32: |
606 | 0 | case GDT_UInt64: |
607 | 0 | return FALSE; |
608 | | |
609 | 0 | case GDT_Int8: |
610 | 0 | case GDT_Int16: |
611 | 0 | case GDT_Int32: |
612 | 0 | case GDT_Int64: |
613 | 0 | case GDT_Float16: |
614 | 0 | case GDT_Float32: |
615 | 0 | case GDT_Float64: |
616 | 0 | case GDT_CInt16: |
617 | 0 | case GDT_CInt32: |
618 | 0 | case GDT_CFloat16: |
619 | 0 | case GDT_CFloat32: |
620 | 0 | case GDT_CFloat64: |
621 | 0 | return TRUE; |
622 | | |
623 | 0 | case GDT_Unknown: |
624 | 0 | case GDT_TypeCount: |
625 | 0 | break; |
626 | 0 | } |
627 | 0 | return FALSE; |
628 | 0 | } |
629 | | |
630 | | /************************************************************************/ |
631 | | /* GDALDataTypeIsConversionLossy() */ |
632 | | /************************************************************************/ |
633 | | |
634 | | /** |
635 | | * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy |
636 | | * |
637 | | * @param eTypeFrom input datatype |
638 | | * @param eTypeTo output datatype |
639 | | * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy. |
640 | | * @since GDAL 2.3 |
641 | | */ |
642 | | |
643 | | int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom, |
644 | | GDALDataType eTypeTo) |
645 | 0 | { |
646 | | // E.g cfloat32 -> float32 |
647 | 0 | if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo)) |
648 | 0 | return TRUE; |
649 | | |
650 | 0 | eTypeFrom = GDALGetNonComplexDataType(eTypeFrom); |
651 | 0 | eTypeTo = GDALGetNonComplexDataType(eTypeTo); |
652 | |
|
653 | 0 | if (GDALDataTypeIsInteger(eTypeTo)) |
654 | 0 | { |
655 | | // E.g. float32 -> int32 |
656 | 0 | if (GDALDataTypeIsFloating(eTypeFrom)) |
657 | 0 | return TRUE; |
658 | | |
659 | | // E.g. Int16 to UInt16 |
660 | 0 | const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom); |
661 | 0 | const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo); |
662 | 0 | if (bIsFromSigned && !bIsToSigned) |
663 | 0 | return TRUE; |
664 | | |
665 | | // E.g UInt32 to UInt16 |
666 | 0 | const int nFromSize = GDALGetDataTypeSize(eTypeFrom); |
667 | 0 | const int nToSize = GDALGetDataTypeSize(eTypeTo); |
668 | 0 | if (nFromSize > nToSize) |
669 | 0 | return TRUE; |
670 | | |
671 | | // E.g UInt16 to Int16 |
672 | 0 | if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned) |
673 | 0 | return TRUE; |
674 | | |
675 | 0 | return FALSE; |
676 | 0 | } |
677 | | |
678 | 0 | if (eTypeTo == GDT_Float16 && |
679 | 0 | (eTypeFrom == GDT_Int16 || eTypeFrom == GDT_UInt16 || |
680 | 0 | eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 || |
681 | 0 | eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 || |
682 | 0 | eTypeFrom == GDT_Float32 || eTypeFrom == GDT_Float64)) |
683 | 0 | { |
684 | 0 | return TRUE; |
685 | 0 | } |
686 | | |
687 | 0 | if (eTypeTo == GDT_Float32 && |
688 | 0 | (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 || |
689 | 0 | eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 || |
690 | 0 | eTypeFrom == GDT_Float64)) |
691 | 0 | { |
692 | 0 | return TRUE; |
693 | 0 | } |
694 | | |
695 | 0 | if (eTypeTo == GDT_Float64 && |
696 | 0 | (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64)) |
697 | 0 | { |
698 | 0 | return TRUE; |
699 | 0 | } |
700 | | |
701 | 0 | return FALSE; |
702 | 0 | } |
703 | | |
704 | | /************************************************************************/ |
705 | | /* GDALGetDataTypeName() */ |
706 | | /************************************************************************/ |
707 | | |
708 | | /** |
709 | | * \brief Get name of data type. |
710 | | * |
711 | | * Returns a symbolic name for the data type. This is essentially the |
712 | | * the enumerated item name with the GDT_ prefix removed. So GDT_Byte returns |
713 | | * "Byte". The returned strings are static strings and should not be modified |
714 | | * or freed by the application. These strings are useful for reporting |
715 | | * datatypes in debug statements, errors and other user output. |
716 | | * |
717 | | * @param eDataType type to get name of. |
718 | | * @return string corresponding to existing data type |
719 | | * or NULL pointer if invalid type given. |
720 | | */ |
721 | | |
722 | | const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType) |
723 | | |
724 | 0 | { |
725 | 0 | switch (eDataType) |
726 | 0 | { |
727 | 0 | case GDT_Unknown: |
728 | 0 | return "Unknown"; |
729 | | |
730 | 0 | case GDT_Byte: |
731 | 0 | return "Byte"; |
732 | | |
733 | 0 | case GDT_Int8: |
734 | 0 | return "Int8"; |
735 | | |
736 | 0 | case GDT_UInt16: |
737 | 0 | return "UInt16"; |
738 | | |
739 | 0 | case GDT_Int16: |
740 | 0 | return "Int16"; |
741 | | |
742 | 0 | case GDT_UInt32: |
743 | 0 | return "UInt32"; |
744 | | |
745 | 0 | case GDT_Int32: |
746 | 0 | return "Int32"; |
747 | | |
748 | 0 | case GDT_UInt64: |
749 | 0 | return "UInt64"; |
750 | | |
751 | 0 | case GDT_Int64: |
752 | 0 | return "Int64"; |
753 | | |
754 | 0 | case GDT_Float16: |
755 | 0 | return "Float16"; |
756 | | |
757 | 0 | case GDT_Float32: |
758 | 0 | return "Float32"; |
759 | | |
760 | 0 | case GDT_Float64: |
761 | 0 | return "Float64"; |
762 | | |
763 | 0 | case GDT_CInt16: |
764 | 0 | return "CInt16"; |
765 | | |
766 | 0 | case GDT_CInt32: |
767 | 0 | return "CInt32"; |
768 | | |
769 | 0 | case GDT_CFloat16: |
770 | 0 | return "CFloat16"; |
771 | | |
772 | 0 | case GDT_CFloat32: |
773 | 0 | return "CFloat32"; |
774 | | |
775 | 0 | case GDT_CFloat64: |
776 | 0 | return "CFloat64"; |
777 | | |
778 | 0 | case GDT_TypeCount: |
779 | 0 | break; |
780 | 0 | } |
781 | 0 | return nullptr; |
782 | 0 | } |
783 | | |
784 | | /************************************************************************/ |
785 | | /* GDALGetDataTypeByName() */ |
786 | | /************************************************************************/ |
787 | | |
788 | | /** |
789 | | * \brief Get data type by symbolic name. |
790 | | * |
791 | | * Returns a data type corresponding to the given symbolic name. This |
792 | | * function is opposite to the GDALGetDataTypeName(). |
793 | | * |
794 | | * @param pszName string containing the symbolic name of the type. |
795 | | * |
796 | | * @return GDAL data type. |
797 | | */ |
798 | | |
799 | | GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName) |
800 | | |
801 | 0 | { |
802 | 0 | VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown); |
803 | | |
804 | 0 | for (int iType = 1; iType < GDT_TypeCount; iType++) |
805 | 0 | { |
806 | 0 | const auto eType = static_cast<GDALDataType>(iType); |
807 | 0 | if (GDALGetDataTypeName(eType) != nullptr && |
808 | 0 | EQUAL(GDALGetDataTypeName(eType), pszName)) |
809 | 0 | { |
810 | 0 | return eType; |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | 0 | return GDT_Unknown; |
815 | 0 | } |
816 | | |
817 | | /************************************************************************/ |
818 | | /* GDALAdjustValueToDataType() */ |
819 | | /************************************************************************/ |
820 | | |
821 | | template <class T> |
822 | | static inline void ClampAndRound(double &dfValue, bool &bClamped, |
823 | | bool &bRounded) |
824 | 0 | { |
825 | 0 | if (dfValue < static_cast<double>(cpl::NumericLimits<T>::lowest())) |
826 | 0 | { |
827 | 0 | bClamped = true; |
828 | 0 | dfValue = static_cast<double>(cpl::NumericLimits<T>::lowest()); |
829 | 0 | } |
830 | 0 | else if (dfValue > static_cast<double>(cpl::NumericLimits<T>::max())) |
831 | 0 | { |
832 | 0 | bClamped = true; |
833 | 0 | dfValue = static_cast<double>(cpl::NumericLimits<T>::max()); |
834 | 0 | } |
835 | 0 | else if (dfValue != static_cast<double>(static_cast<T>(dfValue))) |
836 | 0 | { |
837 | 0 | bRounded = true; |
838 | 0 | dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5))); |
839 | 0 | } |
840 | 0 | } Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned char>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<signed char>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<short>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned short>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<int>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned int>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<long>(double&, bool&, bool&) Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned long>(double&, bool&, bool&) |
841 | | |
842 | | /** |
843 | | * \brief Adjust a value to the output data type |
844 | | * |
845 | | * Adjustment consist in clamping to minimum/maximum values of the data type |
846 | | * and rounding for integral types. |
847 | | * |
848 | | * @param eDT target data type. |
849 | | * @param dfValue value to adjust. |
850 | | * @param pbClamped pointer to a integer(boolean) to indicate if clamping has |
851 | | * been made, or NULL |
852 | | * @param pbRounded pointer to a integer(boolean) to indicate if rounding has |
853 | | * been made, or NULL |
854 | | * |
855 | | * @return adjusted value |
856 | | * @since GDAL 2.1 |
857 | | */ |
858 | | |
859 | | double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue, |
860 | | int *pbClamped, int *pbRounded) |
861 | 0 | { |
862 | 0 | bool bClamped = false; |
863 | 0 | bool bRounded = false; |
864 | 0 | switch (eDT) |
865 | 0 | { |
866 | 0 | case GDT_Byte: |
867 | 0 | ClampAndRound<GByte>(dfValue, bClamped, bRounded); |
868 | 0 | break; |
869 | 0 | case GDT_Int8: |
870 | 0 | ClampAndRound<GInt8>(dfValue, bClamped, bRounded); |
871 | 0 | break; |
872 | 0 | case GDT_Int16: |
873 | 0 | ClampAndRound<GInt16>(dfValue, bClamped, bRounded); |
874 | 0 | break; |
875 | 0 | case GDT_UInt16: |
876 | 0 | ClampAndRound<GUInt16>(dfValue, bClamped, bRounded); |
877 | 0 | break; |
878 | 0 | case GDT_Int32: |
879 | 0 | ClampAndRound<GInt32>(dfValue, bClamped, bRounded); |
880 | 0 | break; |
881 | 0 | case GDT_UInt32: |
882 | 0 | ClampAndRound<GUInt32>(dfValue, bClamped, bRounded); |
883 | 0 | break; |
884 | 0 | case GDT_Int64: |
885 | 0 | ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded); |
886 | 0 | break; |
887 | 0 | case GDT_UInt64: |
888 | 0 | ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded); |
889 | 0 | break; |
890 | 0 | case GDT_Float16: |
891 | 0 | { |
892 | 0 | if (!std::isfinite(dfValue)) |
893 | 0 | break; |
894 | | |
895 | | // TODO: Use ClampAndRound |
896 | 0 | if (dfValue < cpl::NumericLimits<GFloat16>::lowest()) |
897 | 0 | { |
898 | 0 | bClamped = TRUE; |
899 | 0 | dfValue = |
900 | 0 | static_cast<double>(cpl::NumericLimits<GFloat16>::lowest()); |
901 | 0 | } |
902 | 0 | else if (dfValue > cpl::NumericLimits<GFloat16>::max()) |
903 | 0 | { |
904 | 0 | bClamped = TRUE; |
905 | 0 | dfValue = |
906 | 0 | static_cast<double>(cpl::NumericLimits<GFloat16>::max()); |
907 | 0 | } |
908 | 0 | else |
909 | 0 | { |
910 | | // Intentionally lose precision. |
911 | | // TODO(schwehr): Is the double cast really necessary? |
912 | | // If so, why? What will fail? |
913 | 0 | dfValue = static_cast<double>(static_cast<GFloat16>(dfValue)); |
914 | 0 | } |
915 | 0 | break; |
916 | 0 | } |
917 | 0 | case GDT_Float32: |
918 | 0 | { |
919 | 0 | if (!std::isfinite(dfValue)) |
920 | 0 | break; |
921 | | |
922 | | // TODO: Use ClampAndRound |
923 | 0 | if (dfValue < cpl::NumericLimits<float>::lowest()) |
924 | 0 | { |
925 | 0 | bClamped = TRUE; |
926 | 0 | dfValue = |
927 | 0 | static_cast<double>(cpl::NumericLimits<float>::lowest()); |
928 | 0 | } |
929 | 0 | else if (dfValue > cpl::NumericLimits<float>::max()) |
930 | 0 | { |
931 | 0 | bClamped = TRUE; |
932 | 0 | dfValue = static_cast<double>(cpl::NumericLimits<float>::max()); |
933 | 0 | } |
934 | 0 | else |
935 | 0 | { |
936 | | // Intentionally lose precision. |
937 | | // TODO(schwehr): Is the double cast really necessary? |
938 | | // If so, why? What will fail? |
939 | 0 | dfValue = static_cast<double>(static_cast<float>(dfValue)); |
940 | 0 | } |
941 | 0 | break; |
942 | 0 | } |
943 | 0 | case GDT_Float64: |
944 | 0 | case GDT_CInt16: |
945 | 0 | case GDT_CInt32: |
946 | 0 | case GDT_CFloat16: |
947 | 0 | case GDT_CFloat32: |
948 | 0 | case GDT_CFloat64: |
949 | 0 | case GDT_Unknown: |
950 | 0 | case GDT_TypeCount: |
951 | 0 | break; |
952 | 0 | } |
953 | 0 | if (pbClamped) |
954 | 0 | *pbClamped = bClamped; |
955 | 0 | if (pbRounded) |
956 | 0 | *pbRounded = bRounded; |
957 | 0 | return dfValue; |
958 | 0 | } |
959 | | |
960 | | /************************************************************************/ |
961 | | /* GDALIsValueExactAs() */ |
962 | | /************************************************************************/ |
963 | | |
964 | | /** |
965 | | * \brief Check whether the provided value can be exactly represented in a |
966 | | * data type. |
967 | | * |
968 | | * Only implemented for non-complex data types |
969 | | * |
970 | | * @param dfValue value to check. |
971 | | * @param eDT target data type. |
972 | | * |
973 | | * @return true if the provided value can be exactly represented in the |
974 | | * data type. |
975 | | * @since GDAL 3.10 |
976 | | */ |
977 | | bool GDALIsValueExactAs(double dfValue, GDALDataType eDT) |
978 | 0 | { |
979 | 0 | switch (eDT) |
980 | 0 | { |
981 | 0 | case GDT_Byte: |
982 | 0 | return GDALIsValueExactAs<uint8_t>(dfValue); |
983 | 0 | case GDT_Int8: |
984 | 0 | return GDALIsValueExactAs<int8_t>(dfValue); |
985 | 0 | case GDT_UInt16: |
986 | 0 | return GDALIsValueExactAs<uint16_t>(dfValue); |
987 | 0 | case GDT_Int16: |
988 | 0 | return GDALIsValueExactAs<int16_t>(dfValue); |
989 | 0 | case GDT_UInt32: |
990 | 0 | return GDALIsValueExactAs<uint32_t>(dfValue); |
991 | 0 | case GDT_Int32: |
992 | 0 | return GDALIsValueExactAs<int32_t>(dfValue); |
993 | 0 | case GDT_UInt64: |
994 | 0 | return GDALIsValueExactAs<uint64_t>(dfValue); |
995 | 0 | case GDT_Int64: |
996 | 0 | return GDALIsValueExactAs<int64_t>(dfValue); |
997 | 0 | case GDT_Float16: |
998 | 0 | return GDALIsValueExactAs<GFloat16>(dfValue); |
999 | 0 | case GDT_Float32: |
1000 | 0 | return GDALIsValueExactAs<float>(dfValue); |
1001 | 0 | case GDT_Float64: |
1002 | 0 | return true; |
1003 | 0 | case GDT_Unknown: |
1004 | 0 | case GDT_CInt16: |
1005 | 0 | case GDT_CInt32: |
1006 | 0 | case GDT_CFloat16: |
1007 | 0 | case GDT_CFloat32: |
1008 | 0 | case GDT_CFloat64: |
1009 | 0 | case GDT_TypeCount: |
1010 | 0 | break; |
1011 | 0 | } |
1012 | 0 | return true; |
1013 | 0 | } |
1014 | | |
1015 | | /************************************************************************/ |
1016 | | /* GDALIsValueInRangeOf() */ |
1017 | | /************************************************************************/ |
1018 | | |
1019 | | /** |
1020 | | * \brief Check whether the provided value can be represented in the range |
1021 | | * of the data type, possibly with rounding. |
1022 | | * |
1023 | | * Only implemented for non-complex data types |
1024 | | * |
1025 | | * @param dfValue value to check. |
1026 | | * @param eDT target data type. |
1027 | | * |
1028 | | * @return true if the provided value can be represented in the range |
1029 | | * of the data type, possibly with rounding. |
1030 | | * @since GDAL 3.11 |
1031 | | */ |
1032 | | bool GDALIsValueInRangeOf(double dfValue, GDALDataType eDT) |
1033 | 0 | { |
1034 | 0 | switch (eDT) |
1035 | 0 | { |
1036 | 0 | case GDT_Byte: |
1037 | 0 | return GDALIsValueInRange<uint8_t>(dfValue); |
1038 | 0 | case GDT_Int8: |
1039 | 0 | return GDALIsValueInRange<int8_t>(dfValue); |
1040 | 0 | case GDT_UInt16: |
1041 | 0 | return GDALIsValueInRange<uint16_t>(dfValue); |
1042 | 0 | case GDT_Int16: |
1043 | 0 | return GDALIsValueInRange<int16_t>(dfValue); |
1044 | 0 | case GDT_UInt32: |
1045 | 0 | return GDALIsValueInRange<uint32_t>(dfValue); |
1046 | 0 | case GDT_Int32: |
1047 | 0 | return GDALIsValueInRange<int32_t>(dfValue); |
1048 | 0 | case GDT_UInt64: |
1049 | 0 | return GDALIsValueInRange<uint64_t>(dfValue); |
1050 | 0 | case GDT_Int64: |
1051 | 0 | return GDALIsValueInRange<int64_t>(dfValue); |
1052 | 0 | case GDT_Float16: |
1053 | 0 | return GDALIsValueInRange<GFloat16>(dfValue); |
1054 | 0 | case GDT_Float32: |
1055 | 0 | return GDALIsValueInRange<float>(dfValue); |
1056 | 0 | case GDT_Float64: |
1057 | 0 | return true; |
1058 | 0 | case GDT_Unknown: |
1059 | 0 | case GDT_CInt16: |
1060 | 0 | case GDT_CInt32: |
1061 | 0 | case GDT_CFloat16: |
1062 | 0 | case GDT_CFloat32: |
1063 | 0 | case GDT_CFloat64: |
1064 | 0 | case GDT_TypeCount: |
1065 | 0 | break; |
1066 | 0 | } |
1067 | 0 | return true; |
1068 | 0 | } |
1069 | | |
1070 | | /************************************************************************/ |
1071 | | /* GDALGetNonComplexDataType() */ |
1072 | | /************************************************************************/ |
1073 | | /** |
1074 | | * \brief Return the base data type for the specified input. |
1075 | | * |
1076 | | * If the input data type is complex this function returns the base type |
1077 | | * i.e. the data type of the real and imaginary parts (non-complex). |
1078 | | * If the input data type is already non-complex, then it is returned |
1079 | | * unchanged. |
1080 | | * |
1081 | | * @param eDataType type, such as GDT_CFloat32. |
1082 | | * |
1083 | | * @return GDAL data type. |
1084 | | */ |
1085 | | GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType) |
1086 | 0 | { |
1087 | 0 | switch (eDataType) |
1088 | 0 | { |
1089 | 0 | case GDT_CInt16: |
1090 | 0 | return GDT_Int16; |
1091 | 0 | case GDT_CInt32: |
1092 | 0 | return GDT_Int32; |
1093 | 0 | case GDT_CFloat16: |
1094 | 0 | return GDT_Float16; |
1095 | 0 | case GDT_CFloat32: |
1096 | 0 | return GDT_Float32; |
1097 | 0 | case GDT_CFloat64: |
1098 | 0 | return GDT_Float64; |
1099 | | |
1100 | 0 | case GDT_Byte: |
1101 | 0 | case GDT_UInt16: |
1102 | 0 | case GDT_UInt32: |
1103 | 0 | case GDT_UInt64: |
1104 | 0 | case GDT_Int8: |
1105 | 0 | case GDT_Int16: |
1106 | 0 | case GDT_Int32: |
1107 | 0 | case GDT_Int64: |
1108 | 0 | case GDT_Float16: |
1109 | 0 | case GDT_Float32: |
1110 | 0 | case GDT_Float64: |
1111 | 0 | break; |
1112 | | |
1113 | 0 | case GDT_Unknown: |
1114 | 0 | case GDT_TypeCount: |
1115 | 0 | break; |
1116 | 0 | } |
1117 | 0 | return eDataType; |
1118 | 0 | } |
1119 | | |
1120 | | /************************************************************************/ |
1121 | | /* GDALGetAsyncStatusTypeByName() */ |
1122 | | /************************************************************************/ |
1123 | | /** |
1124 | | * Get AsyncStatusType by symbolic name. |
1125 | | * |
1126 | | * Returns a data type corresponding to the given symbolic name. This |
1127 | | * function is opposite to the GDALGetAsyncStatusTypeName(). |
1128 | | * |
1129 | | * @param pszName string containing the symbolic name of the type. |
1130 | | * |
1131 | | * @return GDAL AsyncStatus type. |
1132 | | */ |
1133 | | GDALAsyncStatusType CPL_DLL CPL_STDCALL |
1134 | | GDALGetAsyncStatusTypeByName(const char *pszName) |
1135 | 0 | { |
1136 | 0 | VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR); |
1137 | | |
1138 | 0 | for (int iType = 0; iType < GARIO_TypeCount; iType++) |
1139 | 0 | { |
1140 | 0 | const auto eType = static_cast<GDALAsyncStatusType>(iType); |
1141 | 0 | if (GDALGetAsyncStatusTypeName(eType) != nullptr && |
1142 | 0 | EQUAL(GDALGetAsyncStatusTypeName(eType), pszName)) |
1143 | 0 | { |
1144 | 0 | return eType; |
1145 | 0 | } |
1146 | 0 | } |
1147 | | |
1148 | 0 | return GARIO_ERROR; |
1149 | 0 | } |
1150 | | |
1151 | | /************************************************************************/ |
1152 | | /* GDALGetAsyncStatusTypeName() */ |
1153 | | /************************************************************************/ |
1154 | | |
1155 | | /** |
1156 | | * Get name of AsyncStatus data type. |
1157 | | * |
1158 | | * Returns a symbolic name for the AsyncStatus data type. This is essentially |
1159 | | * the enumerated item name with the GARIO_ prefix removed. So |
1160 | | * GARIO_COMPLETE returns "COMPLETE". The returned strings are static strings |
1161 | | * and should not be modified or freed by the application. These strings are |
1162 | | * useful for reporting datatypes in debug statements, errors and other user |
1163 | | * output. |
1164 | | * |
1165 | | * @param eAsyncStatusType type to get name of. |
1166 | | * @return string corresponding to type. |
1167 | | */ |
1168 | | |
1169 | | const char *CPL_STDCALL |
1170 | | GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType) |
1171 | | |
1172 | 0 | { |
1173 | 0 | switch (eAsyncStatusType) |
1174 | 0 | { |
1175 | 0 | case GARIO_PENDING: |
1176 | 0 | return "PENDING"; |
1177 | | |
1178 | 0 | case GARIO_UPDATE: |
1179 | 0 | return "UPDATE"; |
1180 | | |
1181 | 0 | case GARIO_ERROR: |
1182 | 0 | return "ERROR"; |
1183 | | |
1184 | 0 | case GARIO_COMPLETE: |
1185 | 0 | return "COMPLETE"; |
1186 | | |
1187 | 0 | default: |
1188 | 0 | return nullptr; |
1189 | 0 | } |
1190 | 0 | } |
1191 | | |
1192 | | /************************************************************************/ |
1193 | | /* GDALGetPaletteInterpretationName() */ |
1194 | | /************************************************************************/ |
1195 | | |
1196 | | /** |
1197 | | * \brief Get name of palette interpretation |
1198 | | * |
1199 | | * Returns a symbolic name for the palette interpretation. This is the |
1200 | | * the enumerated item name with the GPI_ prefix removed. So GPI_Gray returns |
1201 | | * "Gray". The returned strings are static strings and should not be modified |
1202 | | * or freed by the application. |
1203 | | * |
1204 | | * @param eInterp palette interpretation to get name of. |
1205 | | * @return string corresponding to palette interpretation. |
1206 | | */ |
1207 | | |
1208 | | const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp) |
1209 | | |
1210 | 0 | { |
1211 | 0 | switch (eInterp) |
1212 | 0 | { |
1213 | 0 | case GPI_Gray: |
1214 | 0 | return "Gray"; |
1215 | | |
1216 | 0 | case GPI_RGB: |
1217 | 0 | return "RGB"; |
1218 | | |
1219 | 0 | case GPI_CMYK: |
1220 | 0 | return "CMYK"; |
1221 | | |
1222 | 0 | case GPI_HLS: |
1223 | 0 | return "HLS"; |
1224 | | |
1225 | 0 | default: |
1226 | 0 | return "Unknown"; |
1227 | 0 | } |
1228 | 0 | } |
1229 | | |
1230 | | /************************************************************************/ |
1231 | | /* GDALGetColorInterpretationName() */ |
1232 | | /************************************************************************/ |
1233 | | |
1234 | | /** |
1235 | | * \brief Get name of color interpretation |
1236 | | * |
1237 | | * Returns a symbolic name for the color interpretation. This is derived from |
1238 | | * the enumerated item name with the GCI_ prefix removed, but there are some |
1239 | | * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red". |
1240 | | * The returned strings are static strings and should not be modified |
1241 | | * or freed by the application. |
1242 | | * |
1243 | | * @param eInterp color interpretation to get name of. |
1244 | | * @return string corresponding to color interpretation |
1245 | | * or NULL pointer if invalid enumerator given. |
1246 | | */ |
1247 | | |
1248 | | const char *GDALGetColorInterpretationName(GDALColorInterp eInterp) |
1249 | | |
1250 | 0 | { |
1251 | 0 | static_assert(GCI_IR_Start == GCI_RedEdgeBand + 1); |
1252 | 0 | static_assert(GCI_NIRBand == GCI_IR_Start); |
1253 | 0 | static_assert(GCI_SAR_Start == GCI_IR_End + 1); |
1254 | 0 | static_assert(GCI_Max == GCI_SAR_End); |
1255 | |
|
1256 | 0 | switch (eInterp) |
1257 | 0 | { |
1258 | 0 | case GCI_Undefined: |
1259 | 0 | break; |
1260 | | |
1261 | 0 | case GCI_GrayIndex: |
1262 | 0 | return "Gray"; |
1263 | | |
1264 | 0 | case GCI_PaletteIndex: |
1265 | 0 | return "Palette"; |
1266 | | |
1267 | 0 | case GCI_RedBand: |
1268 | 0 | return "Red"; |
1269 | | |
1270 | 0 | case GCI_GreenBand: |
1271 | 0 | return "Green"; |
1272 | | |
1273 | 0 | case GCI_BlueBand: |
1274 | 0 | return "Blue"; |
1275 | | |
1276 | 0 | case GCI_AlphaBand: |
1277 | 0 | return "Alpha"; |
1278 | | |
1279 | 0 | case GCI_HueBand: |
1280 | 0 | return "Hue"; |
1281 | | |
1282 | 0 | case GCI_SaturationBand: |
1283 | 0 | return "Saturation"; |
1284 | | |
1285 | 0 | case GCI_LightnessBand: |
1286 | 0 | return "Lightness"; |
1287 | | |
1288 | 0 | case GCI_CyanBand: |
1289 | 0 | return "Cyan"; |
1290 | | |
1291 | 0 | case GCI_MagentaBand: |
1292 | 0 | return "Magenta"; |
1293 | | |
1294 | 0 | case GCI_YellowBand: |
1295 | 0 | return "Yellow"; |
1296 | | |
1297 | 0 | case GCI_BlackBand: |
1298 | 0 | return "Black"; |
1299 | | |
1300 | 0 | case GCI_YCbCr_YBand: |
1301 | 0 | return "YCbCr_Y"; |
1302 | | |
1303 | 0 | case GCI_YCbCr_CbBand: |
1304 | 0 | return "YCbCr_Cb"; |
1305 | | |
1306 | 0 | case GCI_YCbCr_CrBand: |
1307 | 0 | return "YCbCr_Cr"; |
1308 | | |
1309 | 0 | case GCI_PanBand: |
1310 | 0 | return "Pan"; |
1311 | | |
1312 | 0 | case GCI_CoastalBand: |
1313 | 0 | return "Coastal"; |
1314 | | |
1315 | 0 | case GCI_RedEdgeBand: |
1316 | 0 | return "RedEdge"; |
1317 | | |
1318 | 0 | case GCI_NIRBand: |
1319 | 0 | return "NIR"; |
1320 | | |
1321 | 0 | case GCI_SWIRBand: |
1322 | 0 | return "SWIR"; |
1323 | | |
1324 | 0 | case GCI_MWIRBand: |
1325 | 0 | return "MWIR"; |
1326 | | |
1327 | 0 | case GCI_LWIRBand: |
1328 | 0 | return "LWIR"; |
1329 | | |
1330 | 0 | case GCI_TIRBand: |
1331 | 0 | return "TIR"; |
1332 | | |
1333 | 0 | case GCI_OtherIRBand: |
1334 | 0 | return "OtherIR"; |
1335 | | |
1336 | 0 | case GCI_IR_Reserved_1: |
1337 | 0 | return "IR_Reserved_1"; |
1338 | | |
1339 | 0 | case GCI_IR_Reserved_2: |
1340 | 0 | return "IR_Reserved_2"; |
1341 | | |
1342 | 0 | case GCI_IR_Reserved_3: |
1343 | 0 | return "IR_Reserved_3"; |
1344 | | |
1345 | 0 | case GCI_IR_Reserved_4: |
1346 | 0 | return "IR_Reserved_4"; |
1347 | | |
1348 | 0 | case GCI_SAR_Ka_Band: |
1349 | 0 | return "SAR_Ka"; |
1350 | | |
1351 | 0 | case GCI_SAR_K_Band: |
1352 | 0 | return "SAR_K"; |
1353 | | |
1354 | 0 | case GCI_SAR_Ku_Band: |
1355 | 0 | return "SAR_Ku"; |
1356 | | |
1357 | 0 | case GCI_SAR_X_Band: |
1358 | 0 | return "SAR_X"; |
1359 | | |
1360 | 0 | case GCI_SAR_C_Band: |
1361 | 0 | return "SAR_C"; |
1362 | | |
1363 | 0 | case GCI_SAR_S_Band: |
1364 | 0 | return "SAR_S"; |
1365 | | |
1366 | 0 | case GCI_SAR_L_Band: |
1367 | 0 | return "SAR_L"; |
1368 | | |
1369 | 0 | case GCI_SAR_P_Band: |
1370 | 0 | return "SAR_P"; |
1371 | | |
1372 | 0 | case GCI_SAR_Reserved_1: |
1373 | 0 | return "SAR_Reserved_1"; |
1374 | | |
1375 | 0 | case GCI_SAR_Reserved_2: |
1376 | 0 | return "SAR_Reserved_2"; |
1377 | 0 | } |
1378 | 0 | return "Undefined"; |
1379 | 0 | } |
1380 | | |
1381 | | /************************************************************************/ |
1382 | | /* GDALGetColorInterpretationByName() */ |
1383 | | /************************************************************************/ |
1384 | | |
1385 | | /** |
1386 | | * \brief Get color interpretation by symbolic name. |
1387 | | * |
1388 | | * Returns a color interpretation corresponding to the given symbolic name. This |
1389 | | * function is opposite to the GDALGetColorInterpretationName(). |
1390 | | * |
1391 | | * @param pszName string containing the symbolic name of the color |
1392 | | * interpretation. |
1393 | | * |
1394 | | * @return GDAL color interpretation. |
1395 | | * |
1396 | | * @since GDAL 1.7.0 |
1397 | | */ |
1398 | | |
1399 | | GDALColorInterp GDALGetColorInterpretationByName(const char *pszName) |
1400 | | |
1401 | 0 | { |
1402 | 0 | VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName", |
1403 | 0 | GCI_Undefined); |
1404 | | |
1405 | 0 | for (int iType = 0; iType <= GCI_Max; iType++) |
1406 | 0 | { |
1407 | 0 | if (EQUAL(GDALGetColorInterpretationName( |
1408 | 0 | static_cast<GDALColorInterp>(iType)), |
1409 | 0 | pszName)) |
1410 | 0 | { |
1411 | 0 | return static_cast<GDALColorInterp>(iType); |
1412 | 0 | } |
1413 | 0 | } |
1414 | | |
1415 | | // Accept British English spelling |
1416 | 0 | if (EQUAL(pszName, "grey")) |
1417 | 0 | return GCI_GrayIndex; |
1418 | | |
1419 | 0 | return GCI_Undefined; |
1420 | 0 | } |
1421 | | |
1422 | | /************************************************************************/ |
1423 | | /* GDALGetColorInterpFromSTACCommonName() */ |
1424 | | /************************************************************************/ |
1425 | | |
1426 | | static const struct |
1427 | | { |
1428 | | const char *pszName; |
1429 | | GDALColorInterp eInterp; |
1430 | | } asSTACCommonNames[] = { |
1431 | | {"pan", GCI_PanBand}, |
1432 | | {"coastal", GCI_CoastalBand}, |
1433 | | {"blue", GCI_BlueBand}, |
1434 | | {"green", GCI_GreenBand}, |
1435 | | {"green05", GCI_GreenBand}, // no exact match |
1436 | | {"yellow", GCI_YellowBand}, |
1437 | | {"red", GCI_RedBand}, |
1438 | | {"rededge", GCI_RedEdgeBand}, |
1439 | | {"rededge071", GCI_RedEdgeBand}, // no exact match |
1440 | | {"rededge075", GCI_RedEdgeBand}, // no exact match |
1441 | | {"rededge078", GCI_RedEdgeBand}, // no exact match |
1442 | | {"nir", GCI_NIRBand}, |
1443 | | {"nir08", GCI_NIRBand}, // no exact match |
1444 | | {"nir09", GCI_NIRBand}, // no exact match |
1445 | | {"cirrus", GCI_NIRBand}, // no exact match |
1446 | | {nullptr, |
1447 | | GCI_SWIRBand}, // so that GDALGetSTACCommonNameFromColorInterp returns null on GCI_SWIRBand |
1448 | | {"swir16", GCI_SWIRBand}, // no exact match |
1449 | | {"swir22", GCI_SWIRBand}, // no exact match |
1450 | | {"lwir", GCI_LWIRBand}, |
1451 | | {"lwir11", GCI_LWIRBand}, // no exact match |
1452 | | {"lwir12", GCI_LWIRBand}, // no exact match |
1453 | | }; |
1454 | | |
1455 | | /** Get color interpreetation from STAC eo:common_name |
1456 | | * |
1457 | | * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names |
1458 | | * |
1459 | | * @since GDAL 3.10 |
1460 | | */ |
1461 | | GDALColorInterp GDALGetColorInterpFromSTACCommonName(const char *pszName) |
1462 | 0 | { |
1463 | |
|
1464 | 0 | for (const auto &sAssoc : asSTACCommonNames) |
1465 | 0 | { |
1466 | 0 | if (sAssoc.pszName && EQUAL(pszName, sAssoc.pszName)) |
1467 | 0 | return sAssoc.eInterp; |
1468 | 0 | } |
1469 | 0 | return GCI_Undefined; |
1470 | 0 | } |
1471 | | |
1472 | | /************************************************************************/ |
1473 | | /* GDALGetSTACCommonNameFromColorInterp() */ |
1474 | | /************************************************************************/ |
1475 | | |
1476 | | /** Get STAC eo:common_name from GDAL color interpretation |
1477 | | * |
1478 | | * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names |
1479 | | * |
1480 | | * @return nullptr if there is no match |
1481 | | * |
1482 | | * @since GDAL 3.10 |
1483 | | */ |
1484 | | const char *GDALGetSTACCommonNameFromColorInterp(GDALColorInterp eInterp) |
1485 | 0 | { |
1486 | 0 | for (const auto &sAssoc : asSTACCommonNames) |
1487 | 0 | { |
1488 | 0 | if (eInterp == sAssoc.eInterp) |
1489 | 0 | return sAssoc.pszName; |
1490 | 0 | } |
1491 | 0 | return nullptr; |
1492 | 0 | } |
1493 | | |
1494 | | /************************************************************************/ |
1495 | | /* GDALGetRandomRasterSample() */ |
1496 | | /************************************************************************/ |
1497 | | |
1498 | | /** Undocumented |
1499 | | * @param hBand undocumented. |
1500 | | * @param nSamples undocumented. |
1501 | | * @param pafSampleBuf undocumented. |
1502 | | * @return undocumented |
1503 | | */ |
1504 | | int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples, |
1505 | | float *pafSampleBuf) |
1506 | | |
1507 | 0 | { |
1508 | 0 | VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0); |
1509 | | |
1510 | 0 | GDALRasterBand *poBand; |
1511 | |
|
1512 | 0 | poBand = GDALRasterBand::FromHandle( |
1513 | 0 | GDALGetRasterSampleOverview(hBand, nSamples)); |
1514 | 0 | CPLAssert(nullptr != poBand); |
1515 | | |
1516 | | /* -------------------------------------------------------------------- */ |
1517 | | /* Figure out the ratio of blocks we will read to get an */ |
1518 | | /* approximate value. */ |
1519 | | /* -------------------------------------------------------------------- */ |
1520 | 0 | int bGotNoDataValue = FALSE; |
1521 | |
|
1522 | 0 | double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue); |
1523 | |
|
1524 | 0 | int nBlockXSize = 0; |
1525 | 0 | int nBlockYSize = 0; |
1526 | 0 | poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); |
1527 | |
|
1528 | 0 | const int nBlocksPerRow = DIV_ROUND_UP(poBand->GetXSize(), nBlockXSize); |
1529 | 0 | const int nBlocksPerColumn = DIV_ROUND_UP(poBand->GetYSize(), nBlockYSize); |
1530 | |
|
1531 | 0 | const GIntBig nBlockPixels = |
1532 | 0 | static_cast<GIntBig>(nBlockXSize) * nBlockYSize; |
1533 | 0 | const GIntBig nBlockCount = |
1534 | 0 | static_cast<GIntBig>(nBlocksPerRow) * nBlocksPerColumn; |
1535 | |
|
1536 | 0 | if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 || |
1537 | 0 | nBlockCount == 0) |
1538 | 0 | { |
1539 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1540 | 0 | "GDALGetRandomRasterSample(): returning because band" |
1541 | 0 | " appears degenerate."); |
1542 | |
|
1543 | 0 | return FALSE; |
1544 | 0 | } |
1545 | | |
1546 | 0 | int nSampleRate = static_cast<int>( |
1547 | 0 | std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0)); |
1548 | |
|
1549 | 0 | if (nSampleRate == nBlocksPerRow && nSampleRate > 1) |
1550 | 0 | nSampleRate--; |
1551 | |
|
1552 | 0 | while (nSampleRate > 1 && |
1553 | 0 | ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples) |
1554 | 0 | nSampleRate--; |
1555 | |
|
1556 | 0 | int nBlockSampleRate = 1; |
1557 | |
|
1558 | 0 | if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0) |
1559 | 0 | nBlockSampleRate = static_cast<int>(std::max<GIntBig>( |
1560 | 0 | 1, |
1561 | 0 | nBlockPixels / (nSamples / ((nBlockCount - 1) / nSampleRate + 1)))); |
1562 | |
|
1563 | 0 | int nActualSamples = 0; |
1564 | |
|
1565 | 0 | for (GIntBig iSampleBlock = 0; iSampleBlock < nBlockCount; |
1566 | 0 | iSampleBlock += nSampleRate) |
1567 | 0 | { |
1568 | |
|
1569 | 0 | const int iYBlock = static_cast<int>(iSampleBlock / nBlocksPerRow); |
1570 | 0 | const int iXBlock = static_cast<int>(iSampleBlock % nBlocksPerRow); |
1571 | |
|
1572 | 0 | GDALRasterBlock *const poBlock = |
1573 | 0 | poBand->GetLockedBlockRef(iXBlock, iYBlock); |
1574 | 0 | if (poBlock == nullptr) |
1575 | 0 | continue; |
1576 | 0 | void *pDataRef = poBlock->GetDataRef(); |
1577 | |
|
1578 | 0 | int iXValid = nBlockXSize; |
1579 | 0 | if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize()) |
1580 | 0 | iXValid = poBand->GetXSize() - iXBlock * nBlockXSize; |
1581 | |
|
1582 | 0 | int iYValid = nBlockYSize; |
1583 | 0 | if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize()) |
1584 | 0 | iYValid = poBand->GetYSize() - iYBlock * nBlockYSize; |
1585 | |
|
1586 | 0 | int iRemainder = 0; |
1587 | |
|
1588 | 0 | for (int iY = 0; iY < iYValid; iY++) |
1589 | 0 | { |
1590 | 0 | int iX = iRemainder; // Used after for. |
1591 | 0 | for (; iX < iXValid; iX += nBlockSampleRate) |
1592 | 0 | { |
1593 | 0 | double dfValue = 0.0; |
1594 | 0 | const int iOffset = iX + iY * nBlockXSize; |
1595 | |
|
1596 | 0 | switch (poBlock->GetDataType()) |
1597 | 0 | { |
1598 | 0 | case GDT_Byte: |
1599 | 0 | dfValue = |
1600 | 0 | reinterpret_cast<const GByte *>(pDataRef)[iOffset]; |
1601 | 0 | break; |
1602 | 0 | case GDT_Int8: |
1603 | 0 | dfValue = |
1604 | 0 | reinterpret_cast<const GInt8 *>(pDataRef)[iOffset]; |
1605 | 0 | break; |
1606 | 0 | case GDT_UInt16: |
1607 | 0 | dfValue = reinterpret_cast<const GUInt16 *>( |
1608 | 0 | pDataRef)[iOffset]; |
1609 | 0 | break; |
1610 | 0 | case GDT_Int16: |
1611 | 0 | dfValue = |
1612 | 0 | reinterpret_cast<const GInt16 *>(pDataRef)[iOffset]; |
1613 | 0 | break; |
1614 | 0 | case GDT_UInt32: |
1615 | 0 | dfValue = reinterpret_cast<const GUInt32 *>( |
1616 | 0 | pDataRef)[iOffset]; |
1617 | 0 | break; |
1618 | 0 | case GDT_Int32: |
1619 | 0 | dfValue = |
1620 | 0 | reinterpret_cast<const GInt32 *>(pDataRef)[iOffset]; |
1621 | 0 | break; |
1622 | 0 | case GDT_UInt64: |
1623 | 0 | dfValue = static_cast<double>( |
1624 | 0 | reinterpret_cast<const std::uint64_t *>( |
1625 | 0 | pDataRef)[iOffset]); |
1626 | 0 | break; |
1627 | 0 | case GDT_Int64: |
1628 | 0 | dfValue = static_cast<double>( |
1629 | 0 | reinterpret_cast<const std::int64_t *>( |
1630 | 0 | pDataRef)[iOffset]); |
1631 | 0 | break; |
1632 | 0 | case GDT_Float16: |
1633 | 0 | dfValue = reinterpret_cast<const GFloat16 *>( |
1634 | 0 | pDataRef)[iOffset]; |
1635 | 0 | break; |
1636 | 0 | case GDT_Float32: |
1637 | 0 | dfValue = |
1638 | 0 | reinterpret_cast<const float *>(pDataRef)[iOffset]; |
1639 | 0 | break; |
1640 | 0 | case GDT_Float64: |
1641 | 0 | dfValue = |
1642 | 0 | reinterpret_cast<const double *>(pDataRef)[iOffset]; |
1643 | 0 | break; |
1644 | 0 | case GDT_CInt16: |
1645 | 0 | { |
1646 | | // TODO(schwehr): Clean up casts. |
1647 | 0 | const double dfReal = reinterpret_cast<const GInt16 *>( |
1648 | 0 | pDataRef)[iOffset * 2]; |
1649 | 0 | const double dfImag = reinterpret_cast<const GInt16 *>( |
1650 | 0 | pDataRef)[iOffset * 2 + 1]; |
1651 | 0 | dfValue = sqrt(dfReal * dfReal + dfImag * dfImag); |
1652 | 0 | break; |
1653 | 0 | } |
1654 | 0 | case GDT_CInt32: |
1655 | 0 | { |
1656 | 0 | const double dfReal = reinterpret_cast<const GInt32 *>( |
1657 | 0 | pDataRef)[iOffset * 2]; |
1658 | 0 | const double dfImag = reinterpret_cast<const GInt32 *>( |
1659 | 0 | pDataRef)[iOffset * 2 + 1]; |
1660 | 0 | dfValue = sqrt(dfReal * dfReal + dfImag * dfImag); |
1661 | 0 | break; |
1662 | 0 | } |
1663 | 0 | case GDT_CFloat16: |
1664 | 0 | { |
1665 | 0 | const double dfReal = |
1666 | 0 | reinterpret_cast<const GFloat16 *>( |
1667 | 0 | pDataRef)[iOffset * 2]; |
1668 | 0 | const double dfImag = |
1669 | 0 | reinterpret_cast<const GFloat16 *>( |
1670 | 0 | pDataRef)[iOffset * 2 + 1]; |
1671 | 0 | dfValue = sqrt(dfReal * dfReal + dfImag * dfImag); |
1672 | 0 | break; |
1673 | 0 | } |
1674 | 0 | case GDT_CFloat32: |
1675 | 0 | { |
1676 | 0 | const double dfReal = reinterpret_cast<const float *>( |
1677 | 0 | pDataRef)[iOffset * 2]; |
1678 | 0 | const double dfImag = reinterpret_cast<const float *>( |
1679 | 0 | pDataRef)[iOffset * 2 + 1]; |
1680 | 0 | dfValue = sqrt(dfReal * dfReal + dfImag * dfImag); |
1681 | 0 | break; |
1682 | 0 | } |
1683 | 0 | case GDT_CFloat64: |
1684 | 0 | { |
1685 | 0 | const double dfReal = reinterpret_cast<const double *>( |
1686 | 0 | pDataRef)[iOffset * 2]; |
1687 | 0 | const double dfImag = reinterpret_cast<const double *>( |
1688 | 0 | pDataRef)[iOffset * 2 + 1]; |
1689 | 0 | dfValue = sqrt(dfReal * dfReal + dfImag * dfImag); |
1690 | 0 | break; |
1691 | 0 | } |
1692 | 0 | case GDT_Unknown: |
1693 | 0 | case GDT_TypeCount: |
1694 | 0 | CPLAssert(false); |
1695 | 0 | } |
1696 | | |
1697 | 0 | if (bGotNoDataValue && dfValue == dfNoDataValue) |
1698 | 0 | continue; |
1699 | | |
1700 | 0 | if (nActualSamples < nSamples) |
1701 | 0 | pafSampleBuf[nActualSamples++] = |
1702 | 0 | static_cast<float>(dfValue); |
1703 | 0 | } |
1704 | | |
1705 | 0 | iRemainder = iX - iXValid; |
1706 | 0 | } |
1707 | | |
1708 | 0 | poBlock->DropLock(); |
1709 | 0 | } |
1710 | | |
1711 | 0 | return nActualSamples; |
1712 | 0 | } |
1713 | | |
1714 | | /************************************************************************/ |
1715 | | /* gdal::GCP */ |
1716 | | /************************************************************************/ |
1717 | | |
1718 | | namespace gdal |
1719 | | { |
1720 | | /** Constructor. */ |
1721 | | GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine, |
1722 | | double dfX, double dfY, double dfZ) |
1723 | 0 | : gcp{CPLStrdup(pszId ? pszId : ""), |
1724 | 0 | CPLStrdup(pszInfo ? pszInfo : ""), |
1725 | 0 | dfPixel, |
1726 | 0 | dfLine, |
1727 | 0 | dfX, |
1728 | 0 | dfY, |
1729 | 0 | dfZ} |
1730 | 0 | { |
1731 | 0 | static_assert(sizeof(GCP) == sizeof(GDAL_GCP)); |
1732 | 0 | } |
1733 | | |
1734 | | /** Destructor. */ |
1735 | | GCP::~GCP() |
1736 | 0 | { |
1737 | 0 | CPLFree(gcp.pszId); |
1738 | 0 | CPLFree(gcp.pszInfo); |
1739 | 0 | } |
1740 | | |
1741 | | /** Constructor from a C GDAL_GCP instance. */ |
1742 | | GCP::GCP(const GDAL_GCP &other) |
1743 | 0 | : gcp{CPLStrdup(other.pszId), |
1744 | 0 | CPLStrdup(other.pszInfo), |
1745 | 0 | other.dfGCPPixel, |
1746 | 0 | other.dfGCPLine, |
1747 | 0 | other.dfGCPX, |
1748 | 0 | other.dfGCPY, |
1749 | 0 | other.dfGCPZ} |
1750 | 0 | { |
1751 | 0 | } |
1752 | | |
1753 | | /** Copy constructor. */ |
1754 | 0 | GCP::GCP(const GCP &other) : GCP(other.gcp) |
1755 | 0 | { |
1756 | 0 | } |
1757 | | |
1758 | | /** Move constructor. */ |
1759 | | GCP::GCP(GCP &&other) |
1760 | 0 | : gcp{other.gcp.pszId, other.gcp.pszInfo, other.gcp.dfGCPPixel, |
1761 | 0 | other.gcp.dfGCPLine, other.gcp.dfGCPX, other.gcp.dfGCPY, |
1762 | 0 | other.gcp.dfGCPZ} |
1763 | 0 | { |
1764 | 0 | other.gcp.pszId = nullptr; |
1765 | 0 | other.gcp.pszInfo = nullptr; |
1766 | 0 | } |
1767 | | |
1768 | | /** Copy assignment operator. */ |
1769 | | GCP &GCP::operator=(const GCP &other) |
1770 | 0 | { |
1771 | 0 | if (this != &other) |
1772 | 0 | { |
1773 | 0 | CPLFree(gcp.pszId); |
1774 | 0 | CPLFree(gcp.pszInfo); |
1775 | 0 | gcp = other.gcp; |
1776 | 0 | gcp.pszId = CPLStrdup(other.gcp.pszId); |
1777 | 0 | gcp.pszInfo = CPLStrdup(other.gcp.pszInfo); |
1778 | 0 | } |
1779 | 0 | return *this; |
1780 | 0 | } |
1781 | | |
1782 | | /** Move assignment operator. */ |
1783 | | GCP &GCP::operator=(GCP &&other) |
1784 | 0 | { |
1785 | 0 | if (this != &other) |
1786 | 0 | { |
1787 | 0 | CPLFree(gcp.pszId); |
1788 | 0 | CPLFree(gcp.pszInfo); |
1789 | 0 | gcp = other.gcp; |
1790 | 0 | other.gcp.pszId = nullptr; |
1791 | 0 | other.gcp.pszInfo = nullptr; |
1792 | 0 | } |
1793 | 0 | return *this; |
1794 | 0 | } |
1795 | | |
1796 | | /** Set the 'id' member of the GCP. */ |
1797 | | void GCP::SetId(const char *pszId) |
1798 | 0 | { |
1799 | 0 | CPLFree(gcp.pszId); |
1800 | 0 | gcp.pszId = CPLStrdup(pszId ? pszId : ""); |
1801 | 0 | } |
1802 | | |
1803 | | /** Set the 'info' member of the GCP. */ |
1804 | | void GCP::SetInfo(const char *pszInfo) |
1805 | 0 | { |
1806 | 0 | CPLFree(gcp.pszInfo); |
1807 | 0 | gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : ""); |
1808 | 0 | } |
1809 | | |
1810 | | /** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */ |
1811 | | /*static */ |
1812 | | const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs) |
1813 | 0 | { |
1814 | 0 | return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr(); |
1815 | 0 | } |
1816 | | |
1817 | | /** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */ |
1818 | | /*static*/ |
1819 | | std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount) |
1820 | 0 | { |
1821 | 0 | return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount); |
1822 | 0 | } |
1823 | | |
1824 | | } /* namespace gdal */ |
1825 | | |
1826 | | /************************************************************************/ |
1827 | | /* GDALInitGCPs() */ |
1828 | | /************************************************************************/ |
1829 | | |
1830 | | /** Initialize an array of GCPs. |
1831 | | * |
1832 | | * Numeric values are initialized to 0 and strings to the empty string "" |
1833 | | * allocated with CPLStrdup() |
1834 | | * An array initialized with GDALInitGCPs() must be de-initialized with |
1835 | | * GDALDeinitGCPs(). |
1836 | | * |
1837 | | * @param nCount number of GCPs in psGCP |
1838 | | * @param psGCP array of GCPs of size nCount. |
1839 | | */ |
1840 | | void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP) |
1841 | | |
1842 | 0 | { |
1843 | 0 | if (nCount > 0) |
1844 | 0 | { |
1845 | 0 | VALIDATE_POINTER0(psGCP, "GDALInitGCPs"); |
1846 | 0 | } |
1847 | | |
1848 | 0 | for (int iGCP = 0; iGCP < nCount; iGCP++) |
1849 | 0 | { |
1850 | 0 | memset(psGCP, 0, sizeof(GDAL_GCP)); |
1851 | 0 | psGCP->pszId = CPLStrdup(""); |
1852 | 0 | psGCP->pszInfo = CPLStrdup(""); |
1853 | 0 | psGCP++; |
1854 | 0 | } |
1855 | 0 | } |
1856 | | |
1857 | | /************************************************************************/ |
1858 | | /* GDALDeinitGCPs() */ |
1859 | | /************************************************************************/ |
1860 | | |
1861 | | /** De-initialize an array of GCPs (initialized with GDALInitGCPs()) |
1862 | | * |
1863 | | * @param nCount number of GCPs in psGCP |
1864 | | * @param psGCP array of GCPs of size nCount. |
1865 | | */ |
1866 | | void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP) |
1867 | | |
1868 | 0 | { |
1869 | 0 | if (nCount > 0) |
1870 | 0 | { |
1871 | 0 | VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs"); |
1872 | 0 | } |
1873 | | |
1874 | 0 | for (int iGCP = 0; iGCP < nCount; iGCP++) |
1875 | 0 | { |
1876 | 0 | CPLFree(psGCP->pszId); |
1877 | 0 | CPLFree(psGCP->pszInfo); |
1878 | 0 | psGCP++; |
1879 | 0 | } |
1880 | 0 | } |
1881 | | |
1882 | | /************************************************************************/ |
1883 | | /* GDALDuplicateGCPs() */ |
1884 | | /************************************************************************/ |
1885 | | |
1886 | | /** Duplicate an array of GCPs |
1887 | | * |
1888 | | * The return must be freed with GDALDeinitGCPs() followed by CPLFree() |
1889 | | * |
1890 | | * @param nCount number of GCPs in psGCP |
1891 | | * @param pasGCPList array of GCPs of size nCount. |
1892 | | */ |
1893 | | GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList) |
1894 | | |
1895 | 0 | { |
1896 | 0 | GDAL_GCP *pasReturn = |
1897 | 0 | static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount)); |
1898 | 0 | GDALInitGCPs(nCount, pasReturn); |
1899 | |
|
1900 | 0 | for (int iGCP = 0; iGCP < nCount; iGCP++) |
1901 | 0 | { |
1902 | 0 | CPLFree(pasReturn[iGCP].pszId); |
1903 | 0 | pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId); |
1904 | |
|
1905 | 0 | CPLFree(pasReturn[iGCP].pszInfo); |
1906 | 0 | pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo); |
1907 | |
|
1908 | 0 | pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel; |
1909 | 0 | pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine; |
1910 | 0 | pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX; |
1911 | 0 | pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY; |
1912 | 0 | pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ; |
1913 | 0 | } |
1914 | |
|
1915 | 0 | return pasReturn; |
1916 | 0 | } |
1917 | | |
1918 | | /************************************************************************/ |
1919 | | /* GDALFindAssociatedFile() */ |
1920 | | /************************************************************************/ |
1921 | | |
1922 | | /** |
1923 | | * \brief Find file with alternate extension. |
1924 | | * |
1925 | | * Finds the file with the indicated extension, substituting it in place |
1926 | | * of the extension of the base filename. Generally used to search for |
1927 | | * associated files like world files .RPB files, etc. If necessary, the |
1928 | | * extension will be tried in both upper and lower case. If a sibling file |
1929 | | * list is available it will be used instead of doing VSIStatExL() calls to |
1930 | | * probe the file system. |
1931 | | * |
1932 | | * Note that the result is a dynamic CPLString so this method should not |
1933 | | * be used in a situation where there could be cross heap issues. It is |
1934 | | * generally imprudent for application built on GDAL to use this function |
1935 | | * unless they are sure they will always use the same runtime heap as GDAL. |
1936 | | * |
1937 | | * @param pszBaseFilename the filename relative to which to search. |
1938 | | * @param pszExt the target extension in either upper or lower case. |
1939 | | * @param papszSiblingFiles the list of files in the same directory as |
1940 | | * pszBaseFilename or NULL if they are not known. |
1941 | | * @param nFlags special options controlling search. None defined yet, just |
1942 | | * pass 0. |
1943 | | * |
1944 | | * @return an empty string if the target is not found, otherwise the target |
1945 | | * file with similar path style as the pszBaseFilename. |
1946 | | */ |
1947 | | |
1948 | | /**/ |
1949 | | /**/ |
1950 | | |
1951 | | CPLString GDALFindAssociatedFile(const char *pszBaseFilename, |
1952 | | const char *pszExt, |
1953 | | CSLConstList papszSiblingFiles, |
1954 | | CPL_UNUSED int nFlags) |
1955 | | |
1956 | 0 | { |
1957 | 0 | CPLString osTarget = CPLResetExtensionSafe(pszBaseFilename, pszExt); |
1958 | |
|
1959 | 0 | if (papszSiblingFiles == nullptr || |
1960 | | // cppcheck-suppress knownConditionTrueFalse |
1961 | 0 | !GDALCanReliablyUseSiblingFileList(osTarget.c_str())) |
1962 | 0 | { |
1963 | 0 | VSIStatBufL sStatBuf; |
1964 | |
|
1965 | 0 | if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0) |
1966 | 0 | { |
1967 | 0 | CPLString osAltExt = pszExt; |
1968 | |
|
1969 | 0 | if (islower(static_cast<unsigned char>(pszExt[0]))) |
1970 | 0 | osAltExt = osAltExt.toupper(); |
1971 | 0 | else |
1972 | 0 | osAltExt = osAltExt.tolower(); |
1973 | |
|
1974 | 0 | osTarget = CPLResetExtensionSafe(pszBaseFilename, osAltExt); |
1975 | |
|
1976 | 0 | if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0) |
1977 | 0 | return ""; |
1978 | 0 | } |
1979 | 0 | } |
1980 | 0 | else |
1981 | 0 | { |
1982 | 0 | const int iSibling = |
1983 | 0 | CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget)); |
1984 | 0 | if (iSibling < 0) |
1985 | 0 | return ""; |
1986 | | |
1987 | 0 | osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling])); |
1988 | 0 | osTarget += papszSiblingFiles[iSibling]; |
1989 | 0 | } |
1990 | | |
1991 | 0 | return osTarget; |
1992 | 0 | } |
1993 | | |
1994 | | /************************************************************************/ |
1995 | | /* GDALLoadOziMapFile() */ |
1996 | | /************************************************************************/ |
1997 | | |
1998 | | /** Helper function for translator implementer wanting support for OZI .map |
1999 | | * |
2000 | | * @param pszFilename filename of .tab file |
2001 | | * @param padfGeoTransform output geotransform. Must hold 6 doubles. |
2002 | | * @param ppszWKT output pointer to a string that will be allocated with |
2003 | | * CPLMalloc(). |
2004 | | * @param pnGCPCount output pointer to GCP count. |
2005 | | * @param ppasGCPs outputer pointer to an array of GCPs. |
2006 | | * @return TRUE in case of success, FALSE otherwise. |
2007 | | */ |
2008 | | int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename, |
2009 | | double *padfGeoTransform, char **ppszWKT, |
2010 | | int *pnGCPCount, GDAL_GCP **ppasGCPs) |
2011 | | |
2012 | 0 | { |
2013 | 0 | VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE); |
2014 | 0 | VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE); |
2015 | 0 | VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE); |
2016 | 0 | VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE); |
2017 | | |
2018 | 0 | char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr); |
2019 | |
|
2020 | 0 | if (!papszLines) |
2021 | 0 | return FALSE; |
2022 | | |
2023 | 0 | int nLines = CSLCount(papszLines); |
2024 | | |
2025 | | // Check the OziExplorer Map file signature |
2026 | 0 | if (nLines < 5 || |
2027 | 0 | !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version ")) |
2028 | 0 | { |
2029 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
2030 | 0 | "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map " |
2031 | 0 | "format.", |
2032 | 0 | pszFilename); |
2033 | 0 | CSLDestroy(papszLines); |
2034 | 0 | return FALSE; |
2035 | 0 | } |
2036 | | |
2037 | 0 | OGRSpatialReference oSRS; |
2038 | 0 | OGRErr eErr = OGRERR_NONE; |
2039 | | |
2040 | | /* The Map Scale Factor has been introduced recently on the 6th line */ |
2041 | | /* and is a trick that is used to just change that line without changing */ |
2042 | | /* the rest of the MAP file but providing an imagery that is smaller or |
2043 | | * larger */ |
2044 | | /* so we have to correct the pixel/line values read in the .MAP file so they |
2045 | | */ |
2046 | | /* match the actual imagery dimension. Well, this is a bad summary of what |
2047 | | */ |
2048 | | /* is explained at |
2049 | | * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */ |
2050 | 0 | double dfMSF = 1; |
2051 | |
|
2052 | 0 | for (int iLine = 5; iLine < nLines; iLine++) |
2053 | 0 | { |
2054 | 0 | if (STARTS_WITH_CI(papszLines[iLine], "MSF,")) |
2055 | 0 | { |
2056 | 0 | dfMSF = CPLAtof(papszLines[iLine] + 4); |
2057 | 0 | if (dfMSF <= 0.01) /* Suspicious values */ |
2058 | 0 | { |
2059 | 0 | CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]); |
2060 | 0 | dfMSF = 1; |
2061 | 0 | } |
2062 | 0 | } |
2063 | 0 | } |
2064 | |
|
2065 | 0 | eErr = oSRS.importFromOzi(papszLines); |
2066 | 0 | if (eErr == OGRERR_NONE) |
2067 | 0 | { |
2068 | 0 | if (ppszWKT != nullptr) |
2069 | 0 | oSRS.exportToWkt(ppszWKT); |
2070 | 0 | } |
2071 | |
|
2072 | 0 | int nCoordinateCount = 0; |
2073 | | // TODO(schwehr): Initialize asGCPs. |
2074 | 0 | GDAL_GCP asGCPs[30]; |
2075 | | |
2076 | | // Iterate all lines in the MAP-file |
2077 | 0 | for (int iLine = 5; iLine < nLines; iLine++) |
2078 | 0 | { |
2079 | 0 | char **papszTok = CSLTokenizeString2( |
2080 | 0 | papszLines[iLine], ",", |
2081 | 0 | CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); |
2082 | |
|
2083 | 0 | if (CSLCount(papszTok) < 12) |
2084 | 0 | { |
2085 | 0 | CSLDestroy(papszTok); |
2086 | 0 | continue; |
2087 | 0 | } |
2088 | | |
2089 | 0 | if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") && |
2090 | 0 | !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") && |
2091 | 0 | nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs))) |
2092 | 0 | { |
2093 | 0 | bool bReadOk = false; |
2094 | 0 | double dfLon = 0.0; |
2095 | 0 | double dfLat = 0.0; |
2096 | |
|
2097 | 0 | if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") && |
2098 | 0 | !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], "")) |
2099 | 0 | { |
2100 | | // Set geographical coordinates of the pixels |
2101 | 0 | dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0; |
2102 | 0 | dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0; |
2103 | 0 | if (EQUAL(papszTok[11], "W")) |
2104 | 0 | dfLon = -dfLon; |
2105 | 0 | if (EQUAL(papszTok[8], "S")) |
2106 | 0 | dfLat = -dfLat; |
2107 | | |
2108 | | // Transform from the geographical coordinates into projected |
2109 | | // coordinates. |
2110 | 0 | if (eErr == OGRERR_NONE) |
2111 | 0 | { |
2112 | 0 | OGRSpatialReference *poLongLat = oSRS.CloneGeogCS(); |
2113 | |
|
2114 | 0 | if (poLongLat) |
2115 | 0 | { |
2116 | 0 | oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
2117 | 0 | poLongLat->SetAxisMappingStrategy( |
2118 | 0 | OAMS_TRADITIONAL_GIS_ORDER); |
2119 | |
|
2120 | 0 | OGRCoordinateTransformation *poTransform = |
2121 | 0 | OGRCreateCoordinateTransformation(poLongLat, &oSRS); |
2122 | 0 | if (poTransform) |
2123 | 0 | { |
2124 | 0 | bReadOk = CPL_TO_BOOL( |
2125 | 0 | poTransform->Transform(1, &dfLon, &dfLat)); |
2126 | 0 | delete poTransform; |
2127 | 0 | } |
2128 | 0 | delete poLongLat; |
2129 | 0 | } |
2130 | 0 | } |
2131 | 0 | } |
2132 | 0 | else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], "")) |
2133 | 0 | { |
2134 | | // Set cartesian coordinates of the pixels. |
2135 | 0 | dfLon = CPLAtofM(papszTok[14]); |
2136 | 0 | dfLat = CPLAtofM(papszTok[15]); |
2137 | 0 | bReadOk = true; |
2138 | | |
2139 | | // if ( EQUAL(papszTok[16], "S") ) |
2140 | | // dfLat = -dfLat; |
2141 | 0 | } |
2142 | |
|
2143 | 0 | if (bReadOk) |
2144 | 0 | { |
2145 | 0 | GDALInitGCPs(1, asGCPs + nCoordinateCount); |
2146 | | |
2147 | | // Set pixel/line part |
2148 | 0 | asGCPs[nCoordinateCount].dfGCPPixel = |
2149 | 0 | CPLAtofM(papszTok[2]) / dfMSF; |
2150 | 0 | asGCPs[nCoordinateCount].dfGCPLine = |
2151 | 0 | CPLAtofM(papszTok[3]) / dfMSF; |
2152 | |
|
2153 | 0 | asGCPs[nCoordinateCount].dfGCPX = dfLon; |
2154 | 0 | asGCPs[nCoordinateCount].dfGCPY = dfLat; |
2155 | |
|
2156 | 0 | nCoordinateCount++; |
2157 | 0 | } |
2158 | 0 | } |
2159 | |
|
2160 | 0 | CSLDestroy(papszTok); |
2161 | 0 | } |
2162 | |
|
2163 | 0 | CSLDestroy(papszLines); |
2164 | |
|
2165 | 0 | if (nCoordinateCount == 0) |
2166 | 0 | { |
2167 | 0 | CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.", |
2168 | 0 | pszFilename); |
2169 | 0 | return FALSE; |
2170 | 0 | } |
2171 | | |
2172 | | /* -------------------------------------------------------------------- */ |
2173 | | /* Try to convert the GCPs into a geotransform definition, if */ |
2174 | | /* possible. Otherwise we will need to use them as GCPs. */ |
2175 | | /* -------------------------------------------------------------------- */ |
2176 | 0 | if (!GDALGCPsToGeoTransform( |
2177 | 0 | nCoordinateCount, asGCPs, padfGeoTransform, |
2178 | 0 | CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO")))) |
2179 | 0 | { |
2180 | 0 | if (pnGCPCount && ppasGCPs) |
2181 | 0 | { |
2182 | 0 | CPLDebug( |
2183 | 0 | "GDAL", |
2184 | 0 | "GDALLoadOziMapFile(%s) found file, was not able to derive a\n" |
2185 | 0 | "first order geotransform. Using points as GCPs.", |
2186 | 0 | pszFilename); |
2187 | |
|
2188 | 0 | *ppasGCPs = static_cast<GDAL_GCP *>( |
2189 | 0 | CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount)); |
2190 | 0 | memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount); |
2191 | 0 | *pnGCPCount = nCoordinateCount; |
2192 | 0 | } |
2193 | 0 | } |
2194 | 0 | else |
2195 | 0 | { |
2196 | 0 | GDALDeinitGCPs(nCoordinateCount, asGCPs); |
2197 | 0 | } |
2198 | |
|
2199 | 0 | return TRUE; |
2200 | 0 | } |
2201 | | |
2202 | | /************************************************************************/ |
2203 | | /* GDALReadOziMapFile() */ |
2204 | | /************************************************************************/ |
2205 | | |
2206 | | /** Helper function for translator implementer wanting support for OZI .map |
2207 | | * |
2208 | | * @param pszBaseFilename filename whose basename will help building the .map |
2209 | | * filename. |
2210 | | * @param padfGeoTransform output geotransform. Must hold 6 doubles. |
2211 | | * @param ppszWKT output pointer to a string that will be allocated with |
2212 | | * CPLMalloc(). |
2213 | | * @param pnGCPCount output pointer to GCP count. |
2214 | | * @param ppasGCPs outputer pointer to an array of GCPs. |
2215 | | * @return TRUE in case of success, FALSE otherwise. |
2216 | | */ |
2217 | | int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename, |
2218 | | double *padfGeoTransform, char **ppszWKT, |
2219 | | int *pnGCPCount, GDAL_GCP **ppasGCPs) |
2220 | | |
2221 | 0 | { |
2222 | | /* -------------------------------------------------------------------- */ |
2223 | | /* Try lower case, then upper case. */ |
2224 | | /* -------------------------------------------------------------------- */ |
2225 | 0 | std::string osOzi = CPLResetExtensionSafe(pszBaseFilename, "map"); |
2226 | |
|
2227 | 0 | VSILFILE *fpOzi = VSIFOpenL(osOzi.c_str(), "rt"); |
2228 | |
|
2229 | 0 | if (fpOzi == nullptr && VSIIsCaseSensitiveFS(osOzi.c_str())) |
2230 | 0 | { |
2231 | 0 | osOzi = CPLResetExtensionSafe(pszBaseFilename, "MAP"); |
2232 | 0 | fpOzi = VSIFOpenL(osOzi.c_str(), "rt"); |
2233 | 0 | } |
2234 | |
|
2235 | 0 | if (fpOzi == nullptr) |
2236 | 0 | return FALSE; |
2237 | | |
2238 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi)); |
2239 | | |
2240 | | /* -------------------------------------------------------------------- */ |
2241 | | /* We found the file, now load and parse it. */ |
2242 | | /* -------------------------------------------------------------------- */ |
2243 | 0 | return GDALLoadOziMapFile(osOzi.c_str(), padfGeoTransform, ppszWKT, |
2244 | 0 | pnGCPCount, ppasGCPs); |
2245 | 0 | } |
2246 | | |
2247 | | /************************************************************************/ |
2248 | | /* GDALLoadTabFile() */ |
2249 | | /* */ |
2250 | | /************************************************************************/ |
2251 | | |
2252 | | /** Helper function for translator implementer wanting support for MapInfo |
2253 | | * .tab files. |
2254 | | * |
2255 | | * @param pszFilename filename of .tab |
2256 | | * @param padfGeoTransform output geotransform. Must hold 6 doubles. |
2257 | | * @param ppszWKT output pointer to a string that will be allocated with |
2258 | | * CPLMalloc(). |
2259 | | * @param pnGCPCount output pointer to GCP count. |
2260 | | * @param ppasGCPs outputer pointer to an array of GCPs. |
2261 | | * @return TRUE in case of success, FALSE otherwise. |
2262 | | */ |
2263 | | int CPL_STDCALL GDALLoadTabFile(const char *pszFilename, |
2264 | | double *padfGeoTransform, char **ppszWKT, |
2265 | | int *pnGCPCount, GDAL_GCP **ppasGCPs) |
2266 | | |
2267 | 0 | { |
2268 | 0 | char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr); |
2269 | |
|
2270 | 0 | if (!papszLines) |
2271 | 0 | return FALSE; |
2272 | | |
2273 | 0 | char **papszTok = nullptr; |
2274 | 0 | bool bTypeRasterFound = false; |
2275 | 0 | bool bInsideTableDef = false; |
2276 | 0 | int nCoordinateCount = 0; |
2277 | 0 | GDAL_GCP asGCPs[256]; // TODO(schwehr): Initialize. |
2278 | 0 | const int numLines = CSLCount(papszLines); |
2279 | | |
2280 | | // Iterate all lines in the TAB-file |
2281 | 0 | for (int iLine = 0; iLine < numLines; iLine++) |
2282 | 0 | { |
2283 | 0 | CSLDestroy(papszTok); |
2284 | 0 | papszTok = |
2285 | 0 | CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE); |
2286 | |
|
2287 | 0 | if (CSLCount(papszTok) < 2) |
2288 | 0 | continue; |
2289 | | |
2290 | | // Did we find table definition |
2291 | 0 | if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table")) |
2292 | 0 | { |
2293 | 0 | bInsideTableDef = TRUE; |
2294 | 0 | } |
2295 | 0 | else if (bInsideTableDef && (EQUAL(papszTok[0], "Type"))) |
2296 | 0 | { |
2297 | | // Only RASTER-type will be handled |
2298 | 0 | if (EQUAL(papszTok[1], "RASTER")) |
2299 | 0 | { |
2300 | 0 | bTypeRasterFound = true; |
2301 | 0 | } |
2302 | 0 | else |
2303 | 0 | { |
2304 | 0 | CSLDestroy(papszTok); |
2305 | 0 | CSLDestroy(papszLines); |
2306 | 0 | return FALSE; |
2307 | 0 | } |
2308 | 0 | } |
2309 | 0 | else if (bTypeRasterFound && bInsideTableDef && |
2310 | 0 | CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") && |
2311 | 0 | nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs))) |
2312 | 0 | { |
2313 | 0 | GDALInitGCPs(1, asGCPs + nCoordinateCount); |
2314 | |
|
2315 | 0 | asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]); |
2316 | 0 | asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]); |
2317 | 0 | asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]); |
2318 | 0 | asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]); |
2319 | 0 | if (papszTok[5] != nullptr) |
2320 | 0 | { |
2321 | 0 | CPLFree(asGCPs[nCoordinateCount].pszId); |
2322 | 0 | asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]); |
2323 | 0 | } |
2324 | |
|
2325 | 0 | nCoordinateCount++; |
2326 | 0 | } |
2327 | 0 | else if (bTypeRasterFound && bInsideTableDef && |
2328 | 0 | EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr) |
2329 | 0 | { |
2330 | 0 | OGRSpatialReference oSRS; |
2331 | |
|
2332 | 0 | if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE) |
2333 | 0 | oSRS.exportToWkt(ppszWKT); |
2334 | 0 | } |
2335 | 0 | else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 && |
2336 | 0 | EQUAL(papszTok[1], "degree")) |
2337 | 0 | { |
2338 | | /* |
2339 | | ** If we have units of "degree", but a projected coordinate |
2340 | | ** system we need to convert it to geographic. See to01_02.TAB. |
2341 | | */ |
2342 | 0 | if (ppszWKT != nullptr && *ppszWKT != nullptr && |
2343 | 0 | STARTS_WITH_CI(*ppszWKT, "PROJCS")) |
2344 | 0 | { |
2345 | 0 | OGRSpatialReference oSRS; |
2346 | 0 | oSRS.importFromWkt(*ppszWKT); |
2347 | |
|
2348 | 0 | OGRSpatialReference oSRSGeogCS; |
2349 | 0 | oSRSGeogCS.CopyGeogCSFrom(&oSRS); |
2350 | 0 | CPLFree(*ppszWKT); |
2351 | |
|
2352 | 0 | oSRSGeogCS.exportToWkt(ppszWKT); |
2353 | 0 | } |
2354 | 0 | } |
2355 | 0 | } |
2356 | | |
2357 | 0 | CSLDestroy(papszTok); |
2358 | 0 | CSLDestroy(papszLines); |
2359 | |
|
2360 | 0 | if (nCoordinateCount == 0) |
2361 | 0 | { |
2362 | 0 | CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.", |
2363 | 0 | pszFilename); |
2364 | 0 | return FALSE; |
2365 | 0 | } |
2366 | | |
2367 | | /* -------------------------------------------------------------------- */ |
2368 | | /* Try to convert the GCPs into a geotransform definition, if */ |
2369 | | /* possible. Otherwise we will need to use them as GCPs. */ |
2370 | | /* -------------------------------------------------------------------- */ |
2371 | 0 | if (!GDALGCPsToGeoTransform( |
2372 | 0 | nCoordinateCount, asGCPs, padfGeoTransform, |
2373 | 0 | CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO")))) |
2374 | 0 | { |
2375 | 0 | if (pnGCPCount && ppasGCPs) |
2376 | 0 | { |
2377 | 0 | CPLDebug("GDAL", |
2378 | 0 | "GDALLoadTabFile(%s) found file, was not able to derive a " |
2379 | 0 | "first order geotransform. Using points as GCPs.", |
2380 | 0 | pszFilename); |
2381 | |
|
2382 | 0 | *ppasGCPs = static_cast<GDAL_GCP *>( |
2383 | 0 | CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount)); |
2384 | 0 | memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount); |
2385 | 0 | *pnGCPCount = nCoordinateCount; |
2386 | 0 | } |
2387 | 0 | } |
2388 | 0 | else |
2389 | 0 | { |
2390 | 0 | GDALDeinitGCPs(nCoordinateCount, asGCPs); |
2391 | 0 | } |
2392 | |
|
2393 | 0 | return TRUE; |
2394 | 0 | } |
2395 | | |
2396 | | /************************************************************************/ |
2397 | | /* GDALReadTabFile() */ |
2398 | | /************************************************************************/ |
2399 | | |
2400 | | /** Helper function for translator implementer wanting support for MapInfo |
2401 | | * .tab files. |
2402 | | * |
2403 | | * @param pszBaseFilename filename whose basename will help building the .tab |
2404 | | * filename. |
2405 | | * @param padfGeoTransform output geotransform. Must hold 6 doubles. |
2406 | | * @param ppszWKT output pointer to a string that will be allocated with |
2407 | | * CPLMalloc(). |
2408 | | * @param pnGCPCount output pointer to GCP count. |
2409 | | * @param ppasGCPs outputer pointer to an array of GCPs. |
2410 | | * @return TRUE in case of success, FALSE otherwise. |
2411 | | */ |
2412 | | int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename, |
2413 | | double *padfGeoTransform, char **ppszWKT, |
2414 | | int *pnGCPCount, GDAL_GCP **ppasGCPs) |
2415 | | |
2416 | 0 | { |
2417 | 0 | return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT, |
2418 | 0 | pnGCPCount, ppasGCPs, nullptr, nullptr); |
2419 | 0 | } |
2420 | | |
2421 | | int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform, |
2422 | | char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs, |
2423 | | CSLConstList papszSiblingFiles, char **ppszTabFileNameOut) |
2424 | 0 | { |
2425 | 0 | if (ppszTabFileNameOut) |
2426 | 0 | *ppszTabFileNameOut = nullptr; |
2427 | |
|
2428 | 0 | if (!GDALCanFileAcceptSidecarFile(pszBaseFilename)) |
2429 | 0 | return FALSE; |
2430 | | |
2431 | 0 | std::string osTAB = CPLResetExtensionSafe(pszBaseFilename, "tab"); |
2432 | |
|
2433 | 0 | if (papszSiblingFiles && |
2434 | | // cppcheck-suppress knownConditionTrueFalse |
2435 | 0 | GDALCanReliablyUseSiblingFileList(osTAB.c_str())) |
2436 | 0 | { |
2437 | 0 | int iSibling = |
2438 | 0 | CSLFindString(papszSiblingFiles, CPLGetFilename(osTAB.c_str())); |
2439 | 0 | if (iSibling >= 0) |
2440 | 0 | { |
2441 | 0 | CPLString osTabFilename = pszBaseFilename; |
2442 | 0 | osTabFilename.resize(strlen(pszBaseFilename) - |
2443 | 0 | strlen(CPLGetFilename(pszBaseFilename))); |
2444 | 0 | osTabFilename += papszSiblingFiles[iSibling]; |
2445 | 0 | if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT, |
2446 | 0 | pnGCPCount, ppasGCPs)) |
2447 | 0 | { |
2448 | 0 | if (ppszTabFileNameOut) |
2449 | 0 | *ppszTabFileNameOut = CPLStrdup(osTabFilename); |
2450 | 0 | return TRUE; |
2451 | 0 | } |
2452 | 0 | } |
2453 | 0 | return FALSE; |
2454 | 0 | } |
2455 | | |
2456 | | /* -------------------------------------------------------------------- */ |
2457 | | /* Try lower case, then upper case. */ |
2458 | | /* -------------------------------------------------------------------- */ |
2459 | | |
2460 | 0 | VSILFILE *fpTAB = VSIFOpenL(osTAB.c_str(), "rt"); |
2461 | |
|
2462 | 0 | if (fpTAB == nullptr && VSIIsCaseSensitiveFS(osTAB.c_str())) |
2463 | 0 | { |
2464 | 0 | osTAB = CPLResetExtensionSafe(pszBaseFilename, "TAB"); |
2465 | 0 | fpTAB = VSIFOpenL(osTAB.c_str(), "rt"); |
2466 | 0 | } |
2467 | |
|
2468 | 0 | if (fpTAB == nullptr) |
2469 | 0 | return FALSE; |
2470 | | |
2471 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB)); |
2472 | | |
2473 | | /* -------------------------------------------------------------------- */ |
2474 | | /* We found the file, now load and parse it. */ |
2475 | | /* -------------------------------------------------------------------- */ |
2476 | 0 | if (GDALLoadTabFile(osTAB.c_str(), padfGeoTransform, ppszWKT, pnGCPCount, |
2477 | 0 | ppasGCPs)) |
2478 | 0 | { |
2479 | 0 | if (ppszTabFileNameOut) |
2480 | 0 | *ppszTabFileNameOut = CPLStrdup(osTAB.c_str()); |
2481 | 0 | return TRUE; |
2482 | 0 | } |
2483 | 0 | return FALSE; |
2484 | 0 | } |
2485 | | |
2486 | | /************************************************************************/ |
2487 | | /* GDALLoadWorldFile() */ |
2488 | | /************************************************************************/ |
2489 | | |
2490 | | /** |
2491 | | * \brief Read ESRI world file. |
2492 | | * |
2493 | | * This function reads an ESRI style world file, and formats a geotransform |
2494 | | * from its contents. |
2495 | | * |
2496 | | * The world file contains an affine transformation with the parameters |
2497 | | * in a different order than in a geotransform array. |
2498 | | * |
2499 | | * <ul> |
2500 | | * <li> geotransform[1] : width of pixel |
2501 | | * <li> geotransform[4] : rotational coefficient, zero for north up images. |
2502 | | * <li> geotransform[2] : rotational coefficient, zero for north up images. |
2503 | | * <li> geotransform[5] : height of pixel (but negative) |
2504 | | * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x |
2505 | | * offset to center of top left pixel. <li> geotransform[3] + 0.5 * |
2506 | | * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left |
2507 | | * pixel. |
2508 | | * </ul> |
2509 | | * |
2510 | | * @param pszFilename the world file name. |
2511 | | * @param padfGeoTransform the six double array into which the |
2512 | | * geotransformation should be placed. |
2513 | | * |
2514 | | * @return TRUE on success or FALSE on failure. |
2515 | | */ |
2516 | | |
2517 | | int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename, |
2518 | | double *padfGeoTransform) |
2519 | | |
2520 | 0 | { |
2521 | 0 | VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE); |
2522 | 0 | VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE); |
2523 | | |
2524 | 0 | char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr); |
2525 | |
|
2526 | 0 | if (!papszLines) |
2527 | 0 | return FALSE; |
2528 | | |
2529 | 0 | double world[6] = {0.0}; |
2530 | | // reads the first 6 non-empty lines |
2531 | 0 | int nLines = 0; |
2532 | 0 | const int nLinesCount = CSLCount(papszLines); |
2533 | 0 | for (int i = 0; |
2534 | 0 | i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world)); |
2535 | 0 | ++i) |
2536 | 0 | { |
2537 | 0 | CPLString line(papszLines[i]); |
2538 | 0 | if (line.Trim().empty()) |
2539 | 0 | continue; |
2540 | | |
2541 | 0 | world[nLines] = CPLAtofM(line); |
2542 | 0 | ++nLines; |
2543 | 0 | } |
2544 | |
|
2545 | 0 | if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) && |
2546 | 0 | (world[3] != 0.0 || world[1] != 0.0)) |
2547 | 0 | { |
2548 | 0 | padfGeoTransform[0] = world[4]; |
2549 | 0 | padfGeoTransform[1] = world[0]; |
2550 | 0 | padfGeoTransform[2] = world[2]; |
2551 | 0 | padfGeoTransform[3] = world[5]; |
2552 | 0 | padfGeoTransform[4] = world[1]; |
2553 | 0 | padfGeoTransform[5] = world[3]; |
2554 | | |
2555 | | // correct for center of pixel vs. top left of pixel |
2556 | 0 | padfGeoTransform[0] -= 0.5 * padfGeoTransform[1]; |
2557 | 0 | padfGeoTransform[0] -= 0.5 * padfGeoTransform[2]; |
2558 | 0 | padfGeoTransform[3] -= 0.5 * padfGeoTransform[4]; |
2559 | 0 | padfGeoTransform[3] -= 0.5 * padfGeoTransform[5]; |
2560 | |
|
2561 | 0 | CSLDestroy(papszLines); |
2562 | |
|
2563 | 0 | return TRUE; |
2564 | 0 | } |
2565 | 0 | else |
2566 | 0 | { |
2567 | 0 | CPLDebug("GDAL", |
2568 | 0 | "GDALLoadWorldFile(%s) found file, but it was corrupt.", |
2569 | 0 | pszFilename); |
2570 | 0 | CSLDestroy(papszLines); |
2571 | 0 | return FALSE; |
2572 | 0 | } |
2573 | 0 | } |
2574 | | |
2575 | | /************************************************************************/ |
2576 | | /* GDALReadWorldFile() */ |
2577 | | /************************************************************************/ |
2578 | | |
2579 | | /** |
2580 | | * \brief Read ESRI world file. |
2581 | | * |
2582 | | * This function reads an ESRI style world file, and formats a geotransform |
2583 | | * from its contents. It does the same as GDALLoadWorldFile() function, but |
2584 | | * it will form the filename for the worldfile from the filename of the raster |
2585 | | * file referred and the suggested extension. If no extension is provided, |
2586 | | * the code will internally try the unix style and windows style world file |
2587 | | * extensions (eg. for .tif these would be .tfw and .tifw). |
2588 | | * |
2589 | | * The world file contains an affine transformation with the parameters |
2590 | | * in a different order than in a geotransform array. |
2591 | | * |
2592 | | * <ul> |
2593 | | * <li> geotransform[1] : width of pixel |
2594 | | * <li> geotransform[4] : rotational coefficient, zero for north up images. |
2595 | | * <li> geotransform[2] : rotational coefficient, zero for north up images. |
2596 | | * <li> geotransform[5] : height of pixel (but negative) |
2597 | | * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x |
2598 | | * offset to center of top left pixel. <li> geotransform[3] + 0.5 * |
2599 | | * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left |
2600 | | * pixel. |
2601 | | * </ul> |
2602 | | * |
2603 | | * @param pszBaseFilename the target raster file. |
2604 | | * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it |
2605 | | * from the pszBaseFilename |
2606 | | * @param padfGeoTransform the six double array into which the |
2607 | | * geotransformation should be placed. |
2608 | | * |
2609 | | * @return TRUE on success or FALSE on failure. |
2610 | | */ |
2611 | | |
2612 | | int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename, |
2613 | | const char *pszExtension, |
2614 | | double *padfGeoTransform) |
2615 | | |
2616 | 0 | { |
2617 | 0 | return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform, |
2618 | 0 | nullptr, nullptr); |
2619 | 0 | } |
2620 | | |
2621 | | int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension, |
2622 | | double *padfGeoTransform, CSLConstList papszSiblingFiles, |
2623 | | char **ppszWorldFileNameOut) |
2624 | 0 | { |
2625 | 0 | VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE); |
2626 | 0 | VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE); |
2627 | | |
2628 | 0 | if (ppszWorldFileNameOut) |
2629 | 0 | *ppszWorldFileNameOut = nullptr; |
2630 | |
|
2631 | 0 | if (!GDALCanFileAcceptSidecarFile(pszBaseFilename)) |
2632 | 0 | return FALSE; |
2633 | | |
2634 | | /* -------------------------------------------------------------------- */ |
2635 | | /* If we aren't given an extension, try both the unix and */ |
2636 | | /* windows style extensions. */ |
2637 | | /* -------------------------------------------------------------------- */ |
2638 | 0 | if (pszExtension == nullptr) |
2639 | 0 | { |
2640 | 0 | const std::string oBaseExt = CPLGetExtensionSafe(pszBaseFilename); |
2641 | |
|
2642 | 0 | if (oBaseExt.length() < 2) |
2643 | 0 | return FALSE; |
2644 | | |
2645 | | // windows version - first + last + 'w' |
2646 | 0 | char szDerivedExtension[100] = {'\0'}; |
2647 | 0 | szDerivedExtension[0] = oBaseExt[0]; |
2648 | 0 | szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1]; |
2649 | 0 | szDerivedExtension[2] = 'w'; |
2650 | 0 | szDerivedExtension[3] = '\0'; |
2651 | |
|
2652 | 0 | if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension, |
2653 | 0 | padfGeoTransform, papszSiblingFiles, |
2654 | 0 | ppszWorldFileNameOut)) |
2655 | 0 | return TRUE; |
2656 | | |
2657 | | // unix version - extension + 'w' |
2658 | 0 | if (oBaseExt.length() > sizeof(szDerivedExtension) - 2) |
2659 | 0 | return FALSE; |
2660 | | |
2661 | 0 | snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw", |
2662 | 0 | oBaseExt.c_str()); |
2663 | 0 | return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension, |
2664 | 0 | padfGeoTransform, papszSiblingFiles, |
2665 | 0 | ppszWorldFileNameOut); |
2666 | 0 | } |
2667 | | |
2668 | | /* -------------------------------------------------------------------- */ |
2669 | | /* Skip the leading period in the extension if there is one. */ |
2670 | | /* -------------------------------------------------------------------- */ |
2671 | 0 | if (*pszExtension == '.') |
2672 | 0 | pszExtension++; |
2673 | | |
2674 | | /* -------------------------------------------------------------------- */ |
2675 | | /* Generate upper and lower case versions of the extension. */ |
2676 | | /* -------------------------------------------------------------------- */ |
2677 | 0 | char szExtUpper[32] = {'\0'}; |
2678 | 0 | char szExtLower[32] = {'\0'}; |
2679 | 0 | CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper)); |
2680 | 0 | CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower)); |
2681 | |
|
2682 | 0 | for (int i = 0; szExtUpper[i] != '\0'; i++) |
2683 | 0 | { |
2684 | 0 | szExtUpper[i] = static_cast<char>( |
2685 | 0 | CPLToupper(static_cast<unsigned char>(szExtUpper[i]))); |
2686 | 0 | szExtLower[i] = static_cast<char>( |
2687 | 0 | CPLTolower(static_cast<unsigned char>(szExtLower[i]))); |
2688 | 0 | } |
2689 | |
|
2690 | 0 | std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower); |
2691 | |
|
2692 | 0 | if (papszSiblingFiles && |
2693 | | // cppcheck-suppress knownConditionTrueFalse |
2694 | 0 | GDALCanReliablyUseSiblingFileList(osTFW.c_str())) |
2695 | 0 | { |
2696 | 0 | const int iSibling = |
2697 | 0 | CSLFindString(papszSiblingFiles, CPLGetFilename(osTFW.c_str())); |
2698 | 0 | if (iSibling >= 0) |
2699 | 0 | { |
2700 | 0 | CPLString osTFWFilename = pszBaseFilename; |
2701 | 0 | osTFWFilename.resize(strlen(pszBaseFilename) - |
2702 | 0 | strlen(CPLGetFilename(pszBaseFilename))); |
2703 | 0 | osTFWFilename += papszSiblingFiles[iSibling]; |
2704 | 0 | if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform)) |
2705 | 0 | { |
2706 | 0 | if (ppszWorldFileNameOut) |
2707 | 0 | *ppszWorldFileNameOut = CPLStrdup(osTFWFilename); |
2708 | 0 | return TRUE; |
2709 | 0 | } |
2710 | 0 | } |
2711 | 0 | return FALSE; |
2712 | 0 | } |
2713 | | |
2714 | | /* -------------------------------------------------------------------- */ |
2715 | | /* Try lower case, then upper case. */ |
2716 | | /* -------------------------------------------------------------------- */ |
2717 | | |
2718 | 0 | VSIStatBufL sStatBuf; |
2719 | 0 | bool bGotTFW = |
2720 | 0 | VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0; |
2721 | |
|
2722 | 0 | if (!bGotTFW && VSIIsCaseSensitiveFS(osTFW.c_str())) |
2723 | 0 | { |
2724 | 0 | osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtUpper); |
2725 | 0 | bGotTFW = |
2726 | 0 | VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0; |
2727 | 0 | } |
2728 | |
|
2729 | 0 | if (!bGotTFW) |
2730 | 0 | return FALSE; |
2731 | | |
2732 | | /* -------------------------------------------------------------------- */ |
2733 | | /* We found the file, now load and parse it. */ |
2734 | | /* -------------------------------------------------------------------- */ |
2735 | 0 | if (GDALLoadWorldFile(osTFW.c_str(), padfGeoTransform)) |
2736 | 0 | { |
2737 | 0 | if (ppszWorldFileNameOut) |
2738 | 0 | *ppszWorldFileNameOut = CPLStrdup(osTFW.c_str()); |
2739 | 0 | return TRUE; |
2740 | 0 | } |
2741 | 0 | return FALSE; |
2742 | 0 | } |
2743 | | |
2744 | | /************************************************************************/ |
2745 | | /* GDALWriteWorldFile() */ |
2746 | | /* */ |
2747 | | /* Helper function for translator implementer wanting */ |
2748 | | /* support for ESRI world files. */ |
2749 | | /************************************************************************/ |
2750 | | |
2751 | | /** |
2752 | | * \brief Write ESRI world file. |
2753 | | * |
2754 | | * This function writes an ESRI style world file from the passed geotransform. |
2755 | | * |
2756 | | * The world file contains an affine transformation with the parameters |
2757 | | * in a different order than in a geotransform array. |
2758 | | * |
2759 | | * <ul> |
2760 | | * <li> geotransform[1] : width of pixel |
2761 | | * <li> geotransform[4] : rotational coefficient, zero for north up images. |
2762 | | * <li> geotransform[2] : rotational coefficient, zero for north up images. |
2763 | | * <li> geotransform[5] : height of pixel (but negative) |
2764 | | * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x |
2765 | | * offset to center of top left pixel. <li> geotransform[3] + 0.5 * |
2766 | | * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left |
2767 | | * pixel. |
2768 | | * </ul> |
2769 | | * |
2770 | | * @param pszBaseFilename the target raster file. |
2771 | | * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL |
2772 | | * @param padfGeoTransform the six double array from which the |
2773 | | * geotransformation should be read. |
2774 | | * |
2775 | | * @return TRUE on success or FALSE on failure. |
2776 | | */ |
2777 | | |
2778 | | int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename, |
2779 | | const char *pszExtension, |
2780 | | double *padfGeoTransform) |
2781 | | |
2782 | 0 | { |
2783 | 0 | VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE); |
2784 | 0 | VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE); |
2785 | 0 | VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE); |
2786 | | |
2787 | | /* -------------------------------------------------------------------- */ |
2788 | | /* Prepare the text to write to the file. */ |
2789 | | /* -------------------------------------------------------------------- */ |
2790 | 0 | CPLString osTFWText; |
2791 | |
|
2792 | 0 | osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n", |
2793 | 0 | padfGeoTransform[1], padfGeoTransform[4], |
2794 | 0 | padfGeoTransform[2], padfGeoTransform[5], |
2795 | 0 | padfGeoTransform[0] + 0.5 * padfGeoTransform[1] + |
2796 | 0 | 0.5 * padfGeoTransform[2], |
2797 | 0 | padfGeoTransform[3] + 0.5 * padfGeoTransform[4] + |
2798 | 0 | 0.5 * padfGeoTransform[5]); |
2799 | | |
2800 | | /* -------------------------------------------------------------------- */ |
2801 | | /* Update extension, and write to disk. */ |
2802 | | /* -------------------------------------------------------------------- */ |
2803 | 0 | const std::string osTFW = |
2804 | 0 | CPLResetExtensionSafe(pszBaseFilename, pszExtension); |
2805 | 0 | VSILFILE *const fpTFW = VSIFOpenL(osTFW.c_str(), "wt"); |
2806 | 0 | if (fpTFW == nullptr) |
2807 | 0 | return FALSE; |
2808 | | |
2809 | 0 | const int bRet = |
2810 | 0 | VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1; |
2811 | 0 | if (VSIFCloseL(fpTFW) != 0) |
2812 | 0 | return FALSE; |
2813 | | |
2814 | 0 | return bRet; |
2815 | 0 | } |
2816 | | |
2817 | | /************************************************************************/ |
2818 | | /* GDALVersionInfo() */ |
2819 | | /************************************************************************/ |
2820 | | |
2821 | | /** |
2822 | | * \brief Get runtime version information. |
2823 | | * |
2824 | | * Available pszRequest values: |
2825 | | * <ul> |
2826 | | * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. i.e. |
2827 | | * "30603000", e.g for GDAL 3.6.3.0</li> |
2828 | | * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a |
2829 | | * string. i.e. "20230312".</li> |
2830 | | * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li> |
2831 | | * <li> "RELEASE_NICKNAME": (>= 3.11) Returns the GDAL_RELEASE_NICKNAME. |
2832 | | * (may be empty)</li> |
2833 | | * <li> "\--version": Returns one line version message suitable for |
2834 | | * use in response to \--version requests. i.e. "GDAL 3.6.3, released |
2835 | | * 2023/03/12"</li> |
2836 | | * <li> "LICENSE": Returns the content of the LICENSE.TXT file from |
2837 | | * the GDAL_DATA directory. |
2838 | | * </li> |
2839 | | * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines |
2840 | | * with information on build time options.</li> |
2841 | | * </ul> |
2842 | | * |
2843 | | * @param pszRequest the type of version info desired, as listed above. |
2844 | | * |
2845 | | * @return an internal string containing the requested information. |
2846 | | */ |
2847 | | |
2848 | | const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest) |
2849 | | |
2850 | 0 | { |
2851 | | /* -------------------------------------------------------------------- */ |
2852 | | /* Try to capture as much build information as practical. */ |
2853 | | /* -------------------------------------------------------------------- */ |
2854 | 0 | if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO")) |
2855 | 0 | { |
2856 | 0 | CPLString osBuildInfo; |
2857 | |
|
2858 | 0 | #define STRINGIFY_HELPER(x) #x |
2859 | 0 | #define STRINGIFY(x) STRINGIFY_HELPER(x) |
2860 | |
|
2861 | | #ifdef ESRI_BUILD |
2862 | | osBuildInfo += "ESRI_BUILD=YES\n"; |
2863 | | #endif |
2864 | | #ifdef PAM_ENABLED |
2865 | | osBuildInfo += "PAM_ENABLED=YES\n"; |
2866 | | #endif |
2867 | 0 | osBuildInfo += "OGR_ENABLED=YES\n"; // Deprecated. Always yes. |
2868 | | #ifdef HAVE_CURL |
2869 | | osBuildInfo += "CURL_ENABLED=YES\n"; |
2870 | | osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n"; |
2871 | | #endif |
2872 | | #ifdef HAVE_GEOS |
2873 | | osBuildInfo += "GEOS_ENABLED=YES\n"; |
2874 | | #ifdef GEOS_CAPI_VERSION |
2875 | | osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n"; |
2876 | | #endif |
2877 | | #endif |
2878 | 0 | osBuildInfo += |
2879 | 0 | "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY( |
2880 | 0 | PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n"; |
2881 | 0 | osBuildInfo += "PROJ_RUNTIME_VERSION="; |
2882 | 0 | osBuildInfo += proj_info().version; |
2883 | 0 | osBuildInfo += '\n'; |
2884 | |
|
2885 | 0 | #ifdef __VERSION__ |
2886 | 0 | #ifdef __clang_version__ |
2887 | 0 | osBuildInfo += "COMPILER=clang " __clang_version__ "\n"; |
2888 | | #elif defined(__GNUC__) |
2889 | | osBuildInfo += "COMPILER=GCC " __VERSION__ "\n"; |
2890 | | #elif defined(__INTEL_COMPILER) |
2891 | | osBuildInfo += "COMPILER=" __VERSION__ "\n"; |
2892 | | #else |
2893 | | // STRINGIFY() as we're not sure if its a int or a string |
2894 | | osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n"; |
2895 | | #endif |
2896 | | #elif defined(_MSC_FULL_VER) |
2897 | | osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n"; |
2898 | | #elif defined(__INTEL_COMPILER) |
2899 | | osBuildInfo += |
2900 | | "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n"; |
2901 | | #endif |
2902 | | #ifdef CMAKE_UNITY_BUILD |
2903 | | osBuildInfo += "CMAKE_UNITY_BUILD=YES\n"; |
2904 | | #endif |
2905 | | #ifdef EMBED_RESOURCE_FILES |
2906 | | osBuildInfo += "EMBED_RESOURCE_FILES=YES\n"; |
2907 | | #endif |
2908 | | #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES |
2909 | | osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n"; |
2910 | | #endif |
2911 | |
|
2912 | 0 | #undef STRINGIFY_HELPER |
2913 | 0 | #undef STRINGIFY |
2914 | |
|
2915 | 0 | CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); |
2916 | 0 | CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE); |
2917 | 0 | return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO)); |
2918 | 0 | } |
2919 | | |
2920 | | /* -------------------------------------------------------------------- */ |
2921 | | /* LICENSE is a special case. We try to find and read the */ |
2922 | | /* LICENSE.TXT file from the GDAL_DATA directory and return it */ |
2923 | | /* -------------------------------------------------------------------- */ |
2924 | 0 | if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE")) |
2925 | 0 | { |
2926 | | #if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES) |
2927 | | return GDALGetEmbeddedLicense(); |
2928 | | #else |
2929 | 0 | char *pszResultLicence = |
2930 | 0 | reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE)); |
2931 | 0 | if (pszResultLicence != nullptr) |
2932 | 0 | { |
2933 | 0 | return pszResultLicence; |
2934 | 0 | } |
2935 | | |
2936 | 0 | VSILFILE *fp = nullptr; |
2937 | 0 | #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES |
2938 | | #ifdef EMBED_RESOURCE_FILES |
2939 | | CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler); |
2940 | | #endif |
2941 | 0 | const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT"); |
2942 | 0 | if (pszFilename != nullptr) |
2943 | 0 | fp = VSIFOpenL(pszFilename, "r"); |
2944 | 0 | if (fp != nullptr) |
2945 | 0 | { |
2946 | 0 | if (VSIFSeekL(fp, 0, SEEK_END) == 0) |
2947 | 0 | { |
2948 | | // TODO(schwehr): Handle if VSITellL returns a value too large |
2949 | | // for size_t. |
2950 | 0 | const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1); |
2951 | 0 | if (VSIFSeekL(fp, SEEK_SET, 0) == 0) |
2952 | 0 | { |
2953 | 0 | pszResultLicence = |
2954 | 0 | static_cast<char *>(VSICalloc(1, nLength)); |
2955 | 0 | if (pszResultLicence) |
2956 | 0 | CPL_IGNORE_RET_VAL( |
2957 | 0 | VSIFReadL(pszResultLicence, 1, nLength - 1, fp)); |
2958 | 0 | } |
2959 | 0 | } |
2960 | |
|
2961 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
2962 | 0 | } |
2963 | 0 | #endif |
2964 | |
|
2965 | | #ifdef EMBED_RESOURCE_FILES |
2966 | | if (!fp) |
2967 | | { |
2968 | | return GDALGetEmbeddedLicense(); |
2969 | | } |
2970 | | #endif |
2971 | |
|
2972 | 0 | if (!pszResultLicence) |
2973 | 0 | { |
2974 | 0 | pszResultLicence = |
2975 | 0 | CPLStrdup("GDAL/OGR is released under the MIT license.\n" |
2976 | 0 | "The LICENSE.TXT distributed with GDAL/OGR should\n" |
2977 | 0 | "contain additional details.\n"); |
2978 | 0 | } |
2979 | |
|
2980 | 0 | CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE); |
2981 | 0 | return pszResultLicence; |
2982 | 0 | #endif |
2983 | 0 | } |
2984 | | |
2985 | | /* -------------------------------------------------------------------- */ |
2986 | | /* All other strings are fairly small. */ |
2987 | | /* -------------------------------------------------------------------- */ |
2988 | 0 | CPLString osVersionInfo; |
2989 | |
|
2990 | 0 | if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM")) |
2991 | 0 | osVersionInfo.Printf("%d", GDAL_VERSION_NUM); |
2992 | 0 | else if (EQUAL(pszRequest, "RELEASE_DATE")) |
2993 | 0 | osVersionInfo.Printf("%d", GDAL_RELEASE_DATE); |
2994 | 0 | else if (EQUAL(pszRequest, "RELEASE_NAME")) |
2995 | 0 | osVersionInfo.Printf(GDAL_RELEASE_NAME); |
2996 | 0 | else if (EQUAL(pszRequest, "RELEASE_NICKNAME")) |
2997 | 0 | osVersionInfo.Printf("%s", GDAL_RELEASE_NICKNAME); |
2998 | 0 | else // --version |
2999 | 0 | { |
3000 | 0 | osVersionInfo = "GDAL " GDAL_RELEASE_NAME; |
3001 | 0 | if (GDAL_RELEASE_NICKNAME[0]) |
3002 | 0 | { |
3003 | 0 | osVersionInfo += " \"" GDAL_RELEASE_NICKNAME "\""; |
3004 | 0 | } |
3005 | 0 | osVersionInfo += CPLString().Printf( |
3006 | 0 | ", released %d/%02d/%02d", GDAL_RELEASE_DATE / 10000, |
3007 | 0 | (GDAL_RELEASE_DATE % 10000) / 100, GDAL_RELEASE_DATE % 100); |
3008 | | #if defined(__GNUC__) && !defined(__OPTIMIZE__) |
3009 | | // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html |
3010 | | // also true for CLang |
3011 | | osVersionInfo += " (debug build)"; |
3012 | | #elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2 |
3013 | | // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170 |
3014 | | // In release mode, the compiler generates an error if you specify |
3015 | | // _ITERATOR_DEBUG_LEVEL as 2. |
3016 | | osVersionInfo += " (debug build)"; |
3017 | | #endif |
3018 | 0 | } |
3019 | |
|
3020 | 0 | CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); // clear old value. |
3021 | 0 | CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE); |
3022 | 0 | return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO)); |
3023 | 0 | } |
3024 | | |
3025 | | /************************************************************************/ |
3026 | | /* GDALCheckVersion() */ |
3027 | | /************************************************************************/ |
3028 | | |
3029 | | /** Return TRUE if GDAL library version at runtime matches |
3030 | | nVersionMajor.nVersionMinor. |
3031 | | |
3032 | | The purpose of this method is to ensure that calling code will run |
3033 | | with the GDAL version it is compiled for. It is primarily intended |
3034 | | for external plugins. |
3035 | | |
3036 | | @param nVersionMajor Major version to be tested against |
3037 | | @param nVersionMinor Minor version to be tested against |
3038 | | @param pszCallingComponentName If not NULL, in case of version mismatch, the |
3039 | | method will issue a failure mentioning the name of the calling component. |
3040 | | |
3041 | | @return TRUE if GDAL library version at runtime matches |
3042 | | nVersionMajor.nVersionMinor, FALSE otherwise. |
3043 | | */ |
3044 | | int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor, |
3045 | | const char *pszCallingComponentName) |
3046 | 0 | { |
3047 | 0 | if (nVersionMajor == GDAL_VERSION_MAJOR && |
3048 | 0 | nVersionMinor == GDAL_VERSION_MINOR) |
3049 | 0 | return TRUE; |
3050 | | |
3051 | 0 | if (pszCallingComponentName) |
3052 | 0 | { |
3053 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3054 | 0 | "%s was compiled against GDAL %d.%d, but " |
3055 | 0 | "the current library version is %d.%d", |
3056 | 0 | pszCallingComponentName, nVersionMajor, nVersionMinor, |
3057 | 0 | GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR); |
3058 | 0 | } |
3059 | 0 | return FALSE; |
3060 | 0 | } |
3061 | | |
3062 | | /************************************************************************/ |
3063 | | /* GDALDecToDMS() */ |
3064 | | /************************************************************************/ |
3065 | | |
3066 | | /** Translate a decimal degrees value to a DMS string with hemisphere. |
3067 | | */ |
3068 | | const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis, |
3069 | | int nPrecision) |
3070 | | |
3071 | 0 | { |
3072 | 0 | return CPLDecToDMS(dfAngle, pszAxis, nPrecision); |
3073 | 0 | } |
3074 | | |
3075 | | /************************************************************************/ |
3076 | | /* GDALPackedDMSToDec() */ |
3077 | | /************************************************************************/ |
3078 | | |
3079 | | /** |
3080 | | * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees. |
3081 | | * |
3082 | | * See CPLPackedDMSToDec(). |
3083 | | */ |
3084 | | |
3085 | | double CPL_STDCALL GDALPackedDMSToDec(double dfPacked) |
3086 | | |
3087 | 0 | { |
3088 | 0 | return CPLPackedDMSToDec(dfPacked); |
3089 | 0 | } |
3090 | | |
3091 | | /************************************************************************/ |
3092 | | /* GDALDecToPackedDMS() */ |
3093 | | /************************************************************************/ |
3094 | | |
3095 | | /** |
3096 | | * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS). |
3097 | | * |
3098 | | * See CPLDecToPackedDMS(). |
3099 | | */ |
3100 | | |
3101 | | double CPL_STDCALL GDALDecToPackedDMS(double dfDec) |
3102 | | |
3103 | 0 | { |
3104 | 0 | return CPLDecToPackedDMS(dfDec); |
3105 | 0 | } |
3106 | | |
3107 | | /************************************************************************/ |
3108 | | /* GDALGCPsToGeoTransform() */ |
3109 | | /************************************************************************/ |
3110 | | |
3111 | | /** |
3112 | | * \brief Generate Geotransform from GCPs. |
3113 | | * |
3114 | | * Given a set of GCPs perform first order fit as a geotransform. |
3115 | | * |
3116 | | * Due to imprecision in the calculations the fit algorithm will often |
3117 | | * return non-zero rotational coefficients even if given perfectly non-rotated |
3118 | | * inputs. A special case has been implemented for corner corner coordinates |
3119 | | * given in TL, TR, BR, BL order. So when using this to get a geotransform |
3120 | | * from 4 corner coordinates, pass them in this order. |
3121 | | * |
3122 | | * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the |
3123 | | * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If |
3124 | | * set to YES, then bApproxOK will be overridden with TRUE. |
3125 | | * Starting with GDAL 2.2.2, when exact fit is asked, the |
3126 | | * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to |
3127 | | * give the maximum error threshold in pixel. The default is 0.25. |
3128 | | * |
3129 | | * @param nGCPCount the number of GCPs being passed in. |
3130 | | * @param pasGCPs the list of GCP structures. |
3131 | | * @param padfGeoTransform the six double array in which the affine |
3132 | | * geotransformation will be returned. |
3133 | | * @param bApproxOK If FALSE the function will fail if the geotransform is not |
3134 | | * essentially an exact fit (within 0.25 pixel) for all GCPs. |
3135 | | * |
3136 | | * @return TRUE on success or FALSE if there aren't enough points to prepare a |
3137 | | * geotransform, the pointers are ill-determined or if bApproxOK is FALSE |
3138 | | * and the fit is poor. |
3139 | | */ |
3140 | | |
3141 | | // TODO(schwehr): Add consts to args. |
3142 | | int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs, |
3143 | | double *padfGeoTransform, int bApproxOK) |
3144 | | |
3145 | 0 | { |
3146 | 0 | double dfPixelThreshold = 0.25; |
3147 | 0 | if (!bApproxOK) |
3148 | 0 | { |
3149 | 0 | bApproxOK = CPLTestBool( |
3150 | 0 | CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO")); |
3151 | 0 | if (!bApproxOK) |
3152 | 0 | { |
3153 | 0 | dfPixelThreshold = std::clamp( |
3154 | 0 | CPLAtof(CPLGetConfigOption( |
3155 | 0 | "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25")), |
3156 | 0 | 0.0, std::numeric_limits<double>::max()); |
3157 | 0 | } |
3158 | 0 | } |
3159 | | |
3160 | | /* -------------------------------------------------------------------- */ |
3161 | | /* Recognise a few special cases. */ |
3162 | | /* -------------------------------------------------------------------- */ |
3163 | 0 | if (nGCPCount < 2) |
3164 | 0 | return FALSE; |
3165 | | |
3166 | 0 | if (nGCPCount == 2) |
3167 | 0 | { |
3168 | 0 | if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel || |
3169 | 0 | pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine) |
3170 | 0 | return FALSE; |
3171 | | |
3172 | 0 | padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) / |
3173 | 0 | (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel); |
3174 | 0 | padfGeoTransform[2] = 0.0; |
3175 | |
|
3176 | 0 | padfGeoTransform[4] = 0.0; |
3177 | 0 | padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) / |
3178 | 0 | (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine); |
3179 | |
|
3180 | 0 | padfGeoTransform[0] = pasGCPs[0].dfGCPX - |
3181 | 0 | pasGCPs[0].dfGCPPixel * padfGeoTransform[1] - |
3182 | 0 | pasGCPs[0].dfGCPLine * padfGeoTransform[2]; |
3183 | |
|
3184 | 0 | padfGeoTransform[3] = pasGCPs[0].dfGCPY - |
3185 | 0 | pasGCPs[0].dfGCPPixel * padfGeoTransform[4] - |
3186 | 0 | pasGCPs[0].dfGCPLine * padfGeoTransform[5]; |
3187 | |
|
3188 | 0 | return TRUE; |
3189 | 0 | } |
3190 | | |
3191 | | /* -------------------------------------------------------------------- */ |
3192 | | /* Special case of 4 corner coordinates of a non-rotated */ |
3193 | | /* image. The points must be in TL-TR-BR-BL order for now. */ |
3194 | | /* This case helps avoid some imprecision in the general */ |
3195 | | /* calculations. */ |
3196 | | /* -------------------------------------------------------------------- */ |
3197 | 0 | if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine && |
3198 | 0 | pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine && |
3199 | 0 | pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel && |
3200 | 0 | pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel && |
3201 | 0 | pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine && |
3202 | 0 | pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel && |
3203 | 0 | pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY && |
3204 | 0 | pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY && |
3205 | 0 | pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX && |
3206 | 0 | pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX && |
3207 | 0 | pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY && |
3208 | 0 | pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX) |
3209 | 0 | { |
3210 | 0 | padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) / |
3211 | 0 | (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel); |
3212 | 0 | padfGeoTransform[2] = 0.0; |
3213 | 0 | padfGeoTransform[4] = 0.0; |
3214 | 0 | padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) / |
3215 | 0 | (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine); |
3216 | |
|
3217 | 0 | padfGeoTransform[0] = |
3218 | 0 | pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1]; |
3219 | 0 | padfGeoTransform[3] = |
3220 | 0 | pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5]; |
3221 | 0 | return TRUE; |
3222 | 0 | } |
3223 | | |
3224 | | /* -------------------------------------------------------------------- */ |
3225 | | /* Compute source and destination ranges so we can normalize */ |
3226 | | /* the values to make the least squares computation more stable. */ |
3227 | | /* -------------------------------------------------------------------- */ |
3228 | 0 | double min_pixel = pasGCPs[0].dfGCPPixel; |
3229 | 0 | double max_pixel = pasGCPs[0].dfGCPPixel; |
3230 | 0 | double min_line = pasGCPs[0].dfGCPLine; |
3231 | 0 | double max_line = pasGCPs[0].dfGCPLine; |
3232 | 0 | double min_geox = pasGCPs[0].dfGCPX; |
3233 | 0 | double max_geox = pasGCPs[0].dfGCPX; |
3234 | 0 | double min_geoy = pasGCPs[0].dfGCPY; |
3235 | 0 | double max_geoy = pasGCPs[0].dfGCPY; |
3236 | |
|
3237 | 0 | for (int i = 1; i < nGCPCount; ++i) |
3238 | 0 | { |
3239 | 0 | min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel); |
3240 | 0 | max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel); |
3241 | 0 | min_line = std::min(min_line, pasGCPs[i].dfGCPLine); |
3242 | 0 | max_line = std::max(max_line, pasGCPs[i].dfGCPLine); |
3243 | 0 | min_geox = std::min(min_geox, pasGCPs[i].dfGCPX); |
3244 | 0 | max_geox = std::max(max_geox, pasGCPs[i].dfGCPX); |
3245 | 0 | min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY); |
3246 | 0 | max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY); |
3247 | 0 | } |
3248 | |
|
3249 | 0 | double EPS = 1.0e-12; |
3250 | |
|
3251 | 0 | if (std::abs(max_pixel - min_pixel) < EPS || |
3252 | 0 | std::abs(max_line - min_line) < EPS || |
3253 | 0 | std::abs(max_geox - min_geox) < EPS || |
3254 | 0 | std::abs(max_geoy - min_geoy) < EPS) |
3255 | 0 | { |
3256 | 0 | return FALSE; // degenerate in at least one dimension. |
3257 | 0 | } |
3258 | | |
3259 | 0 | double pl_normalize[6], geo_normalize[6]; |
3260 | |
|
3261 | 0 | pl_normalize[0] = -min_pixel / (max_pixel - min_pixel); |
3262 | 0 | pl_normalize[1] = 1.0 / (max_pixel - min_pixel); |
3263 | 0 | pl_normalize[2] = 0.0; |
3264 | 0 | pl_normalize[3] = -min_line / (max_line - min_line); |
3265 | 0 | pl_normalize[4] = 0.0; |
3266 | 0 | pl_normalize[5] = 1.0 / (max_line - min_line); |
3267 | |
|
3268 | 0 | geo_normalize[0] = -min_geox / (max_geox - min_geox); |
3269 | 0 | geo_normalize[1] = 1.0 / (max_geox - min_geox); |
3270 | 0 | geo_normalize[2] = 0.0; |
3271 | 0 | geo_normalize[3] = -min_geoy / (max_geoy - min_geoy); |
3272 | 0 | geo_normalize[4] = 0.0; |
3273 | 0 | geo_normalize[5] = 1.0 / (max_geoy - min_geoy); |
3274 | | |
3275 | | /* -------------------------------------------------------------------- */ |
3276 | | /* In the general case, do a least squares error approximation by */ |
3277 | | /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum */ |
3278 | | /* -------------------------------------------------------------------- */ |
3279 | |
|
3280 | 0 | double sum_x = 0.0; |
3281 | 0 | double sum_y = 0.0; |
3282 | 0 | double sum_xy = 0.0; |
3283 | 0 | double sum_xx = 0.0; |
3284 | 0 | double sum_yy = 0.0; |
3285 | 0 | double sum_Lon = 0.0; |
3286 | 0 | double sum_Lonx = 0.0; |
3287 | 0 | double sum_Lony = 0.0; |
3288 | 0 | double sum_Lat = 0.0; |
3289 | 0 | double sum_Latx = 0.0; |
3290 | 0 | double sum_Laty = 0.0; |
3291 | |
|
3292 | 0 | for (int i = 0; i < nGCPCount; ++i) |
3293 | 0 | { |
3294 | 0 | double pixel, line, geox, geoy; |
3295 | |
|
3296 | 0 | GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel, |
3297 | 0 | pasGCPs[i].dfGCPLine, &pixel, &line); |
3298 | 0 | GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX, |
3299 | 0 | pasGCPs[i].dfGCPY, &geox, &geoy); |
3300 | |
|
3301 | 0 | sum_x += pixel; |
3302 | 0 | sum_y += line; |
3303 | 0 | sum_xy += pixel * line; |
3304 | 0 | sum_xx += pixel * pixel; |
3305 | 0 | sum_yy += line * line; |
3306 | 0 | sum_Lon += geox; |
3307 | 0 | sum_Lonx += geox * pixel; |
3308 | 0 | sum_Lony += geox * line; |
3309 | 0 | sum_Lat += geoy; |
3310 | 0 | sum_Latx += geoy * pixel; |
3311 | 0 | sum_Laty += geoy * line; |
3312 | 0 | } |
3313 | |
|
3314 | 0 | const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) + |
3315 | 0 | 2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx - |
3316 | 0 | sum_x * sum_x * sum_yy; |
3317 | | |
3318 | | /* -------------------------------------------------------------------- */ |
3319 | | /* If the divisor is zero, there is no valid solution. */ |
3320 | | /* -------------------------------------------------------------------- */ |
3321 | 0 | if (divisor == 0.0) |
3322 | 0 | return FALSE; |
3323 | | |
3324 | | /* -------------------------------------------------------------------- */ |
3325 | | /* Compute top/left origin. */ |
3326 | | /* -------------------------------------------------------------------- */ |
3327 | 0 | double gt_normalized[6] = {0.0}; |
3328 | 0 | gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) + |
3329 | 0 | sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) + |
3330 | 0 | sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) / |
3331 | 0 | divisor; |
3332 | |
|
3333 | 0 | gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) + |
3334 | 0 | sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) + |
3335 | 0 | sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) / |
3336 | 0 | divisor; |
3337 | | |
3338 | | /* -------------------------------------------------------------------- */ |
3339 | | /* Compute X related coefficients. */ |
3340 | | /* -------------------------------------------------------------------- */ |
3341 | 0 | gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) + |
3342 | 0 | sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) + |
3343 | 0 | sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) / |
3344 | 0 | divisor; |
3345 | |
|
3346 | 0 | gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) + |
3347 | 0 | sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) + |
3348 | 0 | sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) / |
3349 | 0 | divisor; |
3350 | | |
3351 | | /* -------------------------------------------------------------------- */ |
3352 | | /* Compute Y related coefficients. */ |
3353 | | /* -------------------------------------------------------------------- */ |
3354 | 0 | gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) + |
3355 | 0 | sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) + |
3356 | 0 | sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) / |
3357 | 0 | divisor; |
3358 | |
|
3359 | 0 | gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) + |
3360 | 0 | sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) + |
3361 | 0 | sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) / |
3362 | 0 | divisor; |
3363 | | |
3364 | | /* -------------------------------------------------------------------- */ |
3365 | | /* Compose the resulting transformation with the normalization */ |
3366 | | /* geotransformations. */ |
3367 | | /* -------------------------------------------------------------------- */ |
3368 | 0 | double gt1p2[6] = {0.0}; |
3369 | 0 | double inv_geo_normalize[6] = {0.0}; |
3370 | 0 | if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize)) |
3371 | 0 | return FALSE; |
3372 | | |
3373 | 0 | GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2); |
3374 | 0 | GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform); |
3375 | | |
3376 | | // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618 |
3377 | 0 | if (std::abs(padfGeoTransform[1]) <= 1e-15 || |
3378 | 0 | std::abs(padfGeoTransform[5]) <= 1e-15) |
3379 | 0 | { |
3380 | 0 | return FALSE; |
3381 | 0 | } |
3382 | | |
3383 | | /* -------------------------------------------------------------------- */ |
3384 | | /* Now check if any of the input points fit this poorly. */ |
3385 | | /* -------------------------------------------------------------------- */ |
3386 | 0 | if (!bApproxOK) |
3387 | 0 | { |
3388 | | // FIXME? Not sure if it is the more accurate way of computing |
3389 | | // pixel size |
3390 | 0 | double dfPixelSize = |
3391 | 0 | 0.5 * |
3392 | 0 | (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) + |
3393 | 0 | std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5])); |
3394 | 0 | if (dfPixelSize == 0.0) |
3395 | 0 | { |
3396 | 0 | CPLDebug("GDAL", "dfPixelSize = 0"); |
3397 | 0 | return FALSE; |
3398 | 0 | } |
3399 | | |
3400 | 0 | for (int i = 0; i < nGCPCount; i++) |
3401 | 0 | { |
3402 | 0 | const double dfErrorX = |
3403 | 0 | (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] + |
3404 | 0 | pasGCPs[i].dfGCPLine * padfGeoTransform[2] + |
3405 | 0 | padfGeoTransform[0]) - |
3406 | 0 | pasGCPs[i].dfGCPX; |
3407 | 0 | const double dfErrorY = |
3408 | 0 | (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] + |
3409 | 0 | pasGCPs[i].dfGCPLine * padfGeoTransform[5] + |
3410 | 0 | padfGeoTransform[3]) - |
3411 | 0 | pasGCPs[i].dfGCPY; |
3412 | |
|
3413 | 0 | if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize || |
3414 | 0 | std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize) |
3415 | 0 | { |
3416 | 0 | CPLDebug("GDAL", |
3417 | 0 | "dfErrorX/dfPixelSize = %.2f, " |
3418 | 0 | "dfErrorY/dfPixelSize = %.2f", |
3419 | 0 | std::abs(dfErrorX) / dfPixelSize, |
3420 | 0 | std::abs(dfErrorY) / dfPixelSize); |
3421 | 0 | return FALSE; |
3422 | 0 | } |
3423 | 0 | } |
3424 | 0 | } |
3425 | | |
3426 | 0 | return TRUE; |
3427 | 0 | } |
3428 | | |
3429 | | /************************************************************************/ |
3430 | | /* GDALComposeGeoTransforms() */ |
3431 | | /************************************************************************/ |
3432 | | |
3433 | | /** |
3434 | | * \brief Compose two geotransforms. |
3435 | | * |
3436 | | * The resulting geotransform is the equivalent to padfGT1 and then padfGT2 |
3437 | | * being applied to a point. |
3438 | | * |
3439 | | * @param padfGT1 the first geotransform, six values. |
3440 | | * @param padfGT2 the second geotransform, six values. |
3441 | | * @param padfGTOut the output geotransform, six values, may safely be the same |
3442 | | * array as padfGT1 or padfGT2. |
3443 | | */ |
3444 | | |
3445 | | void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2, |
3446 | | double *padfGTOut) |
3447 | | |
3448 | 0 | { |
3449 | 0 | double gtwrk[6] = {0.0}; |
3450 | | // We need to think of the geotransform in a more normal form to do |
3451 | | // the matrix multiple: |
3452 | | // |
3453 | | // __ __ |
3454 | | // | gt[1] gt[2] gt[0] | |
3455 | | // | gt[4] gt[5] gt[3] | |
3456 | | // | 0.0 0.0 1.0 | |
3457 | | // -- -- |
3458 | | // |
3459 | | // Then we can use normal matrix multiplication to produce the |
3460 | | // composed transformation. I don't actually reform the matrix |
3461 | | // explicitly which is why the following may seem kind of spagettish. |
3462 | |
|
3463 | 0 | gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4]; |
3464 | 0 | gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5]; |
3465 | 0 | gtwrk[0] = |
3466 | 0 | padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0; |
3467 | |
|
3468 | 0 | gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4]; |
3469 | 0 | gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5]; |
3470 | 0 | gtwrk[3] = |
3471 | 0 | padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0; |
3472 | 0 | memcpy(padfGTOut, gtwrk, sizeof(gtwrk)); |
3473 | 0 | } |
3474 | | |
3475 | | /************************************************************************/ |
3476 | | /* StripIrrelevantOptions() */ |
3477 | | /************************************************************************/ |
3478 | | |
3479 | | static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions) |
3480 | 0 | { |
3481 | 0 | if (psCOL == nullptr) |
3482 | 0 | return; |
3483 | 0 | if (nOptions == 0) |
3484 | 0 | nOptions = GDAL_OF_RASTER; |
3485 | 0 | if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0) |
3486 | 0 | return; |
3487 | | |
3488 | 0 | CPLXMLNode *psPrev = nullptr; |
3489 | 0 | for (CPLXMLNode *psIter = psCOL->psChild; psIter;) |
3490 | 0 | { |
3491 | 0 | if (psIter->eType == CXT_Element) |
3492 | 0 | { |
3493 | 0 | CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope"); |
3494 | 0 | bool bStrip = false; |
3495 | 0 | if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild && |
3496 | 0 | psScope->psChild->pszValue && |
3497 | 0 | EQUAL(psScope->psChild->pszValue, "vector")) |
3498 | 0 | { |
3499 | 0 | bStrip = true; |
3500 | 0 | } |
3501 | 0 | else if (nOptions == GDAL_OF_VECTOR && psScope && |
3502 | 0 | psScope->psChild && psScope->psChild->pszValue && |
3503 | 0 | EQUAL(psScope->psChild->pszValue, "raster")) |
3504 | 0 | { |
3505 | 0 | bStrip = true; |
3506 | 0 | } |
3507 | 0 | if (psScope) |
3508 | 0 | { |
3509 | 0 | CPLRemoveXMLChild(psIter, psScope); |
3510 | 0 | CPLDestroyXMLNode(psScope); |
3511 | 0 | } |
3512 | |
|
3513 | 0 | CPLXMLNode *psNext = psIter->psNext; |
3514 | 0 | if (bStrip) |
3515 | 0 | { |
3516 | 0 | if (psPrev) |
3517 | 0 | psPrev->psNext = psNext; |
3518 | 0 | else if (psCOL->psChild == psIter) |
3519 | 0 | psCOL->psChild = psNext; |
3520 | 0 | psIter->psNext = nullptr; |
3521 | 0 | CPLDestroyXMLNode(psIter); |
3522 | 0 | psIter = psNext; |
3523 | 0 | } |
3524 | 0 | else |
3525 | 0 | { |
3526 | 0 | psPrev = psIter; |
3527 | 0 | psIter = psNext; |
3528 | 0 | } |
3529 | 0 | } |
3530 | 0 | else |
3531 | 0 | { |
3532 | 0 | psIter = psIter->psNext; |
3533 | 0 | } |
3534 | 0 | } |
3535 | 0 | } |
3536 | | |
3537 | | /************************************************************************/ |
3538 | | /* GDALPrintDriverList() */ |
3539 | | /************************************************************************/ |
3540 | | |
3541 | | /** Print on stdout the driver list */ |
3542 | | std::string GDALPrintDriverList(int nOptions, bool bJSON) |
3543 | 0 | { |
3544 | 0 | if (nOptions == 0) |
3545 | 0 | nOptions = GDAL_OF_RASTER; |
3546 | |
|
3547 | 0 | if (bJSON) |
3548 | 0 | { |
3549 | 0 | auto poDM = GetGDALDriverManager(); |
3550 | 0 | CPLJSONArray oArray; |
3551 | 0 | const int nDriverCount = poDM->GetDriverCount(); |
3552 | 0 | for (int iDr = 0; iDr < nDriverCount; ++iDr) |
3553 | 0 | { |
3554 | 0 | auto poDriver = poDM->GetDriver(iDr); |
3555 | 0 | CSLConstList papszMD = poDriver->GetMetadata(); |
3556 | |
|
3557 | 0 | if (nOptions == GDAL_OF_RASTER && |
3558 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) |
3559 | 0 | continue; |
3560 | 0 | if (nOptions == GDAL_OF_VECTOR && |
3561 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) |
3562 | 0 | continue; |
3563 | 0 | if (nOptions == GDAL_OF_GNM && |
3564 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false)) |
3565 | 0 | continue; |
3566 | 0 | if (nOptions == GDAL_OF_MULTIDIM_RASTER && |
3567 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false)) |
3568 | 0 | continue; |
3569 | | |
3570 | 0 | CPLJSONObject oJDriver; |
3571 | 0 | oJDriver.Set("short_name", poDriver->GetDescription()); |
3572 | 0 | if (const char *pszLongName = |
3573 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME)) |
3574 | 0 | oJDriver.Set("long_name", pszLongName); |
3575 | 0 | CPLJSONArray oJScopes; |
3576 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) |
3577 | 0 | oJScopes.Add("raster"); |
3578 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false)) |
3579 | 0 | oJScopes.Add("multidimensional_raster"); |
3580 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) |
3581 | 0 | oJScopes.Add("vector"); |
3582 | 0 | oJDriver.Add("scopes", oJScopes); |
3583 | 0 | CPLJSONArray oJCaps; |
3584 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false)) |
3585 | 0 | oJCaps.Add("open"); |
3586 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false)) |
3587 | 0 | oJCaps.Add("create"); |
3588 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false)) |
3589 | 0 | oJCaps.Add("create_copy"); |
3590 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false)) |
3591 | 0 | oJCaps.Add("update"); |
3592 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false)) |
3593 | 0 | oJCaps.Add("virtual_io"); |
3594 | 0 | oJDriver.Add("capabilities", oJCaps); |
3595 | |
|
3596 | 0 | if (const char *pszExtensions = CSLFetchNameValueDef( |
3597 | 0 | papszMD, GDAL_DMD_EXTENSIONS, |
3598 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION))) |
3599 | 0 | { |
3600 | 0 | const CPLStringList aosExt( |
3601 | 0 | CSLTokenizeString2(pszExtensions, " ", 0)); |
3602 | 0 | CPLJSONArray oJExts; |
3603 | 0 | for (int i = 0; i < aosExt.size(); ++i) |
3604 | 0 | { |
3605 | 0 | oJExts.Add(aosExt[i]); |
3606 | 0 | } |
3607 | 0 | oJDriver.Add("file_extensions", oJExts); |
3608 | 0 | } |
3609 | |
|
3610 | 0 | oArray.Add(oJDriver); |
3611 | 0 | } |
3612 | |
|
3613 | 0 | return oArray.Format(CPLJSONObject::PrettyFormat::Pretty); |
3614 | 0 | } |
3615 | | |
3616 | 0 | std::string ret; |
3617 | 0 | ret = "Supported Formats: (ro:read-only, rw:read-write, " |
3618 | 0 | "+:write from scratch, u:update, " |
3619 | 0 | "v:virtual-I/O s:subdatasets)\n"; |
3620 | 0 | for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++) |
3621 | 0 | { |
3622 | 0 | GDALDriverH hDriver = GDALGetDriver(iDr); |
3623 | |
|
3624 | 0 | const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets; |
3625 | 0 | CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr); |
3626 | |
|
3627 | 0 | if (nOptions == GDAL_OF_RASTER && |
3628 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) |
3629 | 0 | continue; |
3630 | 0 | if (nOptions == GDAL_OF_VECTOR && |
3631 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) |
3632 | 0 | continue; |
3633 | 0 | if (nOptions == GDAL_OF_GNM && |
3634 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false)) |
3635 | 0 | continue; |
3636 | 0 | if (nOptions == GDAL_OF_MULTIDIM_RASTER && |
3637 | 0 | !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false)) |
3638 | 0 | continue; |
3639 | | |
3640 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false)) |
3641 | 0 | pszRFlag = "r"; |
3642 | |
|
3643 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false)) |
3644 | 0 | pszWFlag = "w+"; |
3645 | 0 | else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false)) |
3646 | 0 | pszWFlag = "w"; |
3647 | 0 | else |
3648 | 0 | pszWFlag = "o"; |
3649 | |
|
3650 | 0 | const char *pszUpdate = ""; |
3651 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false)) |
3652 | 0 | pszUpdate = "u"; |
3653 | |
|
3654 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false)) |
3655 | 0 | pszVirtualIO = "v"; |
3656 | 0 | else |
3657 | 0 | pszVirtualIO = ""; |
3658 | |
|
3659 | 0 | if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false)) |
3660 | 0 | pszSubdatasets = "s"; |
3661 | 0 | else |
3662 | 0 | pszSubdatasets = ""; |
3663 | |
|
3664 | 0 | CPLString osKind; |
3665 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) |
3666 | 0 | osKind = "raster"; |
3667 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false)) |
3668 | 0 | { |
3669 | 0 | if (!osKind.empty()) |
3670 | 0 | osKind += ','; |
3671 | 0 | osKind += "multidimensional raster"; |
3672 | 0 | } |
3673 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) |
3674 | 0 | { |
3675 | 0 | if (!osKind.empty()) |
3676 | 0 | osKind += ','; |
3677 | 0 | osKind += "vector"; |
3678 | 0 | } |
3679 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false)) |
3680 | 0 | { |
3681 | 0 | if (!osKind.empty()) |
3682 | 0 | osKind += ','; |
3683 | 0 | osKind += "geography network"; |
3684 | 0 | } |
3685 | 0 | if (osKind.empty()) |
3686 | 0 | osKind = "unknown kind"; |
3687 | |
|
3688 | 0 | std::string osExtensions; |
3689 | 0 | if (const char *pszExtensions = CSLFetchNameValueDef( |
3690 | 0 | papszMD, GDAL_DMD_EXTENSIONS, |
3691 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION))) |
3692 | 0 | { |
3693 | 0 | const CPLStringList aosExt( |
3694 | 0 | CSLTokenizeString2(pszExtensions, " ", 0)); |
3695 | 0 | for (int i = 0; i < aosExt.size(); ++i) |
3696 | 0 | { |
3697 | 0 | if (i == 0) |
3698 | 0 | osExtensions = " (*."; |
3699 | 0 | else |
3700 | 0 | osExtensions += ", *."; |
3701 | 0 | osExtensions += aosExt[i]; |
3702 | 0 | } |
3703 | 0 | if (!osExtensions.empty()) |
3704 | 0 | osExtensions += ')'; |
3705 | 0 | } |
3706 | |
|
3707 | 0 | ret += CPLSPrintf(" %s -%s- (%s%s%s%s%s): %s%s\n", /*ok*/ |
3708 | 0 | GDALGetDriverShortName(hDriver), osKind.c_str(), |
3709 | 0 | pszRFlag, pszWFlag, pszUpdate, pszVirtualIO, |
3710 | 0 | pszSubdatasets, GDALGetDriverLongName(hDriver), |
3711 | 0 | osExtensions.c_str()); |
3712 | 0 | } |
3713 | |
|
3714 | 0 | return ret; |
3715 | 0 | } |
3716 | | |
3717 | | /************************************************************************/ |
3718 | | /* GDALGeneralCmdLineProcessor() */ |
3719 | | /************************************************************************/ |
3720 | | |
3721 | | /** |
3722 | | * \brief General utility option processing. |
3723 | | * |
3724 | | * This function is intended to provide a variety of generic commandline |
3725 | | * options for all GDAL commandline utilities. It takes care of the following |
3726 | | * commandline options: |
3727 | | * |
3728 | | * \--version: report version of GDAL in use. |
3729 | | * \--build: report build info about GDAL in use. |
3730 | | * \--license: report GDAL license info. |
3731 | | * \--formats: report all format drivers configured. Can be used with -json since 3.10 |
3732 | | * \--format [format]: report details of one format driver. |
3733 | | * \--optfile filename: expand an option file into the argument list. |
3734 | | * \--config key value: set system configuration option. |
3735 | | * \--config key=value: set system configuration option (since GDAL 3.9) |
3736 | | * \--debug [on/off/value]: set debug level. |
3737 | | * \--mempreload dir: preload directory contents into /vsimem |
3738 | | * \--pause: Pause for user input (allows time to attach debugger) |
3739 | | * \--locale [locale]: Install a locale using setlocale() (debugging) |
3740 | | * \--help-general: report detailed help on general options. |
3741 | | * |
3742 | | * The argument array is replaced "in place" and should be freed with |
3743 | | * CSLDestroy() when no longer needed. The typical usage looks something |
3744 | | * like the following. Note that the formats should be registered so that |
3745 | | * the \--formats and \--format options will work properly. |
3746 | | * |
3747 | | * int main( int argc, char ** argv ) |
3748 | | * { |
3749 | | * GDALAllRegister(); |
3750 | | * |
3751 | | * argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 ); |
3752 | | * if( argc < 1 ) |
3753 | | * exit( -argc ); |
3754 | | * |
3755 | | * @param nArgc number of values in the argument list. |
3756 | | * @param ppapszArgv pointer to the argument list array (will be updated in |
3757 | | * place). |
3758 | | * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR |
3759 | | * to determine which drivers should be displayed by \--formats. |
3760 | | * If set to 0, GDAL_OF_RASTER is assumed. |
3761 | | * |
3762 | | * @return updated nArgc argument count. Return of 0 requests terminate |
3763 | | * without error, return of -1 requests exit with error code. |
3764 | | */ |
3765 | | |
3766 | | int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv, |
3767 | | int nOptions) |
3768 | | |
3769 | 0 | { |
3770 | 0 | CPLStringList aosReturn; |
3771 | 0 | int iArg; |
3772 | 0 | char **papszArgv = *ppapszArgv; |
3773 | | |
3774 | | /* -------------------------------------------------------------------- */ |
3775 | | /* Preserve the program name. */ |
3776 | | /* -------------------------------------------------------------------- */ |
3777 | 0 | aosReturn.AddString(papszArgv[0]); |
3778 | | |
3779 | | /* ==================================================================== */ |
3780 | | /* Loop over all arguments. */ |
3781 | | /* ==================================================================== */ |
3782 | | |
3783 | | // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on" |
3784 | | // detects and warns about a unknown config option. |
3785 | 0 | for (iArg = 1; iArg < nArgc; iArg++) |
3786 | 0 | { |
3787 | 0 | if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc && |
3788 | 0 | EQUAL(papszArgv[iArg + 1], "CPL_DEBUG")) |
3789 | 0 | { |
3790 | 0 | if (iArg + 1 >= nArgc) |
3791 | 0 | { |
3792 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3793 | 0 | "--config option given without a key=value argument."); |
3794 | 0 | return -1; |
3795 | 0 | } |
3796 | | |
3797 | 0 | const char *pszArg = papszArgv[iArg + 1]; |
3798 | 0 | if (strchr(pszArg, '=') != nullptr) |
3799 | 0 | { |
3800 | 0 | char *pszKey = nullptr; |
3801 | 0 | const char *pszValue = CPLParseNameValue(pszArg, &pszKey); |
3802 | 0 | if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue) |
3803 | 0 | { |
3804 | 0 | CPLSetConfigOption(pszKey, pszValue); |
3805 | 0 | } |
3806 | 0 | CPLFree(pszKey); |
3807 | 0 | ++iArg; |
3808 | 0 | } |
3809 | 0 | else |
3810 | 0 | { |
3811 | | // cppcheck-suppress knownConditionTrueFalse |
3812 | 0 | if (iArg + 2 >= nArgc) |
3813 | 0 | { |
3814 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3815 | 0 | "--config option given without a key and value " |
3816 | 0 | "argument."); |
3817 | 0 | return -1; |
3818 | 0 | } |
3819 | | |
3820 | 0 | if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG")) |
3821 | 0 | CPLSetConfigOption(papszArgv[iArg + 1], |
3822 | 0 | papszArgv[iArg + 2]); |
3823 | |
|
3824 | 0 | iArg += 2; |
3825 | 0 | } |
3826 | 0 | } |
3827 | 0 | else if (EQUAL(papszArgv[iArg], "--debug")) |
3828 | 0 | { |
3829 | 0 | if (iArg + 1 >= nArgc) |
3830 | 0 | { |
3831 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3832 | 0 | "--debug option given without debug level."); |
3833 | 0 | return -1; |
3834 | 0 | } |
3835 | | |
3836 | 0 | CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]); |
3837 | 0 | iArg += 1; |
3838 | 0 | } |
3839 | 0 | } |
3840 | | |
3841 | 0 | for (iArg = 1; iArg < nArgc; iArg++) |
3842 | 0 | { |
3843 | | /* -------------------------------------------------------------------- |
3844 | | */ |
3845 | | /* --version */ |
3846 | | /* -------------------------------------------------------------------- |
3847 | | */ |
3848 | 0 | if (EQUAL(papszArgv[iArg], "--version")) |
3849 | 0 | { |
3850 | 0 | printf("%s\n", GDALVersionInfo("--version")); /*ok*/ |
3851 | 0 | return 0; |
3852 | 0 | } |
3853 | | |
3854 | | /* -------------------------------------------------------------------- |
3855 | | */ |
3856 | | /* --build */ |
3857 | | /* -------------------------------------------------------------------- |
3858 | | */ |
3859 | 0 | else if (EQUAL(papszArgv[iArg], "--build")) |
3860 | 0 | { |
3861 | 0 | printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/ |
3862 | 0 | return 0; |
3863 | 0 | } |
3864 | | |
3865 | | /* -------------------------------------------------------------------- |
3866 | | */ |
3867 | | /* --license */ |
3868 | | /* -------------------------------------------------------------------- |
3869 | | */ |
3870 | 0 | else if (EQUAL(papszArgv[iArg], "--license")) |
3871 | 0 | { |
3872 | 0 | printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/ |
3873 | 0 | return 0; |
3874 | 0 | } |
3875 | | |
3876 | | /* -------------------------------------------------------------------- |
3877 | | */ |
3878 | | /* --config */ |
3879 | | /* -------------------------------------------------------------------- |
3880 | | */ |
3881 | 0 | else if (EQUAL(papszArgv[iArg], "--config")) |
3882 | 0 | { |
3883 | 0 | if (iArg + 1 >= nArgc) |
3884 | 0 | { |
3885 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3886 | 0 | "--config option given without a key=value argument."); |
3887 | 0 | return -1; |
3888 | 0 | } |
3889 | | |
3890 | 0 | const char *pszArg = papszArgv[iArg + 1]; |
3891 | 0 | if (strchr(pszArg, '=') != nullptr) |
3892 | 0 | { |
3893 | 0 | char *pszKey = nullptr; |
3894 | 0 | const char *pszValue = CPLParseNameValue(pszArg, &pszKey); |
3895 | 0 | if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue) |
3896 | 0 | { |
3897 | 0 | CPLSetConfigOption(pszKey, pszValue); |
3898 | 0 | } |
3899 | 0 | CPLFree(pszKey); |
3900 | 0 | ++iArg; |
3901 | 0 | } |
3902 | 0 | else |
3903 | 0 | { |
3904 | 0 | if (iArg + 2 >= nArgc) |
3905 | 0 | { |
3906 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3907 | 0 | "--config option given without a key and value " |
3908 | 0 | "argument."); |
3909 | 0 | return -1; |
3910 | 0 | } |
3911 | | |
3912 | 0 | if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG")) |
3913 | 0 | CPLSetConfigOption(papszArgv[iArg + 1], |
3914 | 0 | papszArgv[iArg + 2]); |
3915 | |
|
3916 | 0 | iArg += 2; |
3917 | 0 | } |
3918 | 0 | } |
3919 | | |
3920 | | /* -------------------------------------------------------------------- |
3921 | | */ |
3922 | | /* --mempreload */ |
3923 | | /* -------------------------------------------------------------------- |
3924 | | */ |
3925 | 0 | else if (EQUAL(papszArgv[iArg], "--mempreload")) |
3926 | 0 | { |
3927 | 0 | if (iArg + 1 >= nArgc) |
3928 | 0 | { |
3929 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3930 | 0 | "--mempreload option given without directory path."); |
3931 | 0 | return -1; |
3932 | 0 | } |
3933 | | |
3934 | 0 | char **papszFiles = VSIReadDir(papszArgv[iArg + 1]); |
3935 | 0 | if (CSLCount(papszFiles) == 0) |
3936 | 0 | { |
3937 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3938 | 0 | "--mempreload given invalid or empty directory."); |
3939 | 0 | return -1; |
3940 | 0 | } |
3941 | | |
3942 | 0 | for (int i = 0; papszFiles[i] != nullptr; i++) |
3943 | 0 | { |
3944 | 0 | if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], "..")) |
3945 | 0 | continue; |
3946 | | |
3947 | 0 | std::string osOldPath; |
3948 | 0 | CPLString osNewPath; |
3949 | 0 | osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1], |
3950 | 0 | papszFiles[i], nullptr); |
3951 | 0 | osNewPath.Printf("/vsimem/%s", papszFiles[i]); |
3952 | |
|
3953 | 0 | VSIStatBufL sStatBuf; |
3954 | 0 | if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 || |
3955 | 0 | VSI_ISDIR(sStatBuf.st_mode)) |
3956 | 0 | { |
3957 | 0 | CPLDebug("VSI", "Skipping preload of %s.", |
3958 | 0 | osOldPath.c_str()); |
3959 | 0 | continue; |
3960 | 0 | } |
3961 | | |
3962 | 0 | CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(), |
3963 | 0 | osNewPath.c_str()); |
3964 | |
|
3965 | 0 | if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0) |
3966 | 0 | { |
3967 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3968 | 0 | "Failed to copy %s to /vsimem", osOldPath.c_str()); |
3969 | 0 | return -1; |
3970 | 0 | } |
3971 | 0 | } |
3972 | | |
3973 | 0 | CSLDestroy(papszFiles); |
3974 | 0 | iArg += 1; |
3975 | 0 | } |
3976 | | |
3977 | | /* -------------------------------------------------------------------- |
3978 | | */ |
3979 | | /* --debug */ |
3980 | | /* -------------------------------------------------------------------- |
3981 | | */ |
3982 | 0 | else if (EQUAL(papszArgv[iArg], "--debug")) |
3983 | 0 | { |
3984 | 0 | if (iArg + 1 >= nArgc) |
3985 | 0 | { |
3986 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
3987 | 0 | "--debug option given without debug level."); |
3988 | 0 | return -1; |
3989 | 0 | } |
3990 | | |
3991 | 0 | iArg += 1; |
3992 | 0 | } |
3993 | | |
3994 | | /* -------------------------------------------------------------------- |
3995 | | */ |
3996 | | /* --optfile */ |
3997 | | /* -------------------------------------------------------------------- |
3998 | | */ |
3999 | 0 | else if (EQUAL(papszArgv[iArg], "--optfile")) |
4000 | 0 | { |
4001 | 0 | if (iArg + 1 >= nArgc) |
4002 | 0 | { |
4003 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4004 | 0 | "--optfile option given without filename."); |
4005 | 0 | return -1; |
4006 | 0 | } |
4007 | | |
4008 | 0 | VSILFILE *fpOptFile = VSIFOpenL(papszArgv[iArg + 1], "rb"); |
4009 | |
|
4010 | 0 | if (fpOptFile == nullptr) |
4011 | 0 | { |
4012 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4013 | 0 | "Unable to open optfile '%s'.\n%s", |
4014 | 0 | papszArgv[iArg + 1], VSIStrerror(errno)); |
4015 | 0 | return -1; |
4016 | 0 | } |
4017 | | |
4018 | 0 | const char *pszLine; |
4019 | 0 | CPLStringList aosArgvOptfile; |
4020 | | // dummy value as first argument to please |
4021 | | // GDALGeneralCmdLineProcessor() |
4022 | 0 | aosArgvOptfile.AddString(""); |
4023 | 0 | bool bHasOptfile = false; |
4024 | 0 | while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr) |
4025 | 0 | { |
4026 | 0 | if (pszLine[0] == '#' || strlen(pszLine) == 0) |
4027 | 0 | continue; |
4028 | | |
4029 | 0 | char **papszTokens = CSLTokenizeString(pszLine); |
4030 | 0 | for (int i = 0; |
4031 | 0 | papszTokens != nullptr && papszTokens[i] != nullptr; i++) |
4032 | 0 | { |
4033 | 0 | if (EQUAL(papszTokens[i], "--optfile")) |
4034 | 0 | { |
4035 | | // To avoid potential recursion |
4036 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
4037 | 0 | "--optfile not supported in a option file"); |
4038 | 0 | bHasOptfile = true; |
4039 | 0 | } |
4040 | 0 | aosArgvOptfile.AddStringDirectly(papszTokens[i]); |
4041 | 0 | papszTokens[i] = nullptr; |
4042 | 0 | } |
4043 | 0 | CSLDestroy(papszTokens); |
4044 | 0 | } |
4045 | |
|
4046 | 0 | VSIFCloseL(fpOptFile); |
4047 | |
|
4048 | 0 | char **papszArgvOptfile = aosArgvOptfile.StealList(); |
4049 | 0 | if (!bHasOptfile) |
4050 | 0 | { |
4051 | 0 | char **papszArgvOptfileBefore = papszArgvOptfile; |
4052 | 0 | if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile), |
4053 | 0 | &papszArgvOptfile, |
4054 | 0 | nOptions) < 0) |
4055 | 0 | { |
4056 | 0 | CSLDestroy(papszArgvOptfile); |
4057 | 0 | return -1; |
4058 | 0 | } |
4059 | 0 | CSLDestroy(papszArgvOptfileBefore); |
4060 | 0 | } |
4061 | | |
4062 | 0 | char **papszIter = papszArgvOptfile + 1; |
4063 | 0 | while (*papszIter) |
4064 | 0 | { |
4065 | 0 | aosReturn.AddString(*papszIter); |
4066 | 0 | ++papszIter; |
4067 | 0 | } |
4068 | 0 | CSLDestroy(papszArgvOptfile); |
4069 | |
|
4070 | 0 | iArg += 1; |
4071 | 0 | } |
4072 | | |
4073 | | /* -------------------------------------------------------------------- |
4074 | | */ |
4075 | | /* --formats */ |
4076 | | /* -------------------------------------------------------------------- |
4077 | | */ |
4078 | 0 | else if (EQUAL(papszArgv[iArg], "--formats")) |
4079 | 0 | { |
4080 | 0 | bool bJSON = false; |
4081 | 0 | for (int i = 1; i < nArgc; i++) |
4082 | 0 | { |
4083 | 0 | if (strcmp(papszArgv[i], "-json") == 0 || |
4084 | 0 | strcmp(papszArgv[i], "--json") == 0) |
4085 | 0 | { |
4086 | 0 | bJSON = true; |
4087 | 0 | break; |
4088 | 0 | } |
4089 | 0 | } |
4090 | |
|
4091 | 0 | printf("%s", GDALPrintDriverList(nOptions, bJSON).c_str()); /*ok*/ |
4092 | |
|
4093 | 0 | return 0; |
4094 | 0 | } |
4095 | | |
4096 | | /* -------------------------------------------------------------------- |
4097 | | */ |
4098 | | /* --format */ |
4099 | | /* -------------------------------------------------------------------- |
4100 | | */ |
4101 | 0 | else if (EQUAL(papszArgv[iArg], "--format")) |
4102 | 0 | { |
4103 | 0 | GDALDriverH hDriver; |
4104 | 0 | char **papszMD; |
4105 | |
|
4106 | 0 | if (iArg + 1 >= nArgc) |
4107 | 0 | { |
4108 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4109 | 0 | "--format option given without a format code."); |
4110 | 0 | return -1; |
4111 | 0 | } |
4112 | | |
4113 | 0 | hDriver = GDALGetDriverByName(papszArgv[iArg + 1]); |
4114 | 0 | if (hDriver == nullptr) |
4115 | 0 | { |
4116 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4117 | 0 | "--format option given with format '%s', but that " |
4118 | 0 | "format not\nrecognised. Use the --formats option " |
4119 | 0 | "to get a list of available formats,\n" |
4120 | 0 | "and use the short code (i.e. GTiff or HFA) as the " |
4121 | 0 | "format identifier.\n", |
4122 | 0 | papszArgv[iArg + 1]); |
4123 | 0 | return -1; |
4124 | 0 | } |
4125 | | |
4126 | 0 | printf("Format Details:\n"); /*ok*/ |
4127 | 0 | printf(/*ok*/ " Short Name: %s\n", |
4128 | 0 | GDALGetDriverShortName(hDriver)); |
4129 | 0 | printf(/*ok*/ " Long Name: %s\n", GDALGetDriverLongName(hDriver)); |
4130 | |
|
4131 | 0 | papszMD = GDALGetMetadata(hDriver, nullptr); |
4132 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false)) |
4133 | 0 | printf(" Supports: Raster\n"); /*ok*/ |
4134 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false)) |
4135 | 0 | printf(" Supports: Multidimensional raster\n"); /*ok*/ |
4136 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false)) |
4137 | 0 | printf(" Supports: Vector\n"); /*ok*/ |
4138 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false)) |
4139 | 0 | printf(" Supports: Geography Network\n"); /*ok*/ |
4140 | |
|
4141 | 0 | const char *pszExt = |
4142 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS); |
4143 | 0 | if (pszExt != nullptr) |
4144 | 0 | printf(" Extension%s: %s\n", /*ok*/ |
4145 | 0 | (strchr(pszExt, ' ') ? "s" : ""), pszExt); |
4146 | |
|
4147 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE)) |
4148 | 0 | printf(" Mime Type: %s\n", /*ok*/ |
4149 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE)); |
4150 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC)) |
4151 | 0 | printf(" Help Topic: %s\n", /*ok*/ |
4152 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC)); |
4153 | |
|
4154 | 0 | if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false)) |
4155 | 0 | printf(" Supports: Raster subdatasets\n"); /*ok*/ |
4156 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false)) |
4157 | 0 | printf(" Supports: Open() - Open existing dataset.\n"); /*ok*/ |
4158 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false)) |
4159 | 0 | printf(/*ok*/ |
4160 | 0 | " Supports: Create() - Create writable dataset.\n"); |
4161 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false)) |
4162 | 0 | printf(/*ok*/ " Supports: CreateMultiDimensional() - Create " |
4163 | 0 | "multidimensional dataset.\n"); |
4164 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false)) |
4165 | 0 | printf(/*ok*/ " Supports: CreateCopy() - Create dataset by " |
4166 | 0 | "copying " |
4167 | 0 | "another.\n"); |
4168 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false)) |
4169 | 0 | printf(" Supports: Update\n"); /*ok*/ |
4170 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false)) |
4171 | 0 | printf(" Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/ |
4172 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES)) |
4173 | 0 | printf(" Creation Datatypes: %s\n", /*ok*/ |
4174 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES)); |
4175 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES)) |
4176 | 0 | printf(" Creation Field Datatypes: %s\n", /*ok*/ |
4177 | 0 | CSLFetchNameValue(papszMD, |
4178 | 0 | GDAL_DMD_CREATIONFIELDDATATYPES)); |
4179 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES)) |
4180 | 0 | printf(" Creation Field Data Sub-types: %s\n", /*ok*/ |
4181 | 0 | CSLFetchNameValue(papszMD, |
4182 | 0 | GDAL_DMD_CREATIONFIELDDATASUBTYPES)); |
4183 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false)) |
4184 | 0 | printf(/*ok*/ " Supports: Creating fields with NOT NULL " |
4185 | 0 | "constraint.\n"); |
4186 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false)) |
4187 | 0 | printf(/*ok*/ |
4188 | 0 | " Supports: Creating fields with UNIQUE constraint.\n"); |
4189 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false)) |
4190 | 0 | printf(/*ok*/ |
4191 | 0 | " Supports: Creating fields with DEFAULT values.\n"); |
4192 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false)) |
4193 | 0 | /*ok*/ printf( |
4194 | 0 | " Supports: Creating geometry fields with NOT NULL " |
4195 | 0 | "constraint.\n"); |
4196 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION, |
4197 | 0 | false)) |
4198 | 0 | /*ok*/ printf(" Supports: Writing geometries with given " |
4199 | 0 | "coordinate precision\n"); |
4200 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false)) |
4201 | 0 | printf(" Supports: Reading feature styles.\n"); /*ok*/ |
4202 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false)) |
4203 | 0 | printf(" Supports: Writing feature styles.\n"); /*ok*/ |
4204 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false)) |
4205 | 0 | printf(" Supports: Coordinate epoch.\n"); /*ok*/ |
4206 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false)) |
4207 | 0 | printf(" Supports: Multiple vector layers.\n"); /*ok*/ |
4208 | 0 | if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false)) |
4209 | 0 | printf(" Supports: Reading field domains.\n"); /*ok*/ |
4210 | 0 | if (CSLFetchNameValue(papszMD, |
4211 | 0 | GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES)) |
4212 | 0 | printf(" Creation field domain types: %s\n", /*ok*/ |
4213 | 0 | CSLFetchNameValue(papszMD, |
4214 | 0 | GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES)); |
4215 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS)) |
4216 | 0 | printf(" Supported SQL dialects: %s\n", /*ok*/ |
4217 | 0 | CSLFetchNameValue(papszMD, |
4218 | 0 | GDAL_DMD_SUPPORTED_SQL_DIALECTS)); |
4219 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS)) |
4220 | 0 | printf(" Supported items for update: %s\n", /*ok*/ |
4221 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS)); |
4222 | |
|
4223 | 0 | for (const char *key : |
4224 | 0 | {GDAL_DMD_CREATIONOPTIONLIST, |
4225 | 0 | GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST, |
4226 | 0 | GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST, |
4227 | 0 | GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST, |
4228 | 0 | GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST, |
4229 | 0 | GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST, |
4230 | 0 | GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST, |
4231 | 0 | GDAL_DS_LAYER_CREATIONOPTIONLIST}) |
4232 | 0 | { |
4233 | 0 | if (CSLFetchNameValue(papszMD, key)) |
4234 | 0 | { |
4235 | 0 | CPLXMLNode *psCOL = |
4236 | 0 | CPLParseXMLString(CSLFetchNameValue(papszMD, key)); |
4237 | 0 | StripIrrelevantOptions(psCOL, nOptions); |
4238 | 0 | char *pszFormattedXML = CPLSerializeXMLTree(psCOL); |
4239 | |
|
4240 | 0 | CPLDestroyXMLNode(psCOL); |
4241 | |
|
4242 | 0 | printf("\n%s\n", pszFormattedXML); /*ok*/ |
4243 | 0 | CPLFree(pszFormattedXML); |
4244 | 0 | } |
4245 | 0 | } |
4246 | |
|
4247 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX)) |
4248 | 0 | printf(" Connection prefix: %s\n", /*ok*/ |
4249 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX)); |
4250 | |
|
4251 | 0 | if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST)) |
4252 | 0 | { |
4253 | 0 | CPLXMLNode *psCOL = CPLParseXMLString( |
4254 | 0 | CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST)); |
4255 | 0 | StripIrrelevantOptions(psCOL, nOptions); |
4256 | 0 | char *pszFormattedXML = CPLSerializeXMLTree(psCOL); |
4257 | |
|
4258 | 0 | CPLDestroyXMLNode(psCOL); |
4259 | |
|
4260 | 0 | printf("%s\n", pszFormattedXML); /*ok*/ |
4261 | 0 | CPLFree(pszFormattedXML); |
4262 | 0 | } |
4263 | |
|
4264 | 0 | bool bFirstOtherOption = true; |
4265 | 0 | for (char **papszIter = papszMD; papszIter && *papszIter; |
4266 | 0 | ++papszIter) |
4267 | 0 | { |
4268 | 0 | if (!STARTS_WITH(*papszIter, "DCAP_") && |
4269 | 0 | !STARTS_WITH(*papszIter, "DMD_") && |
4270 | 0 | !STARTS_WITH(*papszIter, "DS_") && |
4271 | 0 | !STARTS_WITH(*papszIter, "OGR_DRIVER=")) |
4272 | 0 | { |
4273 | 0 | if (bFirstOtherOption) |
4274 | 0 | printf(" Other metadata items:\n"); /*ok*/ |
4275 | 0 | bFirstOtherOption = false; |
4276 | 0 | printf(" %s\n", *papszIter); /*ok*/ |
4277 | 0 | } |
4278 | 0 | } |
4279 | |
|
4280 | 0 | return 0; |
4281 | 0 | } |
4282 | | |
4283 | | /* -------------------------------------------------------------------- |
4284 | | */ |
4285 | | /* --help-general */ |
4286 | | /* -------------------------------------------------------------------- |
4287 | | */ |
4288 | 0 | else if (EQUAL(papszArgv[iArg], "--help-general")) |
4289 | 0 | { |
4290 | 0 | printf("Generic GDAL utility command options:\n"); /*ok*/ |
4291 | 0 | printf(" --version: report version of GDAL in use.\n"); /*ok*/ |
4292 | 0 | /*ok*/ printf( |
4293 | 0 | " --build: report detailed information about GDAL in " |
4294 | 0 | "use.\n"); |
4295 | 0 | printf(" --license: report GDAL license info.\n"); /*ok*/ |
4296 | 0 | printf( /*ok*/ |
4297 | 0 | " --formats: report all configured format drivers.\n"); /*ok*/ |
4298 | 0 | printf(" --format [<format>]: details of one format.\n"); /*ok*/ |
4299 | 0 | /*ok*/ printf( |
4300 | 0 | " --optfile filename: expand an option file into the " |
4301 | 0 | "argument list.\n"); |
4302 | 0 | printf(/*ok*/ |
4303 | 0 | " --config <key> <value> or --config <key>=<value>: set " |
4304 | 0 | "system configuration option.\n"); /*ok*/ |
4305 | 0 | printf(" --debug [on/off/value]: set debug level.\n"); /*ok*/ |
4306 | 0 | /*ok*/ printf( /*ok*/ |
4307 | 0 | " --pause: wait for user input, time to attach " |
4308 | 0 | "debugger\n"); |
4309 | 0 | printf(" --locale [<locale>]: install locale for debugging " /*ok*/ |
4310 | 0 | "(i.e. en_US.UTF-8)\n"); |
4311 | 0 | printf(" --help-general: report detailed help on general " /*ok*/ |
4312 | 0 | "options.\n"); |
4313 | |
|
4314 | 0 | return 0; |
4315 | 0 | } |
4316 | | |
4317 | | /* -------------------------------------------------------------------- |
4318 | | */ |
4319 | | /* --locale */ |
4320 | | /* -------------------------------------------------------------------- |
4321 | | */ |
4322 | 0 | else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale")) |
4323 | 0 | { |
4324 | 0 | CPLsetlocale(LC_ALL, papszArgv[++iArg]); |
4325 | 0 | } |
4326 | | |
4327 | | /* -------------------------------------------------------------------- |
4328 | | */ |
4329 | | /* --pause */ |
4330 | | /* -------------------------------------------------------------------- |
4331 | | */ |
4332 | 0 | else if (EQUAL(papszArgv[iArg], "--pause")) |
4333 | 0 | { |
4334 | 0 | std::cout << "Hit <ENTER> to Continue." << std::endl; |
4335 | 0 | std::cin.clear(); |
4336 | 0 | std::cin.ignore(cpl::NumericLimits<std::streamsize>::max(), '\n'); |
4337 | 0 | } |
4338 | | |
4339 | | /* -------------------------------------------------------------------- |
4340 | | */ |
4341 | | /* Carry through unrecognized options. */ |
4342 | | /* -------------------------------------------------------------------- |
4343 | | */ |
4344 | 0 | else |
4345 | 0 | { |
4346 | 0 | aosReturn.AddString(papszArgv[iArg]); |
4347 | 0 | } |
4348 | 0 | } |
4349 | | |
4350 | 0 | const int nSize = aosReturn.size(); |
4351 | 0 | *ppapszArgv = aosReturn.StealList(); |
4352 | |
|
4353 | 0 | return nSize; |
4354 | 0 | } |
4355 | | |
4356 | | /************************************************************************/ |
4357 | | /* _FetchDblFromMD() */ |
4358 | | /************************************************************************/ |
4359 | | |
4360 | | static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey, |
4361 | | double *padfTarget, int nCount, double dfDefault) |
4362 | | |
4363 | 0 | { |
4364 | 0 | char szFullKey[200]; |
4365 | |
|
4366 | 0 | snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey); |
4367 | |
|
4368 | 0 | const char *pszValue = CSLFetchNameValue(papszMD, szFullKey); |
4369 | |
|
4370 | 0 | for (int i = 0; i < nCount; i++) |
4371 | 0 | padfTarget[i] = dfDefault; |
4372 | |
|
4373 | 0 | if (pszValue == nullptr) |
4374 | 0 | return false; |
4375 | | |
4376 | 0 | if (nCount == 1) |
4377 | 0 | { |
4378 | 0 | *padfTarget = CPLAtofM(pszValue); |
4379 | 0 | return true; |
4380 | 0 | } |
4381 | | |
4382 | 0 | char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE); |
4383 | |
|
4384 | 0 | if (CSLCount(papszTokens) != nCount) |
4385 | 0 | { |
4386 | 0 | CSLDestroy(papszTokens); |
4387 | 0 | return false; |
4388 | 0 | } |
4389 | | |
4390 | 0 | for (int i = 0; i < nCount; i++) |
4391 | 0 | padfTarget[i] = CPLAtofM(papszTokens[i]); |
4392 | |
|
4393 | 0 | CSLDestroy(papszTokens); |
4394 | |
|
4395 | 0 | return true; |
4396 | 0 | } |
4397 | | |
4398 | | /************************************************************************/ |
4399 | | /* GDALExtractRPCInfo() */ |
4400 | | /************************************************************************/ |
4401 | | |
4402 | | /** Extract RPC info from metadata, and apply to an RPCInfo structure. |
4403 | | * |
4404 | | * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp |
4405 | | * |
4406 | | * @param papszMD Dictionary of metadata representing RPC |
4407 | | * @param psRPC (output) Pointer to structure to hold the RPC values. |
4408 | | * @return TRUE in case of success. FALSE in case of failure. |
4409 | | */ |
4410 | | int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC) |
4411 | | |
4412 | 0 | { |
4413 | 0 | GDALRPCInfoV2 sRPC; |
4414 | 0 | if (!GDALExtractRPCInfoV2(papszMD, &sRPC)) |
4415 | 0 | return FALSE; |
4416 | 0 | memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1)); |
4417 | 0 | return TRUE; |
4418 | 0 | } |
4419 | | |
4420 | | /** Extract RPC info from metadata, and apply to an RPCInfo structure. |
4421 | | * |
4422 | | * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp |
4423 | | * |
4424 | | * @param papszMD Dictionary of metadata representing RPC |
4425 | | * @param psRPC (output) Pointer to structure to hold the RPC values. |
4426 | | * @return TRUE in case of success. FALSE in case of failure. |
4427 | | */ |
4428 | | int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC) |
4429 | | |
4430 | 0 | { |
4431 | 0 | if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr) |
4432 | 0 | return FALSE; |
4433 | | |
4434 | 0 | if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr || |
4435 | 0 | CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr || |
4436 | 0 | CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr || |
4437 | 0 | CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr) |
4438 | 0 | { |
4439 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4440 | 0 | "Some required RPC metadata missing in GDALExtractRPCInfo()"); |
4441 | 0 | return FALSE; |
4442 | 0 | } |
4443 | | |
4444 | 0 | _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0); |
4445 | 0 | _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0); |
4446 | 0 | _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0); |
4447 | 0 | _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0); |
4448 | 0 | _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0); |
4449 | 0 | _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0); |
4450 | 0 | _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0); |
4451 | 0 | _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1, |
4452 | 0 | 1.0); |
4453 | 0 | _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0); |
4454 | 0 | _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0); |
4455 | 0 | _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0); |
4456 | 0 | _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0); |
4457 | |
|
4458 | 0 | _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20, |
4459 | 0 | 0.0); |
4460 | 0 | _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20, |
4461 | 0 | 0.0); |
4462 | 0 | _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20, |
4463 | 0 | 0.0); |
4464 | 0 | _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20, |
4465 | 0 | 0.0); |
4466 | |
|
4467 | 0 | _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0); |
4468 | 0 | _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0); |
4469 | 0 | _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0); |
4470 | 0 | _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0); |
4471 | |
|
4472 | 0 | return TRUE; |
4473 | 0 | } |
4474 | | |
4475 | | /************************************************************************/ |
4476 | | /* GDALFindAssociatedAuxFile() */ |
4477 | | /************************************************************************/ |
4478 | | |
4479 | | GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename, |
4480 | | GDALAccess eAccess, |
4481 | | GDALDataset *poDependentDS) |
4482 | | |
4483 | 0 | { |
4484 | 0 | const char *pszAuxSuffixLC = "aux"; |
4485 | 0 | const char *pszAuxSuffixUC = "AUX"; |
4486 | |
|
4487 | 0 | if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC)) |
4488 | 0 | return nullptr; |
4489 | | |
4490 | | /* -------------------------------------------------------------------- */ |
4491 | | /* Don't even try to look for an .aux file if we don't have a */ |
4492 | | /* path of any kind. */ |
4493 | | /* -------------------------------------------------------------------- */ |
4494 | 0 | if (strlen(pszBasename) == 0) |
4495 | 0 | return nullptr; |
4496 | | |
4497 | | /* -------------------------------------------------------------------- */ |
4498 | | /* We didn't find that, so try and find a corresponding aux */ |
4499 | | /* file. Check that we are the dependent file of the aux */ |
4500 | | /* file, or if we aren't verify that the dependent file does */ |
4501 | | /* not exist, likely mean it is us but some sort of renaming */ |
4502 | | /* has occurred. */ |
4503 | | /* -------------------------------------------------------------------- */ |
4504 | 0 | CPLString osJustFile = CPLGetFilename(pszBasename); // without dir |
4505 | 0 | CPLString osAuxFilename = |
4506 | 0 | CPLResetExtensionSafe(pszBasename, pszAuxSuffixLC); |
4507 | 0 | GDALDataset *poODS = nullptr; |
4508 | 0 | GByte abyHeader[32]; |
4509 | |
|
4510 | 0 | VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb"); |
4511 | |
|
4512 | 0 | if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename)) |
4513 | 0 | { |
4514 | | // Can't found file with lower case suffix. Try the upper case one. |
4515 | 0 | osAuxFilename = CPLResetExtensionSafe(pszBasename, pszAuxSuffixUC); |
4516 | 0 | fp = VSIFOpenL(osAuxFilename, "rb"); |
4517 | 0 | } |
4518 | |
|
4519 | 0 | if (fp != nullptr) |
4520 | 0 | { |
4521 | 0 | if (VSIFReadL(abyHeader, 1, 32, fp) == 32 && |
4522 | 0 | STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader), |
4523 | 0 | "EHFA_HEADER_TAG")) |
4524 | 0 | { |
4525 | | /* Avoid causing failure in opening of main file from SWIG bindings |
4526 | | */ |
4527 | | /* when auxiliary file cannot be opened (#3269) */ |
4528 | 0 | CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{}; |
4529 | 0 | if (poDependentDS != nullptr && poDependentDS->GetShared()) |
4530 | 0 | poODS = GDALDataset::FromHandle( |
4531 | 0 | GDALOpenShared(osAuxFilename, eAccess)); |
4532 | 0 | else |
4533 | 0 | poODS = |
4534 | 0 | GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess)); |
4535 | 0 | } |
4536 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
4537 | 0 | } |
4538 | | |
4539 | | /* -------------------------------------------------------------------- */ |
4540 | | /* Try replacing extension with .aux */ |
4541 | | /* -------------------------------------------------------------------- */ |
4542 | 0 | if (poODS != nullptr) |
4543 | 0 | { |
4544 | 0 | const char *pszDep = |
4545 | 0 | poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA"); |
4546 | 0 | if (pszDep == nullptr) |
4547 | 0 | { |
4548 | 0 | CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.", |
4549 | 0 | osAuxFilename.c_str()); |
4550 | 0 | GDALClose(poODS); |
4551 | 0 | poODS = nullptr; |
4552 | 0 | } |
4553 | 0 | else if (!EQUAL(pszDep, osJustFile)) |
4554 | 0 | { |
4555 | 0 | VSIStatBufL sStatBuf; |
4556 | |
|
4557 | 0 | if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
4558 | 0 | { |
4559 | 0 | CPLDebug("AUX", "%s is for file %s, not %s, ignoring.", |
4560 | 0 | osAuxFilename.c_str(), pszDep, osJustFile.c_str()); |
4561 | 0 | GDALClose(poODS); |
4562 | 0 | poODS = nullptr; |
4563 | 0 | } |
4564 | 0 | else |
4565 | 0 | { |
4566 | 0 | CPLDebug("AUX", |
4567 | 0 | "%s is for file %s, not %s, but since\n" |
4568 | 0 | "%s does not exist, we will use .aux file as our own.", |
4569 | 0 | osAuxFilename.c_str(), pszDep, osJustFile.c_str(), |
4570 | 0 | pszDep); |
4571 | 0 | } |
4572 | 0 | } |
4573 | | |
4574 | | /* -------------------------------------------------------------------- |
4575 | | */ |
4576 | | /* Confirm that the aux file matches the configuration of the */ |
4577 | | /* dependent dataset. */ |
4578 | | /* -------------------------------------------------------------------- |
4579 | | */ |
4580 | 0 | if (poODS != nullptr && poDependentDS != nullptr && |
4581 | 0 | (poODS->GetRasterCount() != poDependentDS->GetRasterCount() || |
4582 | 0 | poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() || |
4583 | 0 | poODS->GetRasterYSize() != poDependentDS->GetRasterYSize())) |
4584 | 0 | { |
4585 | 0 | CPLDebug("AUX", |
4586 | 0 | "Ignoring aux file %s as its raster configuration\n" |
4587 | 0 | "(%dP x %dL x %dB) does not match master file (%dP x %dL " |
4588 | 0 | "x %dB)", |
4589 | 0 | osAuxFilename.c_str(), poODS->GetRasterXSize(), |
4590 | 0 | poODS->GetRasterYSize(), poODS->GetRasterCount(), |
4591 | 0 | poDependentDS->GetRasterXSize(), |
4592 | 0 | poDependentDS->GetRasterYSize(), |
4593 | 0 | poDependentDS->GetRasterCount()); |
4594 | |
|
4595 | 0 | GDALClose(poODS); |
4596 | 0 | poODS = nullptr; |
4597 | 0 | } |
4598 | 0 | } |
4599 | | |
4600 | | /* -------------------------------------------------------------------- */ |
4601 | | /* Try appending .aux to the end of the filename. */ |
4602 | | /* -------------------------------------------------------------------- */ |
4603 | 0 | if (poODS == nullptr) |
4604 | 0 | { |
4605 | 0 | osAuxFilename = pszBasename; |
4606 | 0 | osAuxFilename += "."; |
4607 | 0 | osAuxFilename += pszAuxSuffixLC; |
4608 | 0 | fp = VSIFOpenL(osAuxFilename, "rb"); |
4609 | 0 | if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename)) |
4610 | 0 | { |
4611 | | // Can't found file with lower case suffix. Try the upper case one. |
4612 | 0 | osAuxFilename = pszBasename; |
4613 | 0 | osAuxFilename += "."; |
4614 | 0 | osAuxFilename += pszAuxSuffixUC; |
4615 | 0 | fp = VSIFOpenL(osAuxFilename, "rb"); |
4616 | 0 | } |
4617 | |
|
4618 | 0 | if (fp != nullptr) |
4619 | 0 | { |
4620 | 0 | if (VSIFReadL(abyHeader, 1, 32, fp) == 32 && |
4621 | 0 | STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader), |
4622 | 0 | "EHFA_HEADER_TAG")) |
4623 | 0 | { |
4624 | | /* Avoid causing failure in opening of main file from SWIG |
4625 | | * bindings */ |
4626 | | /* when auxiliary file cannot be opened (#3269) */ |
4627 | 0 | CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{}; |
4628 | 0 | if (poDependentDS != nullptr && poDependentDS->GetShared()) |
4629 | 0 | poODS = GDALDataset::FromHandle( |
4630 | 0 | GDALOpenShared(osAuxFilename, eAccess)); |
4631 | 0 | else |
4632 | 0 | poODS = GDALDataset::FromHandle( |
4633 | 0 | GDALOpen(osAuxFilename, eAccess)); |
4634 | 0 | } |
4635 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
4636 | 0 | } |
4637 | |
|
4638 | 0 | if (poODS != nullptr) |
4639 | 0 | { |
4640 | 0 | const char *pszDep = |
4641 | 0 | poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA"); |
4642 | 0 | if (pszDep == nullptr) |
4643 | 0 | { |
4644 | 0 | CPLDebug("AUX", |
4645 | 0 | "Found %s but it has no dependent file, ignoring.", |
4646 | 0 | osAuxFilename.c_str()); |
4647 | 0 | GDALClose(poODS); |
4648 | 0 | poODS = nullptr; |
4649 | 0 | } |
4650 | 0 | else if (!EQUAL(pszDep, osJustFile)) |
4651 | 0 | { |
4652 | 0 | VSIStatBufL sStatBuf; |
4653 | |
|
4654 | 0 | if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
4655 | 0 | { |
4656 | 0 | CPLDebug("AUX", "%s is for file %s, not %s, ignoring.", |
4657 | 0 | osAuxFilename.c_str(), pszDep, osJustFile.c_str()); |
4658 | 0 | GDALClose(poODS); |
4659 | 0 | poODS = nullptr; |
4660 | 0 | } |
4661 | 0 | else |
4662 | 0 | { |
4663 | 0 | CPLDebug( |
4664 | 0 | "AUX", |
4665 | 0 | "%s is for file %s, not %s, but since\n" |
4666 | 0 | "%s does not exist, we will use .aux file as our own.", |
4667 | 0 | osAuxFilename.c_str(), pszDep, osJustFile.c_str(), |
4668 | 0 | pszDep); |
4669 | 0 | } |
4670 | 0 | } |
4671 | 0 | } |
4672 | 0 | } |
4673 | | |
4674 | | /* -------------------------------------------------------------------- */ |
4675 | | /* Confirm that the aux file matches the configuration of the */ |
4676 | | /* dependent dataset. */ |
4677 | | /* -------------------------------------------------------------------- */ |
4678 | 0 | if (poODS != nullptr && poDependentDS != nullptr && |
4679 | 0 | (poODS->GetRasterCount() != poDependentDS->GetRasterCount() || |
4680 | 0 | poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() || |
4681 | 0 | poODS->GetRasterYSize() != poDependentDS->GetRasterYSize())) |
4682 | 0 | { |
4683 | 0 | CPLDebug( |
4684 | 0 | "AUX", |
4685 | 0 | "Ignoring aux file %s as its raster configuration\n" |
4686 | 0 | "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)", |
4687 | 0 | osAuxFilename.c_str(), poODS->GetRasterXSize(), |
4688 | 0 | poODS->GetRasterYSize(), poODS->GetRasterCount(), |
4689 | 0 | poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(), |
4690 | 0 | poDependentDS->GetRasterCount()); |
4691 | |
|
4692 | 0 | GDALClose(poODS); |
4693 | 0 | poODS = nullptr; |
4694 | 0 | } |
4695 | |
|
4696 | 0 | return poODS; |
4697 | 0 | } |
4698 | | |
4699 | | /************************************************************************/ |
4700 | | /* Infrastructure to check that dataset characteristics are valid */ |
4701 | | /************************************************************************/ |
4702 | | |
4703 | | CPL_C_START |
4704 | | |
4705 | | /** |
4706 | | * \brief Return TRUE if the dataset dimensions are valid. |
4707 | | * |
4708 | | * @param nXSize raster width |
4709 | | * @param nYSize raster height |
4710 | | * |
4711 | | * @since GDAL 1.7.0 |
4712 | | */ |
4713 | | int GDALCheckDatasetDimensions(int nXSize, int nYSize) |
4714 | 0 | { |
4715 | 0 | if (nXSize <= 0 || nYSize <= 0) |
4716 | 0 | { |
4717 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4718 | 0 | "Invalid dataset dimensions : %d x %d", nXSize, nYSize); |
4719 | 0 | return FALSE; |
4720 | 0 | } |
4721 | 0 | return TRUE; |
4722 | 0 | } |
4723 | | |
4724 | | /** |
4725 | | * \brief Return TRUE if the band count is valid. |
4726 | | * |
4727 | | * If the configuration option GDAL_MAX_BAND_COUNT is defined, |
4728 | | * the band count will be compared to the maximum number of band allowed. |
4729 | | * If not defined, the maximum number allowed is 65536. |
4730 | | * |
4731 | | * @param nBands the band count |
4732 | | * @param bIsZeroAllowed TRUE if band count == 0 is allowed |
4733 | | * |
4734 | | * @since GDAL 1.7.0 |
4735 | | */ |
4736 | | |
4737 | | int GDALCheckBandCount(int nBands, int bIsZeroAllowed) |
4738 | 0 | { |
4739 | 0 | if (nBands < 0 || (!bIsZeroAllowed && nBands == 0)) |
4740 | 0 | { |
4741 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d", |
4742 | 0 | nBands); |
4743 | 0 | return FALSE; |
4744 | 0 | } |
4745 | 0 | const char *pszMaxBandCount = |
4746 | 0 | CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536"); |
4747 | 0 | int nMaxBands = std::clamp(atoi(pszMaxBandCount), 0, INT_MAX - 1); |
4748 | 0 | if (nBands > nMaxBands) |
4749 | 0 | { |
4750 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4751 | 0 | "Invalid band count : %d. Maximum allowed currently is %d. " |
4752 | 0 | "Define GDAL_MAX_BAND_COUNT to a higher level if it is a " |
4753 | 0 | "legitimate number.", |
4754 | 0 | nBands, nMaxBands); |
4755 | 0 | return FALSE; |
4756 | 0 | } |
4757 | 0 | return TRUE; |
4758 | 0 | } |
4759 | | |
4760 | | CPL_C_END |
4761 | | |
4762 | | /************************************************************************/ |
4763 | | /* GDALSerializeGCPListToXML() */ |
4764 | | /************************************************************************/ |
4765 | | |
4766 | | void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode, |
4767 | | const std::vector<gdal::GCP> &asGCPs, |
4768 | | const OGRSpatialReference *poGCP_SRS) |
4769 | 0 | { |
4770 | 0 | CPLString oFmt; |
4771 | |
|
4772 | 0 | CPLXMLNode *psPamGCPList = |
4773 | 0 | CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList"); |
4774 | |
|
4775 | 0 | CPLXMLNode *psLastChild = nullptr; |
4776 | |
|
4777 | 0 | if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty()) |
4778 | 0 | { |
4779 | 0 | char *pszWKT = nullptr; |
4780 | 0 | poGCP_SRS->exportToWkt(&pszWKT); |
4781 | 0 | CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT); |
4782 | 0 | CPLFree(pszWKT); |
4783 | 0 | const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping(); |
4784 | 0 | CPLString osMapping; |
4785 | 0 | for (size_t i = 0; i < mapping.size(); ++i) |
4786 | 0 | { |
4787 | 0 | if (!osMapping.empty()) |
4788 | 0 | osMapping += ","; |
4789 | 0 | osMapping += CPLSPrintf("%d", mapping[i]); |
4790 | 0 | } |
4791 | 0 | CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping", |
4792 | 0 | osMapping.c_str()); |
4793 | |
|
4794 | 0 | psLastChild = psPamGCPList->psChild->psNext; |
4795 | 0 | } |
4796 | |
|
4797 | 0 | for (const gdal::GCP &gcp : asGCPs) |
4798 | 0 | { |
4799 | 0 | CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP"); |
4800 | |
|
4801 | 0 | if (psLastChild == nullptr) |
4802 | 0 | psPamGCPList->psChild = psXMLGCP; |
4803 | 0 | else |
4804 | 0 | psLastChild->psNext = psXMLGCP; |
4805 | 0 | psLastChild = psXMLGCP; |
4806 | |
|
4807 | 0 | CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id()); |
4808 | |
|
4809 | 0 | if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0) |
4810 | 0 | CPLSetXMLValue(psXMLGCP, "Info", gcp.Info()); |
4811 | |
|
4812 | 0 | CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel())); |
4813 | |
|
4814 | 0 | CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line())); |
4815 | |
|
4816 | 0 | CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X())); |
4817 | |
|
4818 | 0 | CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y())); |
4819 | | |
4820 | | /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it |
4821 | | * back */ |
4822 | 0 | if (gcp.Z() != 0.0) |
4823 | 0 | CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z())); |
4824 | 0 | } |
4825 | 0 | } |
4826 | | |
4827 | | /************************************************************************/ |
4828 | | /* GDALDeserializeGCPListFromXML() */ |
4829 | | /************************************************************************/ |
4830 | | |
4831 | | void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList, |
4832 | | std::vector<gdal::GCP> &asGCPs, |
4833 | | OGRSpatialReference **ppoGCP_SRS) |
4834 | 0 | { |
4835 | 0 | if (ppoGCP_SRS) |
4836 | 0 | { |
4837 | 0 | const char *pszRawProj = |
4838 | 0 | CPLGetXMLValue(psGCPList, "Projection", nullptr); |
4839 | |
|
4840 | 0 | *ppoGCP_SRS = nullptr; |
4841 | 0 | if (pszRawProj && pszRawProj[0]) |
4842 | 0 | { |
4843 | 0 | *ppoGCP_SRS = new OGRSpatialReference(); |
4844 | 0 | (*ppoGCP_SRS) |
4845 | 0 | ->SetFromUserInput( |
4846 | 0 | pszRawProj, |
4847 | 0 | OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS); |
4848 | |
|
4849 | 0 | const char *pszMapping = |
4850 | 0 | CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr); |
4851 | 0 | if (pszMapping) |
4852 | 0 | { |
4853 | 0 | char **papszTokens = |
4854 | 0 | CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE); |
4855 | 0 | std::vector<int> anMapping; |
4856 | 0 | for (int i = 0; papszTokens && papszTokens[i]; i++) |
4857 | 0 | { |
4858 | 0 | anMapping.push_back(atoi(papszTokens[i])); |
4859 | 0 | } |
4860 | 0 | CSLDestroy(papszTokens); |
4861 | 0 | (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping); |
4862 | 0 | } |
4863 | 0 | else |
4864 | 0 | { |
4865 | 0 | (*ppoGCP_SRS) |
4866 | 0 | ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
4867 | 0 | } |
4868 | 0 | } |
4869 | 0 | } |
4870 | |
|
4871 | 0 | asGCPs.clear(); |
4872 | 0 | for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP; |
4873 | 0 | psXMLGCP = psXMLGCP->psNext) |
4874 | 0 | { |
4875 | 0 | if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element) |
4876 | 0 | continue; |
4877 | | |
4878 | 0 | gdal::GCP gcp; |
4879 | 0 | gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", "")); |
4880 | 0 | gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", "")); |
4881 | |
|
4882 | 0 | const auto ParseDoubleValue = |
4883 | 0 | [psXMLGCP](const char *pszParameter, double &dfVal) |
4884 | 0 | { |
4885 | 0 | const char *pszVal = |
4886 | 0 | CPLGetXMLValue(psXMLGCP, pszParameter, nullptr); |
4887 | 0 | if (!pszVal) |
4888 | 0 | { |
4889 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing", |
4890 | 0 | pszParameter); |
4891 | 0 | return false; |
4892 | 0 | } |
4893 | 0 | char *endptr = nullptr; |
4894 | 0 | dfVal = CPLStrtod(pszVal, &endptr); |
4895 | 0 | if (endptr == pszVal) |
4896 | 0 | { |
4897 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4898 | 0 | "GCP#%s=%s is an invalid value", pszParameter, pszVal); |
4899 | 0 | return false; |
4900 | 0 | } |
4901 | 0 | return true; |
4902 | 0 | }; |
4903 | |
|
4904 | 0 | bool bOK = true; |
4905 | 0 | if (!ParseDoubleValue("Pixel", gcp.Pixel())) |
4906 | 0 | bOK = false; |
4907 | 0 | if (!ParseDoubleValue("Line", gcp.Line())) |
4908 | 0 | bOK = false; |
4909 | 0 | if (!ParseDoubleValue("X", gcp.X())) |
4910 | 0 | bOK = false; |
4911 | 0 | if (!ParseDoubleValue("Y", gcp.Y())) |
4912 | 0 | bOK = false; |
4913 | 0 | const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr); |
4914 | 0 | if (pszZ == nullptr) |
4915 | 0 | { |
4916 | | // Note: GDAL 1.10.1 and older generated #GCPZ, |
4917 | | // but could not read it back. |
4918 | 0 | pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0"); |
4919 | 0 | } |
4920 | 0 | char *endptr = nullptr; |
4921 | 0 | gcp.Z() = CPLStrtod(pszZ, &endptr); |
4922 | 0 | if (endptr == pszZ) |
4923 | 0 | { |
4924 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
4925 | 0 | "GCP#Z=%s is an invalid value", pszZ); |
4926 | 0 | bOK = false; |
4927 | 0 | } |
4928 | |
|
4929 | 0 | if (bOK) |
4930 | 0 | { |
4931 | 0 | asGCPs.emplace_back(std::move(gcp)); |
4932 | 0 | } |
4933 | 0 | } |
4934 | 0 | } |
4935 | | |
4936 | | /************************************************************************/ |
4937 | | /* GDALSerializeOpenOptionsToXML() */ |
4938 | | /************************************************************************/ |
4939 | | |
4940 | | void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode, |
4941 | | CSLConstList papszOpenOptions) |
4942 | 0 | { |
4943 | 0 | if (papszOpenOptions != nullptr) |
4944 | 0 | { |
4945 | 0 | CPLXMLNode *psOpenOptions = |
4946 | 0 | CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions"); |
4947 | 0 | CPLXMLNode *psLastChild = nullptr; |
4948 | |
|
4949 | 0 | for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr; |
4950 | 0 | papszIter++) |
4951 | 0 | { |
4952 | 0 | const char *pszRawValue; |
4953 | 0 | char *pszKey = nullptr; |
4954 | 0 | CPLXMLNode *psOOI; |
4955 | |
|
4956 | 0 | pszRawValue = CPLParseNameValue(*papszIter, &pszKey); |
4957 | |
|
4958 | 0 | psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI"); |
4959 | 0 | if (psLastChild == nullptr) |
4960 | 0 | psOpenOptions->psChild = psOOI; |
4961 | 0 | else |
4962 | 0 | psLastChild->psNext = psOOI; |
4963 | 0 | psLastChild = psOOI; |
4964 | |
|
4965 | 0 | CPLSetXMLValue(psOOI, "#key", pszKey); |
4966 | 0 | CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue); |
4967 | |
|
4968 | 0 | CPLFree(pszKey); |
4969 | 0 | } |
4970 | 0 | } |
4971 | 0 | } |
4972 | | |
4973 | | /************************************************************************/ |
4974 | | /* GDALDeserializeOpenOptionsFromXML() */ |
4975 | | /************************************************************************/ |
4976 | | |
4977 | | char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode) |
4978 | 0 | { |
4979 | 0 | char **papszOpenOptions = nullptr; |
4980 | 0 | const CPLXMLNode *psOpenOptions = |
4981 | 0 | CPLGetXMLNode(psParentNode, "OpenOptions"); |
4982 | 0 | if (psOpenOptions != nullptr) |
4983 | 0 | { |
4984 | 0 | const CPLXMLNode *psOOI; |
4985 | 0 | for (psOOI = psOpenOptions->psChild; psOOI != nullptr; |
4986 | 0 | psOOI = psOOI->psNext) |
4987 | 0 | { |
4988 | 0 | if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element || |
4989 | 0 | psOOI->psChild == nullptr || |
4990 | 0 | psOOI->psChild->psNext == nullptr || |
4991 | 0 | psOOI->psChild->eType != CXT_Attribute || |
4992 | 0 | psOOI->psChild->psChild == nullptr) |
4993 | 0 | continue; |
4994 | | |
4995 | 0 | char *pszName = psOOI->psChild->psChild->pszValue; |
4996 | 0 | char *pszValue = psOOI->psChild->psNext->pszValue; |
4997 | 0 | if (pszName != nullptr && pszValue != nullptr) |
4998 | 0 | papszOpenOptions = |
4999 | 0 | CSLSetNameValue(papszOpenOptions, pszName, pszValue); |
5000 | 0 | } |
5001 | 0 | } |
5002 | 0 | return papszOpenOptions; |
5003 | 0 | } |
5004 | | |
5005 | | /************************************************************************/ |
5006 | | /* GDALRasterIOGetResampleAlg() */ |
5007 | | /************************************************************************/ |
5008 | | |
5009 | | GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling) |
5010 | 0 | { |
5011 | 0 | GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour; |
5012 | 0 | if (STARTS_WITH_CI(pszResampling, "NEAR")) |
5013 | 0 | eResampleAlg = GRIORA_NearestNeighbour; |
5014 | 0 | else if (EQUAL(pszResampling, "BILINEAR")) |
5015 | 0 | eResampleAlg = GRIORA_Bilinear; |
5016 | 0 | else if (EQUAL(pszResampling, "CUBIC")) |
5017 | 0 | eResampleAlg = GRIORA_Cubic; |
5018 | 0 | else if (EQUAL(pszResampling, "CUBICSPLINE")) |
5019 | 0 | eResampleAlg = GRIORA_CubicSpline; |
5020 | 0 | else if (EQUAL(pszResampling, "LANCZOS")) |
5021 | 0 | eResampleAlg = GRIORA_Lanczos; |
5022 | 0 | else if (EQUAL(pszResampling, "AVERAGE")) |
5023 | 0 | eResampleAlg = GRIORA_Average; |
5024 | 0 | else if (EQUAL(pszResampling, "RMS")) |
5025 | 0 | eResampleAlg = GRIORA_RMS; |
5026 | 0 | else if (EQUAL(pszResampling, "MODE")) |
5027 | 0 | eResampleAlg = GRIORA_Mode; |
5028 | 0 | else if (EQUAL(pszResampling, "GAUSS")) |
5029 | 0 | eResampleAlg = GRIORA_Gauss; |
5030 | 0 | else |
5031 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
5032 | 0 | "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling); |
5033 | 0 | return eResampleAlg; |
5034 | 0 | } |
5035 | | |
5036 | | /************************************************************************/ |
5037 | | /* GDALRasterIOGetResampleAlgStr() */ |
5038 | | /************************************************************************/ |
5039 | | |
5040 | | const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg) |
5041 | 0 | { |
5042 | 0 | switch (eResampleAlg) |
5043 | 0 | { |
5044 | 0 | case GRIORA_NearestNeighbour: |
5045 | 0 | return "NearestNeighbour"; |
5046 | 0 | case GRIORA_Bilinear: |
5047 | 0 | return "Bilinear"; |
5048 | 0 | case GRIORA_Cubic: |
5049 | 0 | return "Cubic"; |
5050 | 0 | case GRIORA_CubicSpline: |
5051 | 0 | return "CubicSpline"; |
5052 | 0 | case GRIORA_Lanczos: |
5053 | 0 | return "Lanczos"; |
5054 | 0 | case GRIORA_Average: |
5055 | 0 | return "Average"; |
5056 | 0 | case GRIORA_RMS: |
5057 | 0 | return "RMS"; |
5058 | 0 | case GRIORA_Mode: |
5059 | 0 | return "Mode"; |
5060 | 0 | case GRIORA_Gauss: |
5061 | 0 | return "Gauss"; |
5062 | 0 | default: |
5063 | 0 | CPLAssert(false); |
5064 | 0 | return "Unknown"; |
5065 | 0 | } |
5066 | 0 | } |
5067 | | |
5068 | | /************************************************************************/ |
5069 | | /* GDALRasterIOExtraArgSetResampleAlg() */ |
5070 | | /************************************************************************/ |
5071 | | |
5072 | | void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg, |
5073 | | int nXSize, int nYSize, int nBufXSize, |
5074 | | int nBufYSize) |
5075 | 0 | { |
5076 | 0 | if ((nBufXSize != nXSize || nBufYSize != nYSize) && |
5077 | 0 | psExtraArg->eResampleAlg == GRIORA_NearestNeighbour) |
5078 | 0 | { |
5079 | 0 | const char *pszResampling = |
5080 | 0 | CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr); |
5081 | 0 | if (pszResampling != nullptr) |
5082 | 0 | { |
5083 | 0 | psExtraArg->eResampleAlg = |
5084 | 0 | GDALRasterIOGetResampleAlg(pszResampling); |
5085 | 0 | } |
5086 | 0 | } |
5087 | 0 | } |
5088 | | |
5089 | | /************************************************************************/ |
5090 | | /* GDALCanFileAcceptSidecarFile() */ |
5091 | | /************************************************************************/ |
5092 | | |
5093 | | int GDALCanFileAcceptSidecarFile(const char *pszFilename) |
5094 | 0 | { |
5095 | 0 | if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?')) |
5096 | 0 | return FALSE; |
5097 | | // Do no attempt reading side-car files on /vsisubfile/ (#6241) |
5098 | 0 | if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0) |
5099 | 0 | return FALSE; |
5100 | 0 | return TRUE; |
5101 | 0 | } |
5102 | | |
5103 | | /************************************************************************/ |
5104 | | /* GDALCanReliablyUseSiblingFileList() */ |
5105 | | /************************************************************************/ |
5106 | | |
5107 | | /* Try to address https://github.com/OSGeo/gdal/issues/2903 */ |
5108 | | /* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */ |
5109 | | /* (normalization form decomposed). The filesystem takes care of converting */ |
5110 | | /* precomposed form as often coming from user interface to this NFD variant */ |
5111 | | /* See |
5112 | | * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x |
5113 | | */ |
5114 | | /* And readdir() will return such NFD variant encoding. Consequently comparing |
5115 | | */ |
5116 | | /* the user filename with ones with readdir() is not reliable */ |
5117 | | /* - APFS preserves both case and normalization of the filename on disk in all |
5118 | | */ |
5119 | | /* variants. In macOS High Sierra, APFS is normalization-insensitive in both |
5120 | | */ |
5121 | | /* the case-insensitive and case-sensitive variants, using a hash-based native |
5122 | | */ |
5123 | | /* normalization scheme. APFS preserves the normalization of the filename and |
5124 | | */ |
5125 | | /* uses hashes of the normalized form of the filename to provide normalization |
5126 | | */ |
5127 | | /* insensitivity. */ |
5128 | | /* From |
5129 | | * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html |
5130 | | */ |
5131 | | /* Issues might still arise if the file has been created using one of the |
5132 | | * UTF-8 */ |
5133 | | /* encoding (likely the decomposed one if using MacOS specific API), but the |
5134 | | */ |
5135 | | /* string passed to GDAL for opening would be with another one (likely the |
5136 | | * precomposed one) */ |
5137 | | bool GDALCanReliablyUseSiblingFileList(const char *pszFilename) |
5138 | 0 | { |
5139 | | #ifdef __APPLE__ |
5140 | | for (int i = 0; pszFilename[i] != 0; ++i) |
5141 | | { |
5142 | | if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127) |
5143 | | { |
5144 | | // non-ASCII character found |
5145 | | |
5146 | | // if this is a network storage, assume no issue |
5147 | | if (!VSIIsLocal(pszFilename)) |
5148 | | { |
5149 | | return true; |
5150 | | } |
5151 | | return false; |
5152 | | } |
5153 | | } |
5154 | | return true; |
5155 | | #else |
5156 | 0 | (void)pszFilename; |
5157 | 0 | return true; |
5158 | 0 | #endif |
5159 | 0 | } |
5160 | | |
5161 | | /************************************************************************/ |
5162 | | /* GDALAdjustNoDataCloseToFloatMax() */ |
5163 | | /************************************************************************/ |
5164 | | |
5165 | | double GDALAdjustNoDataCloseToFloatMax(double dfVal) |
5166 | 0 | { |
5167 | 0 | const auto kMaxFloat = cpl::NumericLimits<float>::max(); |
5168 | 0 | if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat) |
5169 | 0 | return -kMaxFloat; |
5170 | 0 | if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat) |
5171 | 0 | return kMaxFloat; |
5172 | 0 | return dfVal; |
5173 | 0 | } |
5174 | | |
5175 | | /************************************************************************/ |
5176 | | /* GDALCopyNoDataValue() */ |
5177 | | /************************************************************************/ |
5178 | | |
5179 | | /** Copy the nodata value from the source band to the target band if |
5180 | | * it can be exactly represented in the output data type. |
5181 | | * |
5182 | | * @param poDstBand Destination band. |
5183 | | * @param poSrcBand Source band band. |
5184 | | * @param[out] pbCannotBeExactlyRepresented Pointer to a boolean, or nullptr. |
5185 | | * If the value cannot be exactly represented on the output data |
5186 | | * type, *pbCannotBeExactlyRepresented will be set to true. |
5187 | | * |
5188 | | * @return true if the nodata value was successfully set. |
5189 | | */ |
5190 | | bool GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand, |
5191 | | bool *pbCannotBeExactlyRepresented) |
5192 | 0 | { |
5193 | 0 | if (pbCannotBeExactlyRepresented) |
5194 | 0 | *pbCannotBeExactlyRepresented = false; |
5195 | 0 | int bSuccess; |
5196 | 0 | const auto eSrcDataType = poSrcBand->GetRasterDataType(); |
5197 | 0 | const auto eDstDataType = poDstBand->GetRasterDataType(); |
5198 | 0 | if (eSrcDataType == GDT_Int64) |
5199 | 0 | { |
5200 | 0 | const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess); |
5201 | 0 | if (bSuccess) |
5202 | 0 | { |
5203 | 0 | if (eDstDataType == GDT_Int64) |
5204 | 0 | { |
5205 | 0 | return poDstBand->SetNoDataValueAsInt64(nNoData) == CE_None; |
5206 | 0 | } |
5207 | 0 | else if (eDstDataType == GDT_UInt64) |
5208 | 0 | { |
5209 | 0 | if (nNoData >= 0) |
5210 | 0 | { |
5211 | 0 | return poDstBand->SetNoDataValueAsUInt64( |
5212 | 0 | static_cast<uint64_t>(nNoData)) == CE_None; |
5213 | 0 | } |
5214 | 0 | } |
5215 | 0 | else if (nNoData == |
5216 | 0 | static_cast<int64_t>(static_cast<double>(nNoData))) |
5217 | 0 | { |
5218 | 0 | const double dfValue = static_cast<double>(nNoData); |
5219 | 0 | if (GDALIsValueExactAs(dfValue, eDstDataType)) |
5220 | 0 | return poDstBand->SetNoDataValue(dfValue) == CE_None; |
5221 | 0 | } |
5222 | 0 | } |
5223 | 0 | } |
5224 | 0 | else if (eSrcDataType == GDT_UInt64) |
5225 | 0 | { |
5226 | 0 | const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess); |
5227 | 0 | if (bSuccess) |
5228 | 0 | { |
5229 | 0 | if (eDstDataType == GDT_UInt64) |
5230 | 0 | { |
5231 | 0 | return poDstBand->SetNoDataValueAsUInt64(nNoData) == CE_None; |
5232 | 0 | } |
5233 | 0 | else if (eDstDataType == GDT_Int64) |
5234 | 0 | { |
5235 | 0 | if (nNoData < |
5236 | 0 | static_cast<uint64_t>(cpl::NumericLimits<int64_t>::max())) |
5237 | 0 | { |
5238 | 0 | return poDstBand->SetNoDataValueAsInt64( |
5239 | 0 | static_cast<int64_t>(nNoData)) == CE_None; |
5240 | 0 | } |
5241 | 0 | } |
5242 | 0 | else if (nNoData == |
5243 | 0 | static_cast<uint64_t>(static_cast<double>(nNoData))) |
5244 | 0 | { |
5245 | 0 | const double dfValue = static_cast<double>(nNoData); |
5246 | 0 | if (GDALIsValueExactAs(dfValue, eDstDataType)) |
5247 | 0 | return poDstBand->SetNoDataValue(dfValue) == CE_None; |
5248 | 0 | } |
5249 | 0 | } |
5250 | 0 | } |
5251 | 0 | else |
5252 | 0 | { |
5253 | 0 | const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess); |
5254 | 0 | if (bSuccess) |
5255 | 0 | { |
5256 | 0 | if (eDstDataType == GDT_Int64) |
5257 | 0 | { |
5258 | 0 | if (dfNoData >= static_cast<double>( |
5259 | 0 | cpl::NumericLimits<int64_t>::lowest()) && |
5260 | 0 | dfNoData <= static_cast<double>( |
5261 | 0 | cpl::NumericLimits<int64_t>::max()) && |
5262 | 0 | dfNoData == |
5263 | 0 | static_cast<double>(static_cast<int64_t>(dfNoData))) |
5264 | 0 | { |
5265 | 0 | return poDstBand->SetNoDataValueAsInt64( |
5266 | 0 | static_cast<int64_t>(dfNoData)) == CE_None; |
5267 | 0 | } |
5268 | 0 | } |
5269 | 0 | else if (eDstDataType == GDT_UInt64) |
5270 | 0 | { |
5271 | 0 | if (dfNoData >= static_cast<double>( |
5272 | 0 | cpl::NumericLimits<uint64_t>::lowest()) && |
5273 | 0 | dfNoData <= static_cast<double>( |
5274 | 0 | cpl::NumericLimits<uint64_t>::max()) && |
5275 | 0 | dfNoData == |
5276 | 0 | static_cast<double>(static_cast<uint64_t>(dfNoData))) |
5277 | 0 | { |
5278 | 0 | return poDstBand->SetNoDataValueAsInt64( |
5279 | 0 | static_cast<uint64_t>(dfNoData)) == CE_None; |
5280 | 0 | } |
5281 | 0 | } |
5282 | 0 | else |
5283 | 0 | { |
5284 | 0 | return poDstBand->SetNoDataValue(dfNoData) == CE_None; |
5285 | 0 | } |
5286 | 0 | } |
5287 | 0 | } |
5288 | 0 | if (pbCannotBeExactlyRepresented) |
5289 | 0 | *pbCannotBeExactlyRepresented = true; |
5290 | 0 | return false; |
5291 | 0 | } |
5292 | | |
5293 | | /************************************************************************/ |
5294 | | /* GDALGetNoDataValueCastToDouble() */ |
5295 | | /************************************************************************/ |
5296 | | |
5297 | | double GDALGetNoDataValueCastToDouble(int64_t nVal) |
5298 | 0 | { |
5299 | 0 | const double dfVal = static_cast<double>(nVal); |
5300 | 0 | if (static_cast<int64_t>(dfVal) != nVal) |
5301 | 0 | { |
5302 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
5303 | 0 | "GetNoDataValue() returns an approximate value of the " |
5304 | 0 | "true nodata value = " CPL_FRMT_GIB ". Use " |
5305 | 0 | "GetNoDataValueAsInt64() instead", |
5306 | 0 | static_cast<GIntBig>(nVal)); |
5307 | 0 | } |
5308 | 0 | return dfVal; |
5309 | 0 | } |
5310 | | |
5311 | | double GDALGetNoDataValueCastToDouble(uint64_t nVal) |
5312 | 0 | { |
5313 | 0 | const double dfVal = static_cast<double>(nVal); |
5314 | 0 | if (static_cast<uint64_t>(dfVal) != nVal) |
5315 | 0 | { |
5316 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
5317 | 0 | "GetNoDataValue() returns an approximate value of the " |
5318 | 0 | "true nodata value = " CPL_FRMT_GUIB ". Use " |
5319 | 0 | "GetNoDataValueAsUInt64() instead", |
5320 | 0 | static_cast<GUIntBig>(nVal)); |
5321 | 0 | } |
5322 | 0 | return dfVal; |
5323 | 0 | } |
5324 | | |
5325 | | /************************************************************************/ |
5326 | | /* GDALGetCompressionFormatForJPEG() */ |
5327 | | /************************************************************************/ |
5328 | | |
5329 | | //! @cond Doxygen_Suppress |
5330 | | std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp) |
5331 | 0 | { |
5332 | 0 | std::string osRet; |
5333 | 0 | const auto nSavedPos = VSIFTellL(fp); |
5334 | 0 | GByte abyMarkerHeader[4]; |
5335 | 0 | if (VSIFSeekL(fp, 0, SEEK_SET) == 0 && |
5336 | 0 | VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 && |
5337 | 0 | abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8) |
5338 | 0 | { |
5339 | 0 | osRet = "JPEG"; |
5340 | 0 | bool bHasAPP14Adobe = false; |
5341 | 0 | GByte abyAPP14AdobeMarkerData[14 - 2] = {0}; |
5342 | 0 | int nNumComponents = 0; |
5343 | 0 | while (true) |
5344 | 0 | { |
5345 | 0 | const auto nCurPos = VSIFTellL(fp); |
5346 | 0 | if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1) |
5347 | 0 | break; |
5348 | 0 | if (abyMarkerHeader[0] != 0xFF) |
5349 | 0 | break; |
5350 | 0 | const GByte markerType = abyMarkerHeader[1]; |
5351 | 0 | const size_t nMarkerSize = |
5352 | 0 | abyMarkerHeader[2] * 256 + abyMarkerHeader[3]; |
5353 | 0 | if (nMarkerSize < 2) |
5354 | 0 | break; |
5355 | 0 | if (markerType >= 0xC0 && markerType <= 0xCF && |
5356 | 0 | markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC) |
5357 | 0 | { |
5358 | 0 | switch (markerType) |
5359 | 0 | { |
5360 | 0 | case 0xC0: |
5361 | 0 | osRet += ";frame_type=SOF0_baseline"; |
5362 | 0 | break; |
5363 | 0 | case 0xC1: |
5364 | 0 | osRet += ";frame_type=SOF1_extended_sequential"; |
5365 | 0 | break; |
5366 | 0 | case 0xC2: |
5367 | 0 | osRet += ";frame_type=SOF2_progressive_huffman"; |
5368 | 0 | break; |
5369 | 0 | case 0xC3: |
5370 | 0 | osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_" |
5371 | 0 | "supported=no"; |
5372 | 0 | break; |
5373 | 0 | case 0xC5: |
5374 | 0 | osRet += ";frame_type=" |
5375 | 0 | "SOF5_differential_sequential_huffman;" |
5376 | 0 | "libjpeg_supported=no"; |
5377 | 0 | break; |
5378 | 0 | case 0xC6: |
5379 | 0 | osRet += ";frame_type=SOF6_differential_progressive_" |
5380 | 0 | "huffman;libjpeg_supported=no"; |
5381 | 0 | break; |
5382 | 0 | case 0xC7: |
5383 | 0 | osRet += ";frame_type=" |
5384 | 0 | "SOF7_differential_lossless_huffman;" |
5385 | 0 | "libjpeg_supported=no"; |
5386 | 0 | break; |
5387 | 0 | case 0xC9: |
5388 | 0 | osRet += ";frame_type=" |
5389 | 0 | "SOF9_extended_sequential_arithmetic"; |
5390 | 0 | break; |
5391 | 0 | case 0xCA: |
5392 | 0 | osRet += ";frame_type=SOF10_progressive_arithmetic"; |
5393 | 0 | break; |
5394 | 0 | case 0xCB: |
5395 | 0 | osRet += ";frame_type=" |
5396 | 0 | "SOF11_lossless_arithmetic;libjpeg_" |
5397 | 0 | "supported=no"; |
5398 | 0 | break; |
5399 | 0 | case 0xCD: |
5400 | 0 | osRet += ";frame_type=SOF13_differential_sequential_" |
5401 | 0 | "arithmetic;libjpeg_supported=no"; |
5402 | 0 | break; |
5403 | 0 | case 0xCE: |
5404 | 0 | osRet += ";frame_type=SOF14_differential_progressive_" |
5405 | 0 | "arithmetic;libjpeg_supported=no"; |
5406 | 0 | break; |
5407 | 0 | case 0xCF: |
5408 | 0 | osRet += ";frame_type=SOF15_differential_lossless_" |
5409 | 0 | "arithmetic;libjpeg_supported=no"; |
5410 | 0 | break; |
5411 | 0 | default: |
5412 | 0 | break; |
5413 | 0 | } |
5414 | 0 | GByte abySegmentBegin[6]; |
5415 | 0 | if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1, |
5416 | 0 | fp) != 1) |
5417 | 0 | break; |
5418 | 0 | osRet += ";bit_depth="; |
5419 | 0 | osRet += CPLSPrintf("%d", abySegmentBegin[0]); |
5420 | 0 | nNumComponents = abySegmentBegin[5]; |
5421 | 0 | osRet += ";num_components="; |
5422 | 0 | osRet += CPLSPrintf("%d", nNumComponents); |
5423 | 0 | if (nNumComponents == 3) |
5424 | 0 | { |
5425 | 0 | GByte abySegmentNext[3 * 3]; |
5426 | 0 | if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1, |
5427 | 0 | fp) != 1) |
5428 | 0 | break; |
5429 | 0 | if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 && |
5430 | 0 | abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 && |
5431 | 0 | abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11) |
5432 | 0 | { |
5433 | | // no subsampling |
5434 | 0 | osRet += ";subsampling=4:4:4"; |
5435 | 0 | } |
5436 | 0 | else if (abySegmentNext[0] == 1 && |
5437 | 0 | abySegmentNext[1] == 0x22 && |
5438 | 0 | abySegmentNext[3] == 2 && |
5439 | 0 | abySegmentNext[4] == 0x11 && |
5440 | 0 | abySegmentNext[6] == 3 && |
5441 | 0 | abySegmentNext[7] == 0x11) |
5442 | 0 | { |
5443 | | // classic subsampling |
5444 | 0 | osRet += ";subsampling=4:2:0"; |
5445 | 0 | } |
5446 | 0 | else if (abySegmentNext[0] == 1 && |
5447 | 0 | abySegmentNext[1] == 0x21 && |
5448 | 0 | abySegmentNext[3] == 2 && |
5449 | 0 | abySegmentNext[4] == 0x11 && |
5450 | 0 | abySegmentNext[6] == 3 && |
5451 | 0 | abySegmentNext[7] == 0x11) |
5452 | 0 | { |
5453 | 0 | osRet += ";subsampling=4:2:2"; |
5454 | 0 | } |
5455 | 0 | } |
5456 | 0 | } |
5457 | 0 | else if (markerType == 0xEE && nMarkerSize == 14) |
5458 | 0 | { |
5459 | 0 | if (VSIFReadL(abyAPP14AdobeMarkerData, |
5460 | 0 | sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 && |
5461 | 0 | memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) == |
5462 | 0 | 0) |
5463 | 0 | { |
5464 | 0 | bHasAPP14Adobe = true; |
5465 | 0 | } |
5466 | 0 | } |
5467 | 0 | else if (markerType == 0xDA) |
5468 | 0 | { |
5469 | | // Start of scan |
5470 | 0 | break; |
5471 | 0 | } |
5472 | 0 | VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET); |
5473 | 0 | } |
5474 | 0 | std::string osColorspace; |
5475 | 0 | if (bHasAPP14Adobe) |
5476 | 0 | { |
5477 | 0 | if (abyAPP14AdobeMarkerData[11] == 0) |
5478 | 0 | { |
5479 | 0 | if (nNumComponents == 3) |
5480 | 0 | osColorspace = "RGB"; |
5481 | 0 | else if (nNumComponents == 4) |
5482 | 0 | osColorspace = "CMYK"; |
5483 | 0 | } |
5484 | 0 | else if (abyAPP14AdobeMarkerData[11] == 1) |
5485 | 0 | { |
5486 | 0 | osColorspace = "YCbCr"; |
5487 | 0 | } |
5488 | 0 | else if (abyAPP14AdobeMarkerData[11] == 2) |
5489 | 0 | { |
5490 | 0 | osColorspace = "YCCK"; |
5491 | 0 | } |
5492 | 0 | } |
5493 | 0 | else |
5494 | 0 | { |
5495 | 0 | if (nNumComponents == 3) |
5496 | 0 | osColorspace = "YCbCr"; |
5497 | 0 | else if (nNumComponents == 4) |
5498 | 0 | osColorspace = "CMYK"; |
5499 | 0 | } |
5500 | 0 | osRet += ";colorspace="; |
5501 | 0 | if (!osColorspace.empty()) |
5502 | 0 | osRet += osColorspace; |
5503 | 0 | else |
5504 | 0 | osRet += "unknown"; |
5505 | 0 | } |
5506 | 0 | if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0) |
5507 | 0 | { |
5508 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
5509 | 0 | "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed"); |
5510 | 0 | } |
5511 | 0 | return osRet; |
5512 | 0 | } |
5513 | | |
5514 | | std::string GDALGetCompressionFormatForJPEG(const void *pBuffer, |
5515 | | size_t nBufferSize) |
5516 | 0 | { |
5517 | 0 | VSILFILE *fp = VSIFileFromMemBuffer( |
5518 | 0 | nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize, |
5519 | 0 | false); |
5520 | 0 | std::string osRet = GDALGetCompressionFormatForJPEG(fp); |
5521 | 0 | VSIFCloseL(fp); |
5522 | 0 | return osRet; |
5523 | 0 | } |
5524 | | |
5525 | | //! @endcond |
5526 | | |
5527 | | /************************************************************************/ |
5528 | | /* GDALGetNoDataReplacementValue() */ |
5529 | | /************************************************************************/ |
5530 | | |
5531 | | /** |
5532 | | * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue |
5533 | | * is out of range for the specified data type (dt). |
5534 | | * For UInt64 and Int64 data type this function cannot reliably trusted |
5535 | | * because their nodata values might not always be representable exactly |
5536 | | * as a double, in particular the maximum absolute value for those types |
5537 | | * is 2^53. |
5538 | | * |
5539 | | * The replacement value is a value that can be used in a computation |
5540 | | * whose result would match by accident the nodata value, whereas it is |
5541 | | * meant to be valid. For example, for a dataset with a nodata value of 0, |
5542 | | * when averaging -1 and 1, one would get normally a value of 0. The |
5543 | | * replacement nodata value can then be substituted to that 0 value to still |
5544 | | * get a valid value, as close as practical to the true value, while being |
5545 | | * different from the nodata value. |
5546 | | * |
5547 | | * @param dt Data type |
5548 | | * @param dfNoDataValue The no data value |
5549 | | |
5550 | | * @since GDAL 3.9 |
5551 | | */ |
5552 | | double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue) |
5553 | 0 | { |
5554 | | |
5555 | | // The logic here is to check if the value is out of range for the |
5556 | | // specified data type and return a replacement value if it is, return |
5557 | | // 0 otherwise. |
5558 | 0 | double dfReplacementVal = dfNoDataValue; |
5559 | 0 | if (dt == GDT_Byte) |
5560 | 0 | { |
5561 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5562 | 0 | cpl::NumericLimits<uint8_t>::lowest(), |
5563 | 0 | cpl::NumericLimits<uint8_t>::max())) |
5564 | 0 | { |
5565 | 0 | return 0; |
5566 | 0 | } |
5567 | 0 | if (dfNoDataValue == cpl::NumericLimits<unsigned char>::max()) |
5568 | 0 | dfReplacementVal = cpl::NumericLimits<unsigned char>::max() - 1; |
5569 | 0 | else |
5570 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5571 | 0 | } |
5572 | 0 | else if (dt == GDT_Int8) |
5573 | 0 | { |
5574 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5575 | 0 | cpl::NumericLimits<int8_t>::lowest(), |
5576 | 0 | cpl::NumericLimits<int8_t>::max())) |
5577 | 0 | { |
5578 | 0 | return 0; |
5579 | 0 | } |
5580 | 0 | if (dfNoDataValue == cpl::NumericLimits<GInt8>::max()) |
5581 | 0 | dfReplacementVal = cpl::NumericLimits<GInt8>::max() - 1; |
5582 | 0 | else |
5583 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5584 | 0 | } |
5585 | 0 | else if (dt == GDT_UInt16) |
5586 | 0 | { |
5587 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5588 | 0 | cpl::NumericLimits<uint16_t>::lowest(), |
5589 | 0 | cpl::NumericLimits<uint16_t>::max())) |
5590 | 0 | { |
5591 | 0 | return 0; |
5592 | 0 | } |
5593 | 0 | if (dfNoDataValue == cpl::NumericLimits<GUInt16>::max()) |
5594 | 0 | dfReplacementVal = cpl::NumericLimits<GUInt16>::max() - 1; |
5595 | 0 | else |
5596 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5597 | 0 | } |
5598 | 0 | else if (dt == GDT_Int16) |
5599 | 0 | { |
5600 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5601 | 0 | cpl::NumericLimits<int16_t>::lowest(), |
5602 | 0 | cpl::NumericLimits<int16_t>::max())) |
5603 | 0 | { |
5604 | 0 | return 0; |
5605 | 0 | } |
5606 | 0 | if (dfNoDataValue == cpl::NumericLimits<GInt16>::max()) |
5607 | 0 | dfReplacementVal = cpl::NumericLimits<GInt16>::max() - 1; |
5608 | 0 | else |
5609 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5610 | 0 | } |
5611 | 0 | else if (dt == GDT_UInt32) |
5612 | 0 | { |
5613 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5614 | 0 | cpl::NumericLimits<uint32_t>::lowest(), |
5615 | 0 | cpl::NumericLimits<uint32_t>::max())) |
5616 | 0 | { |
5617 | 0 | return 0; |
5618 | 0 | } |
5619 | 0 | if (dfNoDataValue == cpl::NumericLimits<GUInt32>::max()) |
5620 | 0 | dfReplacementVal = cpl::NumericLimits<GUInt32>::max() - 1; |
5621 | 0 | else |
5622 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5623 | 0 | } |
5624 | 0 | else if (dt == GDT_Int32) |
5625 | 0 | { |
5626 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5627 | 0 | cpl::NumericLimits<int32_t>::lowest(), |
5628 | 0 | cpl::NumericLimits<int32_t>::max())) |
5629 | 0 | { |
5630 | 0 | return 0; |
5631 | 0 | } |
5632 | 0 | if (dfNoDataValue == cpl::NumericLimits<int32_t>::max()) |
5633 | 0 | dfReplacementVal = cpl::NumericLimits<int32_t>::max() - 1; |
5634 | 0 | else |
5635 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5636 | 0 | } |
5637 | 0 | else if (dt == GDT_UInt64) |
5638 | 0 | { |
5639 | | // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 |
5640 | | // so we take the next lower value representable as a double 18446744073709549567 |
5641 | 0 | static const double dfMaxUInt64Value{ |
5642 | 0 | std::nextafter( |
5643 | 0 | static_cast<double>(cpl::NumericLimits<uint64_t>::max()), 0) - |
5644 | 0 | 1}; |
5645 | |
|
5646 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5647 | 0 | cpl::NumericLimits<uint64_t>::lowest(), |
5648 | 0 | cpl::NumericLimits<uint64_t>::max())) |
5649 | 0 | { |
5650 | 0 | return 0; |
5651 | 0 | } |
5652 | | |
5653 | 0 | if (dfNoDataValue >= |
5654 | 0 | static_cast<double>(cpl::NumericLimits<uint64_t>::max())) |
5655 | 0 | dfReplacementVal = dfMaxUInt64Value; |
5656 | 0 | else |
5657 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5658 | 0 | } |
5659 | 0 | else if (dt == GDT_Int64) |
5660 | 0 | { |
5661 | | // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808 |
5662 | | // so we take the next lower value representable as a double 9223372036854774784 |
5663 | 0 | static const double dfMaxInt64Value{ |
5664 | 0 | std::nextafter( |
5665 | 0 | static_cast<double>(cpl::NumericLimits<int64_t>::max()), 0) - |
5666 | 0 | 1}; |
5667 | |
|
5668 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5669 | 0 | cpl::NumericLimits<int64_t>::lowest(), |
5670 | 0 | cpl::NumericLimits<int64_t>::max())) |
5671 | 0 | { |
5672 | 0 | return 0; |
5673 | 0 | } |
5674 | | |
5675 | 0 | if (dfNoDataValue >= |
5676 | 0 | static_cast<double>(cpl::NumericLimits<int64_t>::max())) |
5677 | 0 | dfReplacementVal = dfMaxInt64Value; |
5678 | 0 | else |
5679 | 0 | dfReplacementVal = dfNoDataValue + 1; |
5680 | 0 | } |
5681 | 0 | else if (dt == GDT_Float16) |
5682 | 0 | { |
5683 | |
|
5684 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5685 | 0 | cpl::NumericLimits<GFloat16>::lowest(), |
5686 | 0 | cpl::NumericLimits<GFloat16>::max())) |
5687 | 0 | { |
5688 | 0 | return 0; |
5689 | 0 | } |
5690 | | |
5691 | 0 | if (dfNoDataValue == cpl::NumericLimits<GFloat16>::max()) |
5692 | 0 | { |
5693 | 0 | using std::nextafter; |
5694 | 0 | dfReplacementVal = |
5695 | 0 | nextafter(static_cast<GFloat16>(dfNoDataValue), GFloat16(0.0f)); |
5696 | 0 | } |
5697 | 0 | else |
5698 | 0 | { |
5699 | 0 | using std::nextafter; |
5700 | 0 | dfReplacementVal = nextafter(static_cast<GFloat16>(dfNoDataValue), |
5701 | 0 | cpl::NumericLimits<GFloat16>::max()); |
5702 | 0 | } |
5703 | 0 | } |
5704 | 0 | else if (dt == GDT_Float32) |
5705 | 0 | { |
5706 | |
|
5707 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5708 | 0 | cpl::NumericLimits<float>::lowest(), |
5709 | 0 | cpl::NumericLimits<float>::max())) |
5710 | 0 | { |
5711 | 0 | return 0; |
5712 | 0 | } |
5713 | | |
5714 | 0 | if (dfNoDataValue == cpl::NumericLimits<float>::max()) |
5715 | 0 | { |
5716 | 0 | dfReplacementVal = |
5717 | 0 | std::nextafter(static_cast<float>(dfNoDataValue), 0.0f); |
5718 | 0 | } |
5719 | 0 | else |
5720 | 0 | { |
5721 | 0 | dfReplacementVal = std::nextafter(static_cast<float>(dfNoDataValue), |
5722 | 0 | cpl::NumericLimits<float>::max()); |
5723 | 0 | } |
5724 | 0 | } |
5725 | 0 | else if (dt == GDT_Float64) |
5726 | 0 | { |
5727 | 0 | if (GDALClampDoubleValue(dfNoDataValue, |
5728 | 0 | cpl::NumericLimits<double>::lowest(), |
5729 | 0 | cpl::NumericLimits<double>::max())) |
5730 | 0 | { |
5731 | 0 | return 0; |
5732 | 0 | } |
5733 | | |
5734 | 0 | if (dfNoDataValue == cpl::NumericLimits<double>::max()) |
5735 | 0 | { |
5736 | 0 | dfReplacementVal = std::nextafter(dfNoDataValue, 0.0); |
5737 | 0 | } |
5738 | 0 | else |
5739 | 0 | { |
5740 | 0 | dfReplacementVal = std::nextafter( |
5741 | 0 | dfNoDataValue, cpl::NumericLimits<double>::max()); |
5742 | 0 | } |
5743 | 0 | } |
5744 | | |
5745 | 0 | return dfReplacementVal; |
5746 | 0 | } |
5747 | | |
5748 | | /************************************************************************/ |
5749 | | /* GDALGetCacheDirectory() */ |
5750 | | /************************************************************************/ |
5751 | | |
5752 | | /** Return the root path of the GDAL cache. |
5753 | | * |
5754 | | * If the GDAL_CACHE_DIRECTORY configuration option is set, its value will |
5755 | | * be returned. |
5756 | | * Otherwise if the XDG_CACHE_HOME environment variable is set, |
5757 | | * ${XDG_CACHE_HOME}/.gdal will be returned. |
5758 | | * Otherwise ${HOME}/.gdal on Unix or$ ${USERPROFILE}/.gdal on Windows will |
5759 | | * be returned. |
5760 | | * Otherwise ${CPL_TMPDIR|TMPDIR|TEMP}/.gdal_${USERNAME|USER} will be returned. |
5761 | | * Otherwise empty string will be returned. |
5762 | | * |
5763 | | * @since GDAL 3.11 |
5764 | | */ |
5765 | | std::string GDALGetCacheDirectory() |
5766 | 0 | { |
5767 | 0 | if (const char *pszGDAL_CACHE_DIRECTORY = |
5768 | 0 | CPLGetConfigOption("GDAL_CACHE_DIRECTORY", nullptr)) |
5769 | 0 | { |
5770 | 0 | return pszGDAL_CACHE_DIRECTORY; |
5771 | 0 | } |
5772 | | |
5773 | 0 | if (const char *pszXDG_CACHE_HOME = |
5774 | 0 | CPLGetConfigOption("XDG_CACHE_HOME", nullptr)) |
5775 | 0 | { |
5776 | 0 | return CPLFormFilenameSafe(pszXDG_CACHE_HOME, "gdal", nullptr); |
5777 | 0 | } |
5778 | | |
5779 | | #ifdef _WIN32 |
5780 | | const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr); |
5781 | | #else |
5782 | 0 | const char *pszHome = CPLGetConfigOption("HOME", nullptr); |
5783 | 0 | #endif |
5784 | 0 | if (pszHome != nullptr) |
5785 | 0 | { |
5786 | 0 | return CPLFormFilenameSafe(pszHome, ".gdal", nullptr); |
5787 | 0 | } |
5788 | 0 | else |
5789 | 0 | { |
5790 | 0 | const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr); |
5791 | |
|
5792 | 0 | if (pszDir == nullptr) |
5793 | 0 | pszDir = CPLGetConfigOption("TMPDIR", nullptr); |
5794 | |
|
5795 | 0 | if (pszDir == nullptr) |
5796 | 0 | pszDir = CPLGetConfigOption("TEMP", nullptr); |
5797 | |
|
5798 | 0 | const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr); |
5799 | 0 | if (pszUsername == nullptr) |
5800 | 0 | pszUsername = CPLGetConfigOption("USER", nullptr); |
5801 | |
|
5802 | 0 | if (pszDir != nullptr && pszUsername != nullptr) |
5803 | 0 | { |
5804 | 0 | return CPLFormFilenameSafe( |
5805 | 0 | pszDir, CPLSPrintf(".gdal_%s", pszUsername), nullptr); |
5806 | 0 | } |
5807 | 0 | } |
5808 | 0 | return std::string(); |
5809 | 0 | } |
5810 | | |
5811 | | /************************************************************************/ |
5812 | | /* GDALDoesFileOrDatasetExist() */ |
5813 | | /************************************************************************/ |
5814 | | |
5815 | | /** Return whether a file already exists. |
5816 | | */ |
5817 | | bool GDALDoesFileOrDatasetExist(const char *pszName, const char **ppszType, |
5818 | | GDALDriver **ppDriver) |
5819 | 0 | { |
5820 | 0 | { |
5821 | 0 | CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler); |
5822 | 0 | GDALDriverH hDriver = GDALIdentifyDriver(pszName, nullptr); |
5823 | 0 | if (hDriver) |
5824 | 0 | { |
5825 | 0 | if (ppszType) |
5826 | 0 | *ppszType = "Dataset"; |
5827 | 0 | if (ppDriver) |
5828 | 0 | *ppDriver = GDALDriver::FromHandle(hDriver); |
5829 | 0 | return true; |
5830 | 0 | } |
5831 | 0 | } |
5832 | | |
5833 | 0 | VSIStatBufL sStat; |
5834 | 0 | if (VSIStatL(pszName, &sStat) == 0) |
5835 | 0 | { |
5836 | 0 | if (ppszType) |
5837 | 0 | *ppszType = VSI_ISDIR(sStat.st_mode) ? "Directory" : "File"; |
5838 | 0 | return true; |
5839 | 0 | } |
5840 | | |
5841 | 0 | return false; |
5842 | 0 | } |
5843 | | |
5844 | | /************************************************************************/ |
5845 | | /* GDALRescaleGeoTransform() */ |
5846 | | /************************************************************************/ |
5847 | | |
5848 | | /** Rescale a geotransform by multiplying its scale and rotation terms by |
5849 | | * the provided ratios. |
5850 | | * |
5851 | | * This is typically used to compute the geotransform matrix of an overview |
5852 | | * dataset from the full resolution dataset, where the ratios are the size |
5853 | | * of the full resolution dataset divided by the size of the overview. |
5854 | | */ |
5855 | | void GDALRescaleGeoTransform(double adfGeoTransform[6], double dfXRatio, |
5856 | | double dfYRatio) |
5857 | 0 | { |
5858 | 0 | adfGeoTransform[1] *= dfXRatio; |
5859 | 0 | adfGeoTransform[2] *= dfYRatio; |
5860 | 0 | adfGeoTransform[4] *= dfXRatio; |
5861 | 0 | adfGeoTransform[5] *= dfYRatio; |
5862 | 0 | } |