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