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.h"
54 #include "gdal.h"
55 #include "gdal_mdreader.h"
56 #include "gdal_priv.h"
57 #include "ogr_core.h"
58 #include "ogr_spatialref.h"
59 #include "ogr_geos.h"
60
61 CPL_CVSID("$Id: gdal_misc.cpp eb2f5b33947e577fc14b62e62f930728dc573f17 2021-09-14 00:16:31 +0200 Even Rouault $")
62
GetMinBitsForPair(const bool pabSigned[],const bool pabFloating[],const int panBits[])63 static int GetMinBitsForPair(
64 const bool pabSigned[], const bool pabFloating[], const int panBits[])
65 {
66 if(pabFloating[0] != pabFloating[1])
67 {
68 const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
69 const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
70
71 return std::max(panBits[nFloatingTypeIndex],
72 2 * panBits[nNotFloatingTypeIndex]);
73 }
74
75 if( pabSigned[0] != pabSigned[1] )
76 {
77 const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
78 const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
79
80 return std::max(panBits[nSignedTypeIndex],
81 2 * panBits[nUnsignedTypeIndex]);
82 }
83
84 return std::max(panBits[0], panBits[1]);
85 }
86
GetDataTypeElementSizeBits(GDALDataType eDataType)87 static int GetDataTypeElementSizeBits( GDALDataType eDataType )
88 {
89 switch( eDataType )
90 {
91 case GDT_Byte:
92 return 8;
93
94 case GDT_UInt16:
95 case GDT_Int16:
96 case GDT_CInt16:
97 return 16;
98
99 case GDT_UInt32:
100 case GDT_Int32:
101 case GDT_Float32:
102 case GDT_CInt32:
103 case GDT_CFloat32:
104 return 32;
105
106 case GDT_Float64:
107 case GDT_CFloat64:
108 return 64;
109
110 default:
111 return 0;
112 }
113 }
114
115 /************************************************************************/
116 /* GDALDataTypeUnion() */
117 /************************************************************************/
118
119 /**
120 * \brief Return the smallest data type that can fully express both input data
121 * types.
122 *
123 * @param eType1 first data type.
124 * @param eType2 second data type.
125 *
126 * @return a data type able to express eType1 and eType2.
127 */
128
129 GDALDataType CPL_STDCALL
GDALDataTypeUnion(GDALDataType eType1,GDALDataType eType2)130 GDALDataTypeUnion( GDALDataType eType1, GDALDataType eType2 )
131
132 {
133 const int panBits[] = {
134 GetDataTypeElementSizeBits( eType1 ),
135 GetDataTypeElementSizeBits( eType2 )
136 };
137
138 if( panBits[0] == 0 || panBits[1] == 0 )
139 return GDT_Unknown;
140
141 const bool pabSigned[] = {
142 CPL_TO_BOOL( GDALDataTypeIsSigned( eType1 ) ),
143 CPL_TO_BOOL( GDALDataTypeIsSigned( eType2 ) )
144 };
145
146 const bool bSigned = pabSigned[0] || pabSigned[1];
147 const bool pabFloating[] = {
148 CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
149 CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))
150 };
151 const bool bFloating = pabFloating[0] || pabFloating[1];
152 const bool bComplex =
153 CPL_TO_BOOL( GDALDataTypeIsComplex( eType1 ) ) ||
154 CPL_TO_BOOL( GDALDataTypeIsComplex( eType2 ) );
155
156 const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
157
158 return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
159 }
160
161 /************************************************************************/
162 /* GDALDataTypeUnionWithValue() */
163 /************************************************************************/
164
165 /**
166 * \brief Union a data type with the one found for a value
167 *
168 * @param eDT the first data type
169 * @param dValue the value for which to find a data type and union with eDT
170 * @param bComplex if the value is complex
171 *
172 * @return a data type able to express eDT and dValue.
173 * @since GDAL 2.3
174 */
GDALDataTypeUnionWithValue(GDALDataType eDT,double dValue,int bComplex)175 GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(
176 GDALDataType eDT, double dValue, int bComplex )
177 {
178 if( eDT == GDT_Float32 && !bComplex && static_cast<float>(dValue) == dValue )
179 return eDT;
180
181 const GDALDataType eDT2 = GDALFindDataTypeForValue(dValue, bComplex);
182 return GDALDataTypeUnion(eDT, eDT2);
183 }
184
185 /************************************************************************/
186 /* GetMinBitsForValue() */
187 /************************************************************************/
GetMinBitsForValue(double dValue)188 static int GetMinBitsForValue(double dValue)
189 {
190 if( round(dValue) == dValue )
191 {
192 if( dValue <= std::numeric_limits<GByte>::max() &&
193 dValue >= std::numeric_limits<GByte>::min() )
194 return 8;
195
196 if( dValue <= std::numeric_limits<GInt16>::max() &&
197 dValue >= std::numeric_limits<GInt16>::min() )
198 return 16;
199
200 if( dValue <= std::numeric_limits<GUInt16>::max() &&
201 dValue >= std::numeric_limits<GUInt16>::min() )
202 return 16;
203
204 if( dValue <= std::numeric_limits<GInt32>::max() &&
205 dValue >= std::numeric_limits<GInt32>::min() )
206 return 32;
207
208 if( dValue <= std::numeric_limits<GUInt32>::max() &&
209 dValue >= std::numeric_limits<GUInt32>::min() )
210 return 32;
211 }
212 else if( static_cast<float>(dValue) == dValue )
213 {
214 return 32;
215 }
216
217 return 64;
218 }
219
220 /************************************************************************/
221 /* GDALFindDataType() */
222 /************************************************************************/
223
224 /**
225 * \brief Finds the smallest data type able to support the given
226 * requirements
227 *
228 * @param nBits number of bits necessary
229 * @param bSigned if negative values are necessary
230 * @param bFloating if non-integer values necessary
231 * @param bComplex if complex values are necessary
232 *
233 * @return a best fit GDALDataType for supporting the requirements
234 * @since GDAL 2.3
235 */
GDALFindDataType(int nBits,int bSigned,int bFloating,int bComplex)236 GDALDataType CPL_STDCALL GDALFindDataType(
237 int nBits, int bSigned, int bFloating, int bComplex )
238 {
239 if( bSigned ) { nBits = std::max(nBits, 16); }
240 if( bComplex ) { nBits = std::max(nBits, !bSigned ? 32 : 16); } // we don't have complex unsigned data types, so for a complex uint16, promote to complex int32
241 if( bFloating ) { nBits = std::max(nBits, 32); }
242
243 if( nBits <= 8 ) { return GDT_Byte; }
244
245 if( nBits <= 16 )
246 {
247 if( bComplex ) return GDT_CInt16;
248 if( bSigned ) return GDT_Int16;
249 return GDT_UInt16;
250 }
251
252 if( nBits <= 32 )
253 {
254 if( bFloating )
255 {
256 if( bComplex ) return GDT_CFloat32;
257 return GDT_Float32;
258 }
259
260 if( bComplex ) return GDT_CInt32;
261 if( bSigned ) return GDT_Int32;
262 return GDT_UInt32;
263 }
264
265 if( bComplex )
266 return GDT_CFloat64;
267
268 return GDT_Float64;
269 }
270
271 /************************************************************************/
272 /* GDALFindDataTypeForValue() */
273 /************************************************************************/
274
275 /**
276 * \brief Finds the smallest data type able to support the provided value
277 *
278 * @param dValue value to support
279 * @param bComplex is the value complex
280 *
281 * @return a best fit GDALDataType for supporting the value
282 * @since GDAL 2.3
283 */
GDALFindDataTypeForValue(double dValue,int bComplex)284 GDALDataType CPL_STDCALL GDALFindDataTypeForValue(
285 double dValue, int bComplex )
286 {
287 const bool bFloating = round(dValue) != dValue;
288 const bool bSigned = bFloating || dValue < 0;
289 const int nBits = GetMinBitsForValue(dValue);
290
291 return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
292 }
293
294 /************************************************************************/
295 /* GDALGetDataTypeSizeBytes() */
296 /************************************************************************/
297
298 /**
299 * \brief Get data type size in <b>bytes</b>.
300 *
301 * Returns the size of a GDT_* type in bytes. In contrast,
302 * GDALGetDataTypeSize() returns the size in <b>bits</b>.
303 *
304 * @param eDataType type, such as GDT_Byte.
305 * @return the number of bytes or zero if it is not recognised.
306 */
307
GDALGetDataTypeSizeBytes(GDALDataType eDataType)308 int CPL_STDCALL GDALGetDataTypeSizeBytes( GDALDataType eDataType )
309
310 {
311 switch( eDataType )
312 {
313 case GDT_Byte:
314 return 1;
315
316 case GDT_UInt16:
317 case GDT_Int16:
318 return 2;
319
320 case GDT_UInt32:
321 case GDT_Int32:
322 case GDT_Float32:
323 case GDT_CInt16:
324 return 4;
325
326 case GDT_Float64:
327 case GDT_CInt32:
328 case GDT_CFloat32:
329 return 8;
330
331 case GDT_CFloat64:
332 return 16;
333
334 default:
335 return 0;
336 }
337 }
338
339 /************************************************************************/
340 /* GDALGetDataTypeSizeBits() */
341 /************************************************************************/
342
343 /**
344 * \brief Get data type size in <b>bits</b>.
345 *
346 * Returns the size of a GDT_* type in bits, <b>not bytes</b>! Use
347 * GDALGetDataTypeSizeBytes() for bytes.
348 *
349 * @param eDataType type, such as GDT_Byte.
350 * @return the number of bits or zero if it is not recognised.
351 */
352
GDALGetDataTypeSizeBits(GDALDataType eDataType)353 int CPL_STDCALL GDALGetDataTypeSizeBits( GDALDataType eDataType )
354
355 {
356 return GDALGetDataTypeSizeBytes( eDataType ) * 8;
357 }
358
359 /************************************************************************/
360 /* GDALGetDataTypeSize() */
361 /************************************************************************/
362
363 /**
364 * \brief Get data type size in bits. <b>Deprecated</b>.
365 *
366 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
367 *
368 * Use GDALGetDataTypeSizeBytes() for bytes.
369 * Use GDALGetDataTypeSizeBits() for bits.
370 *
371 * @param eDataType type, such as GDT_Byte.
372 * @return the number of bits or zero if it is not recognised.
373 */
374
GDALGetDataTypeSize(GDALDataType eDataType)375 int CPL_STDCALL GDALGetDataTypeSize( GDALDataType eDataType )
376
377 {
378 return GDALGetDataTypeSizeBytes( eDataType ) * 8;
379 }
380
381 /************************************************************************/
382 /* GDALDataTypeIsComplex() */
383 /************************************************************************/
384
385 /**
386 * \brief Is data type complex?
387 *
388 * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
389 * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
390 * component.
391 */
392
GDALDataTypeIsComplex(GDALDataType eDataType)393 int CPL_STDCALL GDALDataTypeIsComplex( GDALDataType eDataType )
394
395 {
396 switch( eDataType )
397 {
398 case GDT_CInt16:
399 case GDT_CInt32:
400 case GDT_CFloat32:
401 case GDT_CFloat64:
402 return TRUE;
403
404 default:
405 return FALSE;
406 }
407 }
408
409 /************************************************************************/
410 /* GDALDataTypeIsFloating() */
411 /************************************************************************/
412
413 /**
414 * \brief Is data type floating? (might be complex)
415 *
416 * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float64,
417 * GDT_CFloat32, GDT_CFloat64)
418 * @since GDAL 2.3
419 */
420
GDALDataTypeIsFloating(GDALDataType eDataType)421 int CPL_STDCALL GDALDataTypeIsFloating( GDALDataType eDataType )
422 {
423 switch( eDataType )
424 {
425 case GDT_Float32:
426 case GDT_Float64:
427 case GDT_CFloat32:
428 case GDT_CFloat64:
429 return TRUE;
430
431 default:
432 return FALSE;
433 }
434 }
435
436 /************************************************************************/
437 /* GDALDataTypeIsInteger() */
438 /************************************************************************/
439
440 /**
441 * \brief Is data type integer? (might be complex)
442 *
443 * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
444 * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
445 * @since GDAL 2.3
446 */
447
GDALDataTypeIsInteger(GDALDataType eDataType)448 int CPL_STDCALL GDALDataTypeIsInteger( GDALDataType eDataType )
449
450 {
451 switch( eDataType )
452 {
453 case GDT_Byte:
454 case GDT_Int16:
455 case GDT_UInt16:
456 case GDT_Int32:
457 case GDT_UInt32:
458 case GDT_CInt16:
459 case GDT_CInt32:
460 return TRUE;
461
462 default:
463 return FALSE;
464 }
465 }
466
467 /************************************************************************/
468 /* GDALDataTypeIsSigned() */
469 /************************************************************************/
470
471 /**
472 * \brief Is data type signed?
473 *
474 * @return TRUE if the passed type is signed.
475 * @since GDAL 2.3
476 */
477
GDALDataTypeIsSigned(GDALDataType eDataType)478 int CPL_STDCALL GDALDataTypeIsSigned( GDALDataType eDataType )
479 {
480 switch( eDataType )
481 {
482 case GDT_Byte:
483 case GDT_UInt16:
484 case GDT_UInt32:
485 return FALSE;
486
487 default:
488 return TRUE;
489 }
490 }
491
492 /************************************************************************/
493 /* GDALDataTypeIsConversionLossy() */
494 /************************************************************************/
495
496 /**
497 * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
498 *
499 * @param eTypeFrom input datatype
500 * @param eTypeTo output datatype
501 * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
502 * @since GDAL 2.3
503 */
504
GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,GDALDataType eTypeTo)505 int CPL_STDCALL GDALDataTypeIsConversionLossy( GDALDataType eTypeFrom,
506 GDALDataType eTypeTo )
507 {
508 // E.g cfloat32 -> float32
509 if( GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo) )
510 return TRUE;
511
512 eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
513 eTypeTo = GDALGetNonComplexDataType(eTypeTo);
514
515 if( GDALDataTypeIsInteger(eTypeTo) )
516 {
517 // E.g. float32 -> int32
518 if( GDALDataTypeIsFloating(eTypeFrom) )
519 return TRUE;
520
521 // E.g. Int16 to UInt16
522 const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
523 const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
524 if( bIsFromSigned && !bIsToSigned )
525 return TRUE;
526
527 // E.g UInt32 to UInt16
528 const int nFromSize = GDALGetDataTypeSize(eTypeFrom);
529 const int nToSize = GDALGetDataTypeSize(eTypeTo);
530 if( nFromSize > nToSize )
531 return TRUE;
532
533 // E.g UInt16 to Int16
534 if( nFromSize == nToSize && !bIsFromSigned && bIsToSigned )
535 return TRUE;
536
537 return FALSE;
538 }
539
540 if( eTypeTo == GDT_Float32 && (eTypeFrom == GDT_Int32 ||
541 eTypeFrom == GDT_UInt32 ||
542 eTypeFrom == GDT_Float64) )
543 {
544 return TRUE;
545 }
546
547 return FALSE;
548 }
549
550 /************************************************************************/
551 /* GDALGetDataTypeName() */
552 /************************************************************************/
553
554 /**
555 * \brief Get name of data type.
556 *
557 * Returns a symbolic name for the data type. This is essentially the
558 * the enumerated item name with the GDT_ prefix removed. So GDT_Byte returns
559 * "Byte". The returned strings are static strings and should not be modified
560 * or freed by the application. These strings are useful for reporting
561 * datatypes in debug statements, errors and other user output.
562 *
563 * @param eDataType type to get name of.
564 * @return string corresponding to existing data type
565 * or NULL pointer if invalid type given.
566 */
567
GDALGetDataTypeName(GDALDataType eDataType)568 const char * CPL_STDCALL GDALGetDataTypeName( GDALDataType eDataType )
569
570 {
571 switch( eDataType )
572 {
573 case GDT_Unknown:
574 return "Unknown";
575
576 case GDT_Byte:
577 return "Byte";
578
579 case GDT_UInt16:
580 return "UInt16";
581
582 case GDT_Int16:
583 return "Int16";
584
585 case GDT_UInt32:
586 return "UInt32";
587
588 case GDT_Int32:
589 return "Int32";
590
591 case GDT_Float32:
592 return "Float32";
593
594 case GDT_Float64:
595 return "Float64";
596
597 case GDT_CInt16:
598 return "CInt16";
599
600 case GDT_CInt32:
601 return "CInt32";
602
603 case GDT_CFloat32:
604 return "CFloat32";
605
606 case GDT_CFloat64:
607 return "CFloat64";
608
609 default:
610 return nullptr;
611 }
612 }
613
614 /************************************************************************/
615 /* GDALGetDataTypeByName() */
616 /************************************************************************/
617
618 /**
619 * \brief Get data type by symbolic name.
620 *
621 * Returns a data type corresponding to the given symbolic name. This
622 * function is opposite to the GDALGetDataTypeName().
623 *
624 * @param pszName string containing the symbolic name of the type.
625 *
626 * @return GDAL data type.
627 */
628
GDALGetDataTypeByName(const char * pszName)629 GDALDataType CPL_STDCALL GDALGetDataTypeByName( const char *pszName )
630
631 {
632 VALIDATE_POINTER1( pszName, "GDALGetDataTypeByName", GDT_Unknown );
633
634 for( int iType = 1; iType < GDT_TypeCount; iType++ )
635 {
636 const auto eType = static_cast<GDALDataType>(iType);
637 if( GDALGetDataTypeName(eType) != nullptr
638 && EQUAL(GDALGetDataTypeName(eType), pszName) )
639 {
640 return eType;
641 }
642 }
643
644 return GDT_Unknown;
645 }
646
647 /************************************************************************/
648 /* GDALAdjustValueToDataType() */
649 /************************************************************************/
650
ClampAndRound(double & dfValue,bool & bClamped,bool & bRounded)651 template<class T> static inline void ClampAndRound(
652 double& dfValue, bool& bClamped, bool &bRounded )
653 {
654 // TODO(schwehr): Rework this template. ::min() versus ::lowest.
655
656 if (dfValue < std::numeric_limits<T>::min())
657 {
658 bClamped = true;
659 dfValue = static_cast<double>(std::numeric_limits<T>::min());
660 }
661 else if (dfValue > std::numeric_limits<T>::max())
662 {
663 bClamped = true;
664 dfValue = static_cast<double>(std::numeric_limits<T>::max());
665 }
666 else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
667 {
668 bRounded = true;
669 dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
670 }
671 }
672
673 /**
674 * \brief Adjust a value to the output data type
675 *
676 * Adjustment consist in clamping to minimum/maxmimum values of the data type
677 * and rounding for integral types.
678 *
679 * @param eDT target data type.
680 * @param dfValue value to adjust.
681 * @param pbClamped pointer to a integer(boolean) to indicate if clamping has been made, or NULL
682 * @param pbRounded pointer to a integer(boolean) to indicate if rounding has been made, or NULL
683 *
684 * @return adjusted value
685 * @since GDAL 2.1
686 */
687
GDALAdjustValueToDataType(GDALDataType eDT,double dfValue,int * pbClamped,int * pbRounded)688 double GDALAdjustValueToDataType(
689 GDALDataType eDT, double dfValue, int* pbClamped, int* pbRounded )
690 {
691 bool bClamped = false;
692 bool bRounded = false;
693 switch(eDT)
694 {
695 case GDT_Byte:
696 ClampAndRound<GByte>(dfValue, bClamped, bRounded);
697 break;
698 case GDT_Int16:
699 ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
700 break;
701 case GDT_UInt16:
702 ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
703 break;
704 case GDT_Int32:
705 ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
706 break;
707 case GDT_UInt32:
708 ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
709 break;
710 case GDT_Float32:
711 {
712 if( !CPLIsFinite(dfValue) )
713 break;
714
715 // TODO(schwehr): ::min() versus ::lowest.
716 // Use ClampAndRound after it has been fixed.
717 if ( dfValue < -std::numeric_limits<float>::max())
718 {
719 bClamped = TRUE;
720 dfValue = static_cast<double>(
721 -std::numeric_limits<float>::max());
722 }
723 else if (dfValue > std::numeric_limits<float>::max())
724 {
725 bClamped = TRUE;
726 dfValue = static_cast<double>(
727 std::numeric_limits<float>::max());
728 }
729 else
730 {
731 // Intentionally loose precision.
732 // TODO(schwehr): Is the double cast really necessary?
733 // If so, why? What will fail?
734 dfValue = static_cast<double>(static_cast<float>(dfValue));
735 }
736 break;
737 }
738 default:
739 break;
740 }
741 if( pbClamped )
742 *pbClamped = bClamped;
743 if( pbRounded )
744 *pbRounded = bRounded;
745 return dfValue;
746 }
747
748 /************************************************************************/
749 /* GDALGetNonComplexDataType() */
750 /************************************************************************/
751 /**
752 * \brief Return the base data type for the specified input.
753 *
754 * If the input data type is complex this function returns the base type
755 * i.e. the data type of the real and imaginary parts (non-complex).
756 * If the input data type is already non-complex, then it is returned
757 * unchanged.
758 *
759 * @param eDataType type, such as GDT_CFloat32.
760 *
761 * @return GDAL data type.
762 */
GDALGetNonComplexDataType(GDALDataType eDataType)763 GDALDataType CPL_STDCALL GDALGetNonComplexDataType( GDALDataType eDataType )
764 {
765 switch( eDataType )
766 {
767 case GDT_CInt16:
768 return GDT_Int16;
769 case GDT_CInt32:
770 return GDT_Int32;
771 case GDT_CFloat32:
772 return GDT_Float32;
773 case GDT_CFloat64:
774 return GDT_Float64;
775 default:
776 return eDataType;
777 }
778 }
779
780 /************************************************************************/
781 /* GDALGetAsyncStatusTypeByName() */
782 /************************************************************************/
783 /**
784 * Get AsyncStatusType by symbolic name.
785 *
786 * Returns a data type corresponding to the given symbolic name. This
787 * function is opposite to the GDALGetAsyncStatusTypeName().
788 *
789 * @param pszName string containing the symbolic name of the type.
790 *
791 * @return GDAL AsyncStatus type.
792 */
GDALGetAsyncStatusTypeByName(const char * pszName)793 GDALAsyncStatusType CPL_DLL CPL_STDCALL GDALGetAsyncStatusTypeByName(
794 const char *pszName )
795 {
796 VALIDATE_POINTER1( pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
797
798 for( int iType = 0; iType < GARIO_TypeCount; iType++ )
799 {
800 const auto eType = static_cast<GDALAsyncStatusType>(iType);
801 if( GDALGetAsyncStatusTypeName(eType) != nullptr
802 && EQUAL(GDALGetAsyncStatusTypeName(eType), pszName) )
803 {
804 return eType;
805 }
806 }
807
808 return GARIO_ERROR;
809 }
810
811 /************************************************************************/
812 /* GDALGetAsyncStatusTypeName() */
813 /************************************************************************/
814
815 /**
816 * Get name of AsyncStatus data type.
817 *
818 * Returns a symbolic name for the AsyncStatus data type. This is essentially the
819 * the enumerated item name with the GARIO_ prefix removed. So GARIO_COMPLETE returns
820 * "COMPLETE". The returned strings are static strings and should not be modified
821 * or freed by the application. These strings are useful for reporting
822 * datatypes in debug statements, errors and other user output.
823 *
824 * @param eAsyncStatusType type to get name of.
825 * @return string corresponding to type.
826 */
827
GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)828 const char * CPL_STDCALL GDALGetAsyncStatusTypeName(
829 GDALAsyncStatusType eAsyncStatusType )
830
831 {
832 switch( eAsyncStatusType )
833 {
834 case GARIO_PENDING:
835 return "PENDING";
836
837 case GARIO_UPDATE:
838 return "UPDATE";
839
840 case GARIO_ERROR:
841 return "ERROR";
842
843 case GARIO_COMPLETE:
844 return "COMPLETE";
845
846 default:
847 return nullptr;
848 }
849 }
850
851 /************************************************************************/
852 /* GDALGetPaletteInterpretationName() */
853 /************************************************************************/
854
855 /**
856 * \brief Get name of palette interpretation
857 *
858 * Returns a symbolic name for the palette interpretation. This is the
859 * the enumerated item name with the GPI_ prefix removed. So GPI_Gray returns
860 * "Gray". The returned strings are static strings and should not be modified
861 * or freed by the application.
862 *
863 * @param eInterp palette interpretation to get name of.
864 * @return string corresponding to palette interpretation.
865 */
866
GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)867 const char *GDALGetPaletteInterpretationName( GDALPaletteInterp eInterp )
868
869 {
870 switch( eInterp )
871 {
872 case GPI_Gray:
873 return "Gray";
874
875 case GPI_RGB:
876 return "RGB";
877
878 case GPI_CMYK:
879 return "CMYK";
880
881 case GPI_HLS:
882 return "HLS";
883
884 default:
885 return "Unknown";
886 }
887 }
888
889 /************************************************************************/
890 /* GDALGetColorInterpretationName() */
891 /************************************************************************/
892
893 /**
894 * \brief Get name of color interpretation
895 *
896 * Returns a symbolic name for the color interpretation. This is derived from
897 * the enumerated item name with the GCI_ prefix removed, but there are some
898 * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
899 * The returned strings are static strings and should not be modified
900 * or freed by the application.
901 *
902 * @param eInterp color interpretation to get name of.
903 * @return string corresponding to color interpretation
904 * or NULL pointer if invalid enumerator given.
905 */
906
GDALGetColorInterpretationName(GDALColorInterp eInterp)907 const char *GDALGetColorInterpretationName( GDALColorInterp eInterp )
908
909 {
910 switch( eInterp )
911 {
912 case GCI_Undefined:
913 return "Undefined";
914
915 case GCI_GrayIndex:
916 return "Gray";
917
918 case GCI_PaletteIndex:
919 return "Palette";
920
921 case GCI_RedBand:
922 return "Red";
923
924 case GCI_GreenBand:
925 return "Green";
926
927 case GCI_BlueBand:
928 return "Blue";
929
930 case GCI_AlphaBand:
931 return "Alpha";
932
933 case GCI_HueBand:
934 return "Hue";
935
936 case GCI_SaturationBand:
937 return "Saturation";
938
939 case GCI_LightnessBand:
940 return "Lightness";
941
942 case GCI_CyanBand:
943 return "Cyan";
944
945 case GCI_MagentaBand:
946 return "Magenta";
947
948 case GCI_YellowBand:
949 return "Yellow";
950
951 case GCI_BlackBand:
952 return "Black";
953
954 case GCI_YCbCr_YBand:
955 return "YCbCr_Y";
956
957 case GCI_YCbCr_CbBand:
958 return "YCbCr_Cb";
959
960 case GCI_YCbCr_CrBand:
961 return "YCbCr_Cr";
962
963 default:
964 return "Unknown";
965 }
966 }
967
968 /************************************************************************/
969 /* GDALGetColorInterpretationByName() */
970 /************************************************************************/
971
972 /**
973 * \brief Get color interpretation by symbolic name.
974 *
975 * Returns a color interpretation corresponding to the given symbolic name. This
976 * function is opposite to the GDALGetColorInterpretationName().
977 *
978 * @param pszName string containing the symbolic name of the color
979 * interpretation.
980 *
981 * @return GDAL color interpretation.
982 *
983 * @since GDAL 1.7.0
984 */
985
GDALGetColorInterpretationByName(const char * pszName)986 GDALColorInterp GDALGetColorInterpretationByName( const char *pszName )
987
988 {
989 VALIDATE_POINTER1( pszName, "GDALGetColorInterpretationByName",
990 GCI_Undefined );
991
992 for( int iType = 0; iType <= GCI_Max; iType++ )
993 {
994 if( EQUAL(GDALGetColorInterpretationName(static_cast<GDALColorInterp>(iType)),
995 pszName) )
996 {
997 return static_cast<GDALColorInterp>(iType);
998 }
999 }
1000
1001 return GCI_Undefined;
1002 }
1003
1004 /************************************************************************/
1005 /* GDALGetRandomRasterSample() */
1006 /************************************************************************/
1007
1008 /** Undocumented
1009 * @param hBand undocumented.
1010 * @param nSamples undocumented.
1011 * @param pafSampleBuf undocumented.
1012 * @return undocumented
1013 */
1014 int CPL_STDCALL
GDALGetRandomRasterSample(GDALRasterBandH hBand,int nSamples,float * pafSampleBuf)1015 GDALGetRandomRasterSample( GDALRasterBandH hBand, int nSamples,
1016 float *pafSampleBuf )
1017
1018 {
1019 VALIDATE_POINTER1( hBand, "GDALGetRandomRasterSample", 0 );
1020
1021 GDALRasterBand *poBand;
1022
1023 poBand = GDALRasterBand::FromHandle(GDALGetRasterSampleOverview( hBand, nSamples ));
1024 CPLAssert( nullptr != poBand );
1025
1026 /* -------------------------------------------------------------------- */
1027 /* Figure out the ratio of blocks we will read to get an */
1028 /* approximate value. */
1029 /* -------------------------------------------------------------------- */
1030 int bGotNoDataValue = FALSE;
1031
1032 double dfNoDataValue = poBand->GetNoDataValue( &bGotNoDataValue );
1033
1034 int nBlockXSize = 0;
1035 int nBlockYSize = 0;
1036 poBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
1037
1038 const int nBlocksPerRow =
1039 (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
1040 const int nBlocksPerColumn =
1041 (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
1042
1043 const int nBlockPixels = nBlockXSize * nBlockYSize;
1044 const int nBlockCount = nBlocksPerRow * nBlocksPerColumn;
1045
1046 if( nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0
1047 || nBlockCount == 0 )
1048 {
1049 CPLError( CE_Failure, CPLE_AppDefined,
1050 "GDALGetRandomRasterSample(): returning because band"
1051 " appears degenerate." );
1052
1053 return FALSE;
1054 }
1055
1056 int nSampleRate = static_cast<int>(
1057 std::max(1.0, sqrt( static_cast<double>(nBlockCount) ) - 2.0));
1058
1059 if( nSampleRate == nBlocksPerRow && nSampleRate > 1 )
1060 nSampleRate--;
1061
1062 while( nSampleRate > 1
1063 && ((nBlockCount-1) / nSampleRate + 1) * nBlockPixels < nSamples )
1064 nSampleRate--;
1065
1066 int nBlockSampleRate = 1;
1067
1068 if ((nSamples / ((nBlockCount-1) / nSampleRate + 1)) != 0)
1069 nBlockSampleRate =
1070 std::max( 1,
1071 nBlockPixels /
1072 (nSamples / ((nBlockCount-1) / nSampleRate + 1)));
1073
1074 int nActualSamples = 0;
1075
1076 for( int iSampleBlock = 0;
1077 iSampleBlock < nBlockCount;
1078 iSampleBlock += nSampleRate )
1079 {
1080
1081 const int iYBlock = iSampleBlock / nBlocksPerRow;
1082 const int iXBlock = iSampleBlock - nBlocksPerRow * iYBlock;
1083
1084 GDALRasterBlock * const poBlock =
1085 poBand->GetLockedBlockRef( iXBlock, iYBlock );
1086 if( poBlock == nullptr )
1087 continue;
1088 void* pDataRef = poBlock->GetDataRef();
1089
1090 int iXValid = nBlockXSize;
1091 if( (iXBlock + 1) * nBlockXSize > poBand->GetXSize() )
1092 iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
1093
1094 int iYValid = nBlockYSize;
1095 if( (iYBlock + 1) * nBlockYSize > poBand->GetYSize() )
1096 iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
1097
1098 int iRemainder = 0;
1099
1100 for( int iY = 0; iY < iYValid; iY++ )
1101 {
1102 int iX = iRemainder; // Used after for.
1103 for( ; iX < iXValid; iX += nBlockSampleRate )
1104 {
1105 double dfValue = 0.0;
1106 const int iOffset = iX + iY * nBlockXSize;
1107
1108 switch( poBlock->GetDataType() )
1109 {
1110 case GDT_Byte:
1111 dfValue = reinterpret_cast<const GByte*>(pDataRef)[iOffset];
1112 break;
1113 case GDT_UInt16:
1114 dfValue = reinterpret_cast<const GUInt16*>(pDataRef)[iOffset];
1115 break;
1116 case GDT_Int16:
1117 dfValue = reinterpret_cast<const GInt16*>(pDataRef)[iOffset];
1118 break;
1119 case GDT_UInt32:
1120 dfValue = reinterpret_cast<const GUInt32*>(pDataRef)[iOffset];
1121 break;
1122 case GDT_Int32:
1123 dfValue = reinterpret_cast<const GInt32*>(pDataRef)[iOffset];
1124 break;
1125 case GDT_Float32:
1126 dfValue = reinterpret_cast<const float*>(pDataRef)[iOffset];
1127 break;
1128 case GDT_Float64:
1129 dfValue = reinterpret_cast<const double*>(pDataRef)[iOffset];
1130 break;
1131 case GDT_CInt16:
1132 {
1133 // TODO(schwehr): Clean up casts.
1134 const double dfReal = reinterpret_cast<const GInt16*>(pDataRef)[iOffset*2];
1135 const double dfImag = reinterpret_cast<const GInt16*>(pDataRef)[iOffset*2+1];
1136 dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
1137 break;
1138 }
1139 case GDT_CInt32:
1140 {
1141 const double dfReal = reinterpret_cast<const GInt32*>(pDataRef)[iOffset*2];
1142 const double dfImag = reinterpret_cast<const GInt32*>(pDataRef)[iOffset*2+1];
1143 dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
1144 break;
1145 }
1146 case GDT_CFloat32:
1147 {
1148 const double dfReal = reinterpret_cast<const float*>(pDataRef)[iOffset*2];
1149 const double dfImag = reinterpret_cast<const float*>(pDataRef)[iOffset*2+1];
1150 dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
1151 break;
1152 }
1153 case GDT_CFloat64:
1154 {
1155 const double dfReal = reinterpret_cast<const double*>(pDataRef)[iOffset*2];
1156 const double dfImag = reinterpret_cast<const double*>(pDataRef)[iOffset*2+1];
1157 dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
1158 break;
1159 }
1160 default:
1161 CPLAssert( false );
1162 }
1163
1164 if( bGotNoDataValue && dfValue == dfNoDataValue )
1165 continue;
1166
1167 if( nActualSamples < nSamples )
1168 pafSampleBuf[nActualSamples++] = static_cast<float>(dfValue);
1169 }
1170
1171 iRemainder = iX - iXValid;
1172 }
1173
1174 poBlock->DropLock();
1175 }
1176
1177 return nActualSamples;
1178 }
1179
1180 /************************************************************************/
1181 /* GDALInitGCPs() */
1182 /************************************************************************/
1183
1184 /** Initialize an array of GCPs.
1185 *
1186 * Numeric values are initialized to 0 and strings to the empty string ""
1187 * allocated with CPLStrdup()
1188 * An array initialized with GDALInitGCPs() must be de-initialized with
1189 * GDALDeinitGCPs().
1190 *
1191 * @param nCount number of GCPs in psGCP
1192 * @param psGCP array of GCPs of size nCount.
1193 */
GDALInitGCPs(int nCount,GDAL_GCP * psGCP)1194 void CPL_STDCALL GDALInitGCPs( int nCount, GDAL_GCP *psGCP )
1195
1196 {
1197 if( nCount > 0 )
1198 {
1199 VALIDATE_POINTER0( psGCP, "GDALInitGCPs" );
1200 }
1201
1202 for( int iGCP = 0; iGCP < nCount; iGCP++ )
1203 {
1204 memset( psGCP, 0, sizeof(GDAL_GCP) );
1205 psGCP->pszId = CPLStrdup("");
1206 psGCP->pszInfo = CPLStrdup("");
1207 psGCP++;
1208 }
1209 }
1210
1211 /************************************************************************/
1212 /* GDALDeinitGCPs() */
1213 /************************************************************************/
1214
1215 /** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1216 *
1217 * @param nCount number of GCPs in psGCP
1218 * @param psGCP array of GCPs of size nCount.
1219 */
GDALDeinitGCPs(int nCount,GDAL_GCP * psGCP)1220 void CPL_STDCALL GDALDeinitGCPs( int nCount, GDAL_GCP *psGCP )
1221
1222 {
1223 if ( nCount > 0 )
1224 {
1225 VALIDATE_POINTER0( psGCP, "GDALDeinitGCPs" );
1226 }
1227
1228 for( int iGCP = 0; iGCP < nCount; iGCP++ )
1229 {
1230 CPLFree( psGCP->pszId );
1231 CPLFree( psGCP->pszInfo );
1232 psGCP++;
1233 }
1234 }
1235
1236 /************************************************************************/
1237 /* GDALDuplicateGCPs() */
1238 /************************************************************************/
1239
1240 /** Duplicate an array of GCPs
1241 *
1242 * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1243 *
1244 * @param nCount number of GCPs in psGCP
1245 * @param pasGCPList array of GCPs of size nCount.
1246 */
GDALDuplicateGCPs(int nCount,const GDAL_GCP * pasGCPList)1247 GDAL_GCP * CPL_STDCALL GDALDuplicateGCPs( int nCount, const GDAL_GCP *pasGCPList )
1248
1249 {
1250 GDAL_GCP *pasReturn = static_cast<GDAL_GCP *>(
1251 CPLMalloc(sizeof(GDAL_GCP) * nCount) );
1252 GDALInitGCPs( nCount, pasReturn );
1253
1254 for( int iGCP = 0; iGCP < nCount; iGCP++ )
1255 {
1256 CPLFree( pasReturn[iGCP].pszId );
1257 pasReturn[iGCP].pszId = CPLStrdup( pasGCPList[iGCP].pszId );
1258
1259 CPLFree( pasReturn[iGCP].pszInfo );
1260 pasReturn[iGCP].pszInfo = CPLStrdup( pasGCPList[iGCP].pszInfo );
1261
1262 pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
1263 pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
1264 pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
1265 pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
1266 pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
1267 }
1268
1269 return pasReturn;
1270 }
1271
1272 /************************************************************************/
1273 /* GDALFindAssociatedFile() */
1274 /************************************************************************/
1275
1276 /**
1277 * \brief Find file with alternate extension.
1278 *
1279 * Finds the file with the indicated extension, substituting it in place
1280 * of the extension of the base filename. Generally used to search for
1281 * associated files like world files .RPB files, etc. If necessary, the
1282 * extension will be tried in both upper and lower case. If a sibling file
1283 * list is available it will be used instead of doing VSIStatExL() calls to
1284 * probe the file system.
1285 *
1286 * Note that the result is a dynamic CPLString so this method should not
1287 * be used in a situation where there could be cross heap issues. It is
1288 * generally imprudent for application built on GDAL to use this function
1289 * unless they are sure they will always use the same runtime heap as GDAL.
1290 *
1291 * @param pszBaseFilename the filename relative to which to search.
1292 * @param pszExt the target extension in either upper or lower case.
1293 * @param papszSiblingFiles the list of files in the same directory as
1294 * pszBaseFilename or NULL if they are not known.
1295 * @param nFlags special options controlling search. None defined yet, just
1296 * pass 0.
1297 *
1298 * @return an empty string if the target is not found, otherwise the target
1299 * file with similar path style as the pszBaseFilename.
1300 */
1301
1302 /**/
1303 /**/
1304
GDALFindAssociatedFile(const char * pszBaseFilename,const char * pszExt,CSLConstList papszSiblingFiles,CPL_UNUSED int nFlags)1305 CPLString GDALFindAssociatedFile( const char *pszBaseFilename,
1306 const char *pszExt,
1307 CSLConstList papszSiblingFiles,
1308 CPL_UNUSED int nFlags )
1309
1310 {
1311 CPLString osTarget = CPLResetExtension( pszBaseFilename, pszExt );
1312
1313 if( papszSiblingFiles == nullptr ||
1314 !GDALCanReliablyUseSiblingFileList(osTarget.c_str()) )
1315 {
1316 VSIStatBufL sStatBuf;
1317
1318 if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 )
1319 {
1320 CPLString osAltExt = pszExt;
1321
1322 if( islower( pszExt[0] ) )
1323 osAltExt.toupper();
1324 else
1325 osAltExt.tolower();
1326
1327 osTarget = CPLResetExtension( pszBaseFilename, osAltExt );
1328
1329 if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 )
1330 return "";
1331 }
1332 }
1333 else
1334 {
1335 const int iSibling = CSLFindString( papszSiblingFiles,
1336 CPLGetFilename(osTarget) );
1337 if( iSibling < 0 )
1338 return "";
1339
1340 osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
1341 osTarget += papszSiblingFiles[iSibling];
1342 }
1343
1344 return osTarget;
1345 }
1346
1347 /************************************************************************/
1348 /* GDALLoadOziMapFile() */
1349 /************************************************************************/
1350
1351 /** Helper function for translator implementer wanting support for OZI .map
1352 *
1353 * @param pszFilename filename of .tab file
1354 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1355 * @param ppszWKT output pointer to a string that will be allocated with CPLMalloc().
1356 * @param pnGCPCount output pointer to GCP count.
1357 * @param ppasGCPs outputer pointer to an array of GCPs.
1358 * @return TRUE in case of success, FALSE otherwise.
1359 */
GDALLoadOziMapFile(const char * pszFilename,double * padfGeoTransform,char ** ppszWKT,int * pnGCPCount,GDAL_GCP ** ppasGCPs)1360 int CPL_STDCALL GDALLoadOziMapFile( const char *pszFilename,
1361 double *padfGeoTransform, char **ppszWKT,
1362 int *pnGCPCount, GDAL_GCP **ppasGCPs )
1363
1364 {
1365 VALIDATE_POINTER1( pszFilename, "GDALLoadOziMapFile", FALSE );
1366 VALIDATE_POINTER1( padfGeoTransform, "GDALLoadOziMapFile", FALSE );
1367 VALIDATE_POINTER1( pnGCPCount, "GDALLoadOziMapFile", FALSE );
1368 VALIDATE_POINTER1( ppasGCPs, "GDALLoadOziMapFile", FALSE );
1369
1370 char **papszLines = CSLLoad2( pszFilename, 1000, 200, nullptr );
1371
1372 if ( !papszLines )
1373 return FALSE;
1374
1375 int nLines = CSLCount( papszLines );
1376
1377 // Check the OziExplorer Map file signature
1378 if ( nLines < 5
1379 || !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version ") )
1380 {
1381 CPLError( CE_Failure, CPLE_AppDefined,
1382 "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map format.",
1383 pszFilename );
1384 CSLDestroy( papszLines );
1385 return FALSE;
1386 }
1387
1388 OGRSpatialReference oSRS;
1389 OGRErr eErr = OGRERR_NONE;
1390
1391 /* The Map Scale Factor has been introduced recently on the 6th line */
1392 /* and is a trick that is used to just change that line without changing */
1393 /* the rest of the MAP file but providing an imagery that is smaller or larger */
1394 /* so we have to correct the pixel/line values read in the .MAP file so they */
1395 /* match the actual imagery dimension. Well, this is a bad summary of what */
1396 /* is explained at http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
1397 double dfMSF = 1;
1398
1399 for ( int iLine = 5; iLine < nLines; iLine++ )
1400 {
1401 if ( STARTS_WITH_CI(papszLines[iLine], "MSF,") )
1402 {
1403 dfMSF = CPLAtof(papszLines[iLine] + 4);
1404 if (dfMSF <= 0.01) /* Suspicious values */
1405 {
1406 CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
1407 dfMSF = 1;
1408 }
1409 }
1410 }
1411
1412 eErr = oSRS.importFromOzi( papszLines );
1413 if ( eErr == OGRERR_NONE )
1414 {
1415 if ( ppszWKT != nullptr )
1416 oSRS.exportToWkt( ppszWKT );
1417 }
1418
1419 int nCoordinateCount = 0;
1420 // TODO(schwehr): Initialize asGCPs.
1421 GDAL_GCP asGCPs[30];
1422
1423 // Iterate all lines in the MAP-file
1424 for ( int iLine = 5; iLine < nLines; iLine++ )
1425 {
1426 char **papszTok = CSLTokenizeString2( papszLines[iLine], ",",
1427 CSLT_ALLOWEMPTYTOKENS
1428 | CSLT_STRIPLEADSPACES
1429 | CSLT_STRIPENDSPACES );
1430
1431 if ( CSLCount(papszTok) < 12 )
1432 {
1433 CSLDestroy(papszTok);
1434 continue;
1435 }
1436
1437 if ( CSLCount(papszTok) >= 17
1438 && STARTS_WITH_CI(papszTok[0], "Point")
1439 && !EQUAL(papszTok[2], "")
1440 && !EQUAL(papszTok[3], "")
1441 && nCoordinateCount <
1442 static_cast<int>(CPL_ARRAYSIZE(asGCPs)) )
1443 {
1444 bool bReadOk = false;
1445 double dfLon = 0.0;
1446 double dfLat = 0.0;
1447
1448 if ( !EQUAL(papszTok[6], "")
1449 && !EQUAL(papszTok[7], "")
1450 && !EQUAL(papszTok[9], "")
1451 && !EQUAL(papszTok[10], "") )
1452 {
1453 // Set geographical coordinates of the pixels
1454 dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
1455 dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
1456 if ( EQUAL(papszTok[11], "W") )
1457 dfLon = -dfLon;
1458 if ( EQUAL(papszTok[8], "S") )
1459 dfLat = -dfLat;
1460
1461 // Transform from the geographical coordinates into projected
1462 // coordinates.
1463 if ( eErr == OGRERR_NONE )
1464 {
1465 OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
1466
1467 if ( poLongLat )
1468 {
1469 oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1470 poLongLat->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1471
1472 OGRCoordinateTransformation *poTransform =
1473 OGRCreateCoordinateTransformation( poLongLat,
1474 &oSRS );
1475 if ( poTransform )
1476 {
1477 bReadOk = CPL_TO_BOOL(
1478 poTransform->Transform( 1, &dfLon, &dfLat ));
1479 delete poTransform;
1480 }
1481 delete poLongLat;
1482 }
1483 }
1484 }
1485 else if ( !EQUAL(papszTok[14], "")
1486 && !EQUAL(papszTok[15], "") )
1487 {
1488 // Set cartesian coordinates of the pixels.
1489 dfLon = CPLAtofM(papszTok[14]);
1490 dfLat = CPLAtofM(papszTok[15]);
1491 bReadOk = true;
1492
1493 //if ( EQUAL(papszTok[16], "S") )
1494 // dfLat = -dfLat;
1495 }
1496
1497 if ( bReadOk )
1498 {
1499 GDALInitGCPs( 1, asGCPs + nCoordinateCount );
1500
1501 // Set pixel/line part
1502 asGCPs[nCoordinateCount].dfGCPPixel =
1503 CPLAtofM(papszTok[2]) / dfMSF;
1504 asGCPs[nCoordinateCount].dfGCPLine =
1505 CPLAtofM(papszTok[3]) / dfMSF;
1506
1507 asGCPs[nCoordinateCount].dfGCPX = dfLon;
1508 asGCPs[nCoordinateCount].dfGCPY = dfLat;
1509
1510 nCoordinateCount++;
1511 }
1512 }
1513
1514 CSLDestroy( papszTok );
1515 }
1516
1517 CSLDestroy( papszLines );
1518
1519 if ( nCoordinateCount == 0 )
1520 {
1521 CPLDebug( "GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
1522 pszFilename );
1523 return FALSE;
1524 }
1525
1526 /* -------------------------------------------------------------------- */
1527 /* Try to convert the GCPs into a geotransform definition, if */
1528 /* possible. Otherwise we will need to use them as GCPs. */
1529 /* -------------------------------------------------------------------- */
1530 if( !GDALGCPsToGeoTransform(
1531 nCoordinateCount, asGCPs, padfGeoTransform,
1532 CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO")) ) )
1533 {
1534 if ( pnGCPCount && ppasGCPs )
1535 {
1536 CPLDebug( "GDAL",
1537 "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
1538 "first order geotransform. Using points as GCPs.",
1539 pszFilename );
1540
1541 *ppasGCPs = static_cast<GDAL_GCP *>(
1542 CPLCalloc( sizeof(GDAL_GCP), nCoordinateCount ) );
1543 memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount );
1544 *pnGCPCount = nCoordinateCount;
1545 }
1546 }
1547 else
1548 {
1549 GDALDeinitGCPs( nCoordinateCount, asGCPs );
1550 }
1551
1552 return TRUE;
1553 }
1554
1555 /************************************************************************/
1556 /* GDALReadOziMapFile() */
1557 /************************************************************************/
1558
1559 /** Helper function for translator implementer wanting support for OZI .map
1560 *
1561 * @param pszBaseFilename filename whose basename will help building the .map filename.
1562 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1563 * @param ppszWKT output pointer to a string that will be allocated with CPLMalloc().
1564 * @param pnGCPCount output pointer to GCP count.
1565 * @param ppasGCPs outputer pointer to an array of GCPs.
1566 * @return TRUE in case of success, FALSE otherwise.
1567 */
GDALReadOziMapFile(const char * pszBaseFilename,double * padfGeoTransform,char ** ppszWKT,int * pnGCPCount,GDAL_GCP ** ppasGCPs)1568 int CPL_STDCALL GDALReadOziMapFile( const char * pszBaseFilename,
1569 double *padfGeoTransform, char **ppszWKT,
1570 int *pnGCPCount, GDAL_GCP **ppasGCPs )
1571
1572 {
1573 /* -------------------------------------------------------------------- */
1574 /* Try lower case, then upper case. */
1575 /* -------------------------------------------------------------------- */
1576 const char *pszOzi = CPLResetExtension( pszBaseFilename, "map" );
1577
1578 VSILFILE *fpOzi = VSIFOpenL( pszOzi, "rt" );
1579
1580 if ( fpOzi == nullptr && VSIIsCaseSensitiveFS(pszOzi) )
1581 {
1582 pszOzi = CPLResetExtension( pszBaseFilename, "MAP" );
1583 fpOzi = VSIFOpenL( pszOzi, "rt" );
1584 }
1585
1586 if ( fpOzi == nullptr )
1587 return FALSE;
1588
1589 CPL_IGNORE_RET_VAL(VSIFCloseL( fpOzi ));
1590
1591 /* -------------------------------------------------------------------- */
1592 /* We found the file, now load and parse it. */
1593 /* -------------------------------------------------------------------- */
1594 return GDALLoadOziMapFile( pszOzi, padfGeoTransform, ppszWKT,
1595 pnGCPCount, ppasGCPs );
1596 }
1597
1598 /************************************************************************/
1599 /* GDALLoadTabFile() */
1600 /* */
1601 /************************************************************************/
1602
1603 /** Helper function for translator implementer wanting support for MapInfo
1604 * .tab files.
1605 *
1606 * @param pszFilename filename of .tab
1607 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1608 * @param ppszWKT output pointer to a string that will be allocated with CPLMalloc().
1609 * @param pnGCPCount output pointer to GCP count.
1610 * @param ppasGCPs outputer pointer to an array of GCPs.
1611 * @return TRUE in case of success, FALSE otherwise.
1612 */
GDALLoadTabFile(const char * pszFilename,double * padfGeoTransform,char ** ppszWKT,int * pnGCPCount,GDAL_GCP ** ppasGCPs)1613 int CPL_STDCALL GDALLoadTabFile( const char *pszFilename,
1614 double *padfGeoTransform, char **ppszWKT,
1615 int *pnGCPCount, GDAL_GCP **ppasGCPs )
1616
1617 {
1618 char **papszLines = CSLLoad2( pszFilename, 1000, 200, nullptr );
1619
1620 if ( !papszLines )
1621 return FALSE;
1622
1623 char **papszTok = nullptr;
1624 bool bTypeRasterFound = false;
1625 bool bInsideTableDef = false;
1626 int nCoordinateCount = 0;
1627 GDAL_GCP asGCPs[256]; // TODO(schwehr): Initialize.
1628 const int numLines = CSLCount(papszLines);
1629
1630 // Iterate all lines in the TAB-file
1631 for( int iLine=0; iLine<numLines; iLine++ )
1632 {
1633 CSLDestroy(papszTok);
1634 papszTok = CSLTokenizeStringComplex(papszLines[iLine], " \t(),;",
1635 TRUE, FALSE);
1636
1637 if (CSLCount(papszTok) < 2)
1638 continue;
1639
1640 // Did we find table definition
1641 if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table") )
1642 {
1643 bInsideTableDef = TRUE;
1644 }
1645 else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")) )
1646 {
1647 // Only RASTER-type will be handled
1648 if (EQUAL(papszTok[1], "RASTER"))
1649 {
1650 bTypeRasterFound = true;
1651 }
1652 else
1653 {
1654 CSLDestroy(papszTok);
1655 CSLDestroy(papszLines);
1656 return FALSE;
1657 }
1658 }
1659 else if (bTypeRasterFound && bInsideTableDef
1660 && CSLCount(papszTok) > 4
1661 && EQUAL(papszTok[4], "Label")
1662 && nCoordinateCount <
1663 static_cast<int>(CPL_ARRAYSIZE(asGCPs)) )
1664 {
1665 GDALInitGCPs( 1, asGCPs + nCoordinateCount );
1666
1667 asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
1668 asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
1669 asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
1670 asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
1671 if( papszTok[5] != nullptr )
1672 {
1673 CPLFree( asGCPs[nCoordinateCount].pszId );
1674 asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
1675 }
1676
1677 nCoordinateCount++;
1678 }
1679 else if( bTypeRasterFound && bInsideTableDef
1680 && EQUAL(papszTok[0],"CoordSys")
1681 && ppszWKT != nullptr )
1682 {
1683 OGRSpatialReference oSRS;
1684
1685 if( oSRS.importFromMICoordSys( papszLines[iLine] ) == OGRERR_NONE )
1686 oSRS.exportToWkt( ppszWKT );
1687 }
1688 else if( EQUAL(papszTok[0],"Units")
1689 && CSLCount(papszTok) > 1
1690 && EQUAL(papszTok[1],"degree") )
1691 {
1692 /*
1693 ** If we have units of "degree", but a projected coordinate
1694 ** system we need to convert it to geographic. See to01_02.TAB.
1695 */
1696 if( ppszWKT != nullptr && *ppszWKT != nullptr
1697 && STARTS_WITH_CI(*ppszWKT, "PROJCS") )
1698 {
1699 OGRSpatialReference oSRS;
1700 oSRS.importFromWkt( *ppszWKT );
1701
1702 OGRSpatialReference oSRSGeogCS;
1703 oSRSGeogCS.CopyGeogCSFrom( &oSRS );
1704 CPLFree( *ppszWKT );
1705
1706 oSRSGeogCS.exportToWkt( ppszWKT );
1707 }
1708 }
1709 }
1710
1711 CSLDestroy(papszTok);
1712 CSLDestroy(papszLines);
1713
1714 if( nCoordinateCount == 0 )
1715 {
1716 CPLDebug( "GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
1717 pszFilename );
1718 return FALSE;
1719 }
1720
1721 /* -------------------------------------------------------------------- */
1722 /* Try to convert the GCPs into a geotransform definition, if */
1723 /* possible. Otherwise we will need to use them as GCPs. */
1724 /* -------------------------------------------------------------------- */
1725 if( !GDALGCPsToGeoTransform(
1726 nCoordinateCount, asGCPs, padfGeoTransform,
1727 CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO")) ) )
1728 {
1729 if (pnGCPCount && ppasGCPs)
1730 {
1731 CPLDebug( "GDAL",
1732 "GDALLoadTabFile(%s) found file, was not able to derive a "
1733 "first order geotransform. Using points as GCPs.",
1734 pszFilename );
1735
1736 *ppasGCPs = static_cast<GDAL_GCP *>(
1737 CPLCalloc( sizeof(GDAL_GCP), nCoordinateCount ) );
1738 memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount );
1739 *pnGCPCount = nCoordinateCount;
1740 }
1741 }
1742 else
1743 {
1744 GDALDeinitGCPs( nCoordinateCount, asGCPs );
1745 }
1746
1747 return TRUE;
1748 }
1749
1750 /************************************************************************/
1751 /* GDALReadTabFile() */
1752 /************************************************************************/
1753
1754 /** Helper function for translator implementer wanting support for MapInfo
1755 * .tab files.
1756 *
1757 * @param pszBaseFilename filename whose basename will help building the .tab filename.
1758 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1759 * @param ppszWKT output pointer to a string that will be allocated with CPLMalloc().
1760 * @param pnGCPCount output pointer to GCP count.
1761 * @param ppasGCPs outputer pointer to an array of GCPs.
1762 * @return TRUE in case of success, FALSE otherwise.
1763 */
GDALReadTabFile(const char * pszBaseFilename,double * padfGeoTransform,char ** ppszWKT,int * pnGCPCount,GDAL_GCP ** ppasGCPs)1764 int CPL_STDCALL GDALReadTabFile( const char * pszBaseFilename,
1765 double *padfGeoTransform, char **ppszWKT,
1766 int *pnGCPCount, GDAL_GCP **ppasGCPs )
1767
1768 {
1769 return GDALReadTabFile2( pszBaseFilename, padfGeoTransform,
1770 ppszWKT, pnGCPCount, ppasGCPs,
1771 nullptr, nullptr );
1772 }
1773
GDALReadTabFile2(const char * pszBaseFilename,double * padfGeoTransform,char ** ppszWKT,int * pnGCPCount,GDAL_GCP ** ppasGCPs,char ** papszSiblingFiles,char ** ppszTabFileNameOut)1774 int GDALReadTabFile2( const char * pszBaseFilename,
1775 double *padfGeoTransform, char **ppszWKT,
1776 int *pnGCPCount, GDAL_GCP **ppasGCPs,
1777 char** papszSiblingFiles, char** ppszTabFileNameOut )
1778 {
1779 if (ppszTabFileNameOut)
1780 *ppszTabFileNameOut = nullptr;
1781
1782 if( !GDALCanFileAcceptSidecarFile(pszBaseFilename) )
1783 return FALSE;
1784
1785 const char *pszTAB = CPLResetExtension( pszBaseFilename, "tab" );
1786
1787 if (papszSiblingFiles && GDALCanReliablyUseSiblingFileList(pszTAB))
1788 {
1789 int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB));
1790 if (iSibling >= 0)
1791 {
1792 CPLString osTabFilename = pszBaseFilename;
1793 osTabFilename.resize(strlen(pszBaseFilename) -
1794 strlen(CPLGetFilename(pszBaseFilename)));
1795 osTabFilename += papszSiblingFiles[iSibling];
1796 if ( GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
1797 pnGCPCount, ppasGCPs ) )
1798 {
1799 if (ppszTabFileNameOut)
1800 *ppszTabFileNameOut = CPLStrdup(osTabFilename);
1801 return TRUE;
1802 }
1803 }
1804 return FALSE;
1805 }
1806
1807 /* -------------------------------------------------------------------- */
1808 /* Try lower case, then upper case. */
1809 /* -------------------------------------------------------------------- */
1810
1811 VSILFILE *fpTAB = VSIFOpenL( pszTAB, "rt" );
1812
1813 if( fpTAB == nullptr && VSIIsCaseSensitiveFS(pszTAB) )
1814 {
1815 pszTAB = CPLResetExtension( pszBaseFilename, "TAB" );
1816 fpTAB = VSIFOpenL( pszTAB, "rt" );
1817 }
1818
1819 if( fpTAB == nullptr )
1820 return FALSE;
1821
1822 CPL_IGNORE_RET_VAL(VSIFCloseL( fpTAB ));
1823
1824 /* -------------------------------------------------------------------- */
1825 /* We found the file, now load and parse it. */
1826 /* -------------------------------------------------------------------- */
1827 if (GDALLoadTabFile( pszTAB, padfGeoTransform, ppszWKT,
1828 pnGCPCount, ppasGCPs ) )
1829 {
1830 if (ppszTabFileNameOut)
1831 *ppszTabFileNameOut = CPLStrdup(pszTAB);
1832 return TRUE;
1833 }
1834 return FALSE;
1835 }
1836
1837 /************************************************************************/
1838 /* GDALLoadWorldFile() */
1839 /************************************************************************/
1840
1841 /**
1842 * \brief Read ESRI world file.
1843 *
1844 * This function reads an ESRI style world file, and formats a geotransform
1845 * from its contents.
1846 *
1847 * The world file contains an affine transformation with the parameters
1848 * in a different order than in a geotransform array.
1849 *
1850 * <ul>
1851 * <li> geotransform[1] : width of pixel
1852 * <li> geotransform[4] : rotational coefficient, zero for north up images.
1853 * <li> geotransform[2] : rotational coefficient, zero for north up images.
1854 * <li> geotransform[5] : height of pixel (but negative)
1855 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
1856 * <li> geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
1857 * </ul>
1858 *
1859 * @param pszFilename the world file name.
1860 * @param padfGeoTransform the six double array into which the
1861 * geotransformation should be placed.
1862 *
1863 * @return TRUE on success or FALSE on failure.
1864 */
1865
1866 int CPL_STDCALL
GDALLoadWorldFile(const char * pszFilename,double * padfGeoTransform)1867 GDALLoadWorldFile( const char *pszFilename, double *padfGeoTransform )
1868
1869 {
1870 VALIDATE_POINTER1( pszFilename, "GDALLoadWorldFile", FALSE );
1871 VALIDATE_POINTER1( padfGeoTransform, "GDALLoadWorldFile", FALSE );
1872
1873 char **papszLines = CSLLoad2( pszFilename, 100, 100, nullptr );
1874
1875 if ( !papszLines )
1876 return FALSE;
1877
1878 double world[6] = { 0.0 };
1879 // reads the first 6 non-empty lines
1880 int nLines = 0;
1881 const int nLinesCount = CSLCount(papszLines);
1882 for( int i = 0;
1883 i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
1884 ++i )
1885 {
1886 CPLString line(papszLines[i]);
1887 if( line.Trim().empty() )
1888 continue;
1889
1890 world[nLines] = CPLAtofM(line);
1891 ++nLines;
1892 }
1893
1894 if( nLines == 6
1895 && (world[0] != 0.0 || world[2] != 0.0)
1896 && (world[3] != 0.0 || world[1] != 0.0) )
1897 {
1898 padfGeoTransform[0] = world[4];
1899 padfGeoTransform[1] = world[0];
1900 padfGeoTransform[2] = world[2];
1901 padfGeoTransform[3] = world[5];
1902 padfGeoTransform[4] = world[1];
1903 padfGeoTransform[5] = world[3];
1904
1905 // correct for center of pixel vs. top left of pixel
1906 padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
1907 padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
1908 padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
1909 padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
1910
1911 CSLDestroy(papszLines);
1912
1913 return TRUE;
1914 }
1915 else
1916 {
1917 CPLDebug( "GDAL",
1918 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
1919 pszFilename );
1920 CSLDestroy(papszLines);
1921 return FALSE;
1922 }
1923 }
1924
1925 /************************************************************************/
1926 /* GDALReadWorldFile() */
1927 /************************************************************************/
1928
1929 /**
1930 * \brief Read ESRI world file.
1931 *
1932 * This function reads an ESRI style world file, and formats a geotransform
1933 * from its contents. It does the same as GDALLoadWorldFile() function, but
1934 * it will form the filename for the worldfile from the filename of the raster
1935 * file referred and the suggested extension. If no extension is provided,
1936 * the code will internally try the unix style and windows style world file
1937 * extensions (eg. for .tif these would be .tfw and .tifw).
1938 *
1939 * The world file contains an affine transformation with the parameters
1940 * in a different order than in a geotransform array.
1941 *
1942 * <ul>
1943 * <li> geotransform[1] : width of pixel
1944 * <li> geotransform[4] : rotational coefficient, zero for north up images.
1945 * <li> geotransform[2] : rotational coefficient, zero for north up images.
1946 * <li> geotransform[5] : height of pixel (but negative)
1947 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
1948 * <li> geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
1949 * </ul>
1950 *
1951 * @param pszBaseFilename the target raster file.
1952 * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
1953 * from the pszBaseFilename
1954 * @param padfGeoTransform the six double array into which the
1955 * geotransformation should be placed.
1956 *
1957 * @return TRUE on success or FALSE on failure.
1958 */
1959
1960 int CPL_STDCALL
GDALReadWorldFile(const char * pszBaseFilename,const char * pszExtension,double * padfGeoTransform)1961 GDALReadWorldFile( const char *pszBaseFilename, const char *pszExtension,
1962 double *padfGeoTransform )
1963
1964 {
1965 return GDALReadWorldFile2( pszBaseFilename, pszExtension,
1966 padfGeoTransform, nullptr, nullptr );
1967 }
1968
GDALReadWorldFile2(const char * pszBaseFilename,const char * pszExtension,double * padfGeoTransform,char ** papszSiblingFiles,char ** ppszWorldFileNameOut)1969 int GDALReadWorldFile2( const char *pszBaseFilename, const char *pszExtension,
1970 double *padfGeoTransform, char** papszSiblingFiles,
1971 char** ppszWorldFileNameOut )
1972 {
1973 VALIDATE_POINTER1( pszBaseFilename, "GDALReadWorldFile", FALSE );
1974 VALIDATE_POINTER1( padfGeoTransform, "GDALReadWorldFile", FALSE );
1975
1976 if (ppszWorldFileNameOut)
1977 *ppszWorldFileNameOut = nullptr;
1978
1979 if( !GDALCanFileAcceptSidecarFile(pszBaseFilename) )
1980 return FALSE;
1981
1982 /* -------------------------------------------------------------------- */
1983 /* If we aren't given an extension, try both the unix and */
1984 /* windows style extensions. */
1985 /* -------------------------------------------------------------------- */
1986 if( pszExtension == nullptr )
1987 {
1988 const std::string oBaseExt = CPLGetExtension( pszBaseFilename );
1989
1990 if( oBaseExt.length() < 2 )
1991 return FALSE;
1992
1993 // windows version - first + last + 'w'
1994 char szDerivedExtension[100] = { '\0' };
1995 szDerivedExtension[0] = oBaseExt[0];
1996 szDerivedExtension[1] = oBaseExt[oBaseExt.length()-1];
1997 szDerivedExtension[2] = 'w';
1998 szDerivedExtension[3] = '\0';
1999
2000 if( GDALReadWorldFile2( pszBaseFilename, szDerivedExtension,
2001 padfGeoTransform, papszSiblingFiles,
2002 ppszWorldFileNameOut ) )
2003 return TRUE;
2004
2005 // unix version - extension + 'w'
2006 if( oBaseExt.length() > sizeof(szDerivedExtension)-2 )
2007 return FALSE;
2008
2009 snprintf( szDerivedExtension, sizeof(szDerivedExtension),
2010 "%sw", oBaseExt.c_str() );
2011 return GDALReadWorldFile2( pszBaseFilename, szDerivedExtension,
2012 padfGeoTransform, papszSiblingFiles,
2013 ppszWorldFileNameOut );
2014 }
2015
2016 /* -------------------------------------------------------------------- */
2017 /* Skip the leading period in the extension if there is one. */
2018 /* -------------------------------------------------------------------- */
2019 if( *pszExtension == '.' )
2020 pszExtension++;
2021
2022 /* -------------------------------------------------------------------- */
2023 /* Generate upper and lower case versions of the extension. */
2024 /* -------------------------------------------------------------------- */
2025 char szExtUpper[32] = { '\0' };
2026 char szExtLower[32] = { '\0' };
2027 CPLStrlcpy( szExtUpper, pszExtension, sizeof(szExtUpper) );
2028 CPLStrlcpy( szExtLower, pszExtension, sizeof(szExtLower) );
2029
2030 for( int i = 0; szExtUpper[i] != '\0'; i++ )
2031 {
2032 szExtUpper[i] = static_cast<char>( toupper(szExtUpper[i]) );
2033 szExtLower[i] = static_cast<char>( tolower(szExtLower[i]) );
2034 }
2035
2036 const char *pszTFW = CPLResetExtension( pszBaseFilename, szExtLower );
2037
2038 if (papszSiblingFiles && GDALCanReliablyUseSiblingFileList(pszTFW))
2039 {
2040 const int iSibling =
2041 CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW));
2042 if (iSibling >= 0)
2043 {
2044 CPLString osTFWFilename = pszBaseFilename;
2045 osTFWFilename.resize(strlen(pszBaseFilename) -
2046 strlen(CPLGetFilename(pszBaseFilename)));
2047 osTFWFilename += papszSiblingFiles[iSibling];
2048 if (GDALLoadWorldFile( osTFWFilename, padfGeoTransform ))
2049 {
2050 if (ppszWorldFileNameOut)
2051 *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
2052 return TRUE;
2053 }
2054 }
2055 return FALSE;
2056 }
2057
2058 /* -------------------------------------------------------------------- */
2059 /* Try lower case, then upper case. */
2060 /* -------------------------------------------------------------------- */
2061
2062 VSIStatBufL sStatBuf;
2063 bool bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0;
2064
2065 if( !bGotTFW && VSIIsCaseSensitiveFS(pszTFW) )
2066 {
2067 pszTFW = CPLResetExtension( pszBaseFilename, szExtUpper );
2068 bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0;
2069 }
2070
2071 if( !bGotTFW )
2072 return FALSE;
2073
2074 /* -------------------------------------------------------------------- */
2075 /* We found the file, now load and parse it. */
2076 /* -------------------------------------------------------------------- */
2077 if (GDALLoadWorldFile( pszTFW, padfGeoTransform ))
2078 {
2079 if (ppszWorldFileNameOut)
2080 *ppszWorldFileNameOut = CPLStrdup(pszTFW);
2081 return TRUE;
2082 }
2083 return FALSE;
2084 }
2085
2086 /************************************************************************/
2087 /* GDALWriteWorldFile() */
2088 /* */
2089 /* Helper function for translator implementer wanting */
2090 /* support for ESRI world files. */
2091 /************************************************************************/
2092
2093 /**
2094 * \brief Write ESRI world file.
2095 *
2096 * This function writes an ESRI style world file from the passed geotransform.
2097 *
2098 * The world file contains an affine transformation with the parameters
2099 * in a different order than in a geotransform array.
2100 *
2101 * <ul>
2102 * <li> geotransform[1] : width of pixel
2103 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2104 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2105 * <li> geotransform[5] : height of pixel (but negative)
2106 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
2107 * <li> geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
2108 * </ul>
2109 *
2110 * @param pszBaseFilename the target raster file.
2111 * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2112 * @param padfGeoTransform the six double array from which the
2113 * geotransformation should be read.
2114 *
2115 * @return TRUE on success or FALSE on failure.
2116 */
2117
2118 int CPL_STDCALL
GDALWriteWorldFile(const char * pszBaseFilename,const char * pszExtension,double * padfGeoTransform)2119 GDALWriteWorldFile( const char * pszBaseFilename, const char *pszExtension,
2120 double *padfGeoTransform )
2121
2122 {
2123 VALIDATE_POINTER1( pszBaseFilename, "GDALWriteWorldFile", FALSE );
2124 VALIDATE_POINTER1( pszExtension, "GDALWriteWorldFile", FALSE );
2125 VALIDATE_POINTER1( padfGeoTransform, "GDALWriteWorldFile", FALSE );
2126
2127 /* -------------------------------------------------------------------- */
2128 /* Prepare the text to write to the file. */
2129 /* -------------------------------------------------------------------- */
2130 CPLString osTFWText;
2131
2132 osTFWText.Printf( "%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2133 padfGeoTransform[1],
2134 padfGeoTransform[4],
2135 padfGeoTransform[2],
2136 padfGeoTransform[5],
2137 padfGeoTransform[0]
2138 + 0.5 * padfGeoTransform[1]
2139 + 0.5 * padfGeoTransform[2],
2140 padfGeoTransform[3]
2141 + 0.5 * padfGeoTransform[4]
2142 + 0.5 * padfGeoTransform[5] );
2143
2144 /* -------------------------------------------------------------------- */
2145 /* Update extension, and write to disk. */
2146 /* -------------------------------------------------------------------- */
2147 const char *pszTFW = CPLResetExtension( pszBaseFilename, pszExtension );
2148 VSILFILE * const fpTFW = VSIFOpenL( pszTFW, "wt" );
2149 if( fpTFW == nullptr )
2150 return FALSE;
2151
2152 const int bRet =
2153 VSIFWriteL( osTFWText.c_str(), osTFWText.size(), 1, fpTFW )
2154 == 1;
2155 if( VSIFCloseL( fpTFW ) != 0 )
2156 return FALSE;
2157
2158 return bRet;
2159 }
2160
2161 /************************************************************************/
2162 /* GDALVersionInfo() */
2163 /************************************************************************/
2164
2165 /**
2166 * \brief Get runtime version information.
2167 *
2168 * Available pszRequest values:
2169 * <ul>
2170 * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. i.e. "1170"
2171 * Note: starting with GDAL 1.10, this string will be longer than 4 characters.
2172 * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a string.
2173 * i.e. "20020416".
2174 * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "1.1.7"
2175 * <li> "--version": Returns one line version message suitable for use in
2176 * response to --version requests. i.e. "GDAL 1.1.7, released 2002/04/16"
2177 * <li> "LICENSE": Returns the content of the LICENSE.TXT file from the GDAL_DATA directory.
2178 * Before GDAL 1.7.0, the returned string was leaking memory but this is now resolved.
2179 * So the result should not been freed by the caller.
2180 * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines with
2181 * information on build time options.
2182 * </ul>
2183 *
2184 * @param pszRequest the type of version info desired, as listed above.
2185 *
2186 * @return an internal string containing the requested information.
2187 */
2188
GDALVersionInfo(const char * pszRequest)2189 const char * CPL_STDCALL GDALVersionInfo( const char *pszRequest )
2190
2191 {
2192 /* -------------------------------------------------------------------- */
2193 /* Try to capture as much build information as practical. */
2194 /* -------------------------------------------------------------------- */
2195 if( pszRequest != nullptr && EQUAL(pszRequest,"BUILD_INFO") )
2196 {
2197 CPLString osBuildInfo;
2198
2199 #ifdef ESRI_BUILD
2200 osBuildInfo += "ESRI_BUILD=YES\n";
2201 #endif
2202 #ifdef PAM_ENABLED
2203 osBuildInfo += "PAM_ENABLED=YES\n";
2204 #endif
2205 osBuildInfo += "OGR_ENABLED=YES\n"; // Deprecated. Always yes.
2206 #ifdef HAVE_GEOS
2207 osBuildInfo += "GEOS_ENABLED=YES\n";
2208 #ifdef GEOS_CAPI_VERSION
2209 osBuildInfo += CPLString("GEOS_VERSION=") + GEOS_CAPI_VERSION + "\n";
2210 #endif
2211 #endif
2212 CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
2213 CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE );
2214 return static_cast<char *>( CPLGetTLS(CTLS_VERSIONINFO) );
2215 }
2216
2217 /* -------------------------------------------------------------------- */
2218 /* LICENSE is a special case. We try to find and read the */
2219 /* LICENSE.TXT file from the GDAL_DATA directory and return it */
2220 /* -------------------------------------------------------------------- */
2221 if( pszRequest != nullptr && EQUAL(pszRequest,"LICENSE") )
2222 {
2223 char* pszResultLicence = reinterpret_cast<char *>(
2224 CPLGetTLS( CTLS_VERSIONINFO_LICENCE ) );
2225 if( pszResultLicence != nullptr )
2226 {
2227 return pszResultLicence;
2228 }
2229
2230 const char *pszFilename = CPLFindFile( "etc", "LICENSE.TXT" );
2231 VSILFILE *fp = nullptr;
2232
2233 if( pszFilename != nullptr )
2234 fp = VSIFOpenL( pszFilename, "r" );
2235
2236 if( fp != nullptr )
2237 {
2238 if( VSIFSeekL( fp, 0, SEEK_END ) == 0 )
2239 {
2240 // TODO(schwehr): Handle if VSITellL returns a value too large
2241 // for size_t.
2242 const size_t nLength =
2243 static_cast<size_t>( VSIFTellL( fp ) + 1 );
2244 if( VSIFSeekL( fp, SEEK_SET, 0 ) == 0 )
2245 {
2246 pszResultLicence = static_cast<char *>(
2247 VSICalloc(1, nLength) );
2248 if (pszResultLicence)
2249 CPL_IGNORE_RET_VAL(
2250 VSIFReadL( pszResultLicence, 1, nLength - 1, fp ) );
2251 }
2252 }
2253
2254 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
2255 }
2256
2257 if (!pszResultLicence)
2258 {
2259 pszResultLicence = CPLStrdup(
2260 "GDAL/OGR is released under the MIT/X license.\n"
2261 "The LICENSE.TXT distributed with GDAL/OGR should\n"
2262 "contain additional details.\n" );
2263 }
2264
2265 CPLSetTLS( CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE );
2266 return pszResultLicence;
2267 }
2268
2269 /* -------------------------------------------------------------------- */
2270 /* All other strings are fairly small. */
2271 /* -------------------------------------------------------------------- */
2272 CPLString osVersionInfo;
2273
2274 if( pszRequest == nullptr || EQUAL(pszRequest,"VERSION_NUM") )
2275 osVersionInfo.Printf( "%d", GDAL_VERSION_NUM );
2276 else if( EQUAL(pszRequest,"RELEASE_DATE") )
2277 osVersionInfo.Printf( "%d", GDAL_RELEASE_DATE );
2278 else if( EQUAL(pszRequest,"RELEASE_NAME") )
2279 osVersionInfo.Printf( GDAL_RELEASE_NAME );
2280 else // --version
2281 osVersionInfo.Printf( "GDAL %s, released %d/%02d/%02d",
2282 GDAL_RELEASE_NAME,
2283 GDAL_RELEASE_DATE / 10000,
2284 (GDAL_RELEASE_DATE % 10000) / 100,
2285 GDAL_RELEASE_DATE % 100 );
2286
2287 CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); // clear old value.
2288 CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE );
2289 return static_cast<char *>( CPLGetTLS(CTLS_VERSIONINFO) );
2290 }
2291
2292 /************************************************************************/
2293 /* GDALCheckVersion() */
2294 /************************************************************************/
2295
2296 /** Return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor.
2297
2298 The purpose of this method is to ensure that calling code will run
2299 with the GDAL version it is compiled for. It is primarily intended
2300 for external plugins.
2301
2302 @param nVersionMajor Major version to be tested against
2303 @param nVersionMinor Minor version to be tested against
2304 @param pszCallingComponentName If not NULL, in case of version mismatch, the method
2305 will issue a failure mentioning the name of
2306 the calling component.
2307
2308 @return TRUE if GDAL library version at runtime matches
2309 nVersionMajor.nVersionMinor, FALSE otherwise.
2310 */
GDALCheckVersion(int nVersionMajor,int nVersionMinor,const char * pszCallingComponentName)2311 int CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor,
2312 const char* pszCallingComponentName)
2313 {
2314 if (nVersionMajor == GDAL_VERSION_MAJOR &&
2315 nVersionMinor == GDAL_VERSION_MINOR)
2316 return TRUE;
2317
2318 if (pszCallingComponentName)
2319 {
2320 CPLError( CE_Failure, CPLE_AppDefined,
2321 "%s was compiled against GDAL %d.%d, but "
2322 "the current library version is %d.%d",
2323 pszCallingComponentName, nVersionMajor, nVersionMinor,
2324 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
2325 }
2326 return FALSE;
2327 }
2328
2329 /************************************************************************/
2330 /* GDALDecToDMS() */
2331 /************************************************************************/
2332
2333 /** Translate a decimal degrees value to a DMS string with hemisphere.
2334 */
GDALDecToDMS(double dfAngle,const char * pszAxis,int nPrecision)2335 const char * CPL_STDCALL GDALDecToDMS( double dfAngle, const char * pszAxis,
2336 int nPrecision )
2337
2338 {
2339 return CPLDecToDMS( dfAngle, pszAxis, nPrecision );
2340 }
2341
2342 /************************************************************************/
2343 /* GDALPackedDMSToDec() */
2344 /************************************************************************/
2345
2346 /**
2347 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2348 *
2349 * See CPLPackedDMSToDec().
2350 */
2351
GDALPackedDMSToDec(double dfPacked)2352 double CPL_STDCALL GDALPackedDMSToDec( double dfPacked )
2353
2354 {
2355 return CPLPackedDMSToDec( dfPacked );
2356 }
2357
2358 /************************************************************************/
2359 /* GDALDecToPackedDMS() */
2360 /************************************************************************/
2361
2362 /**
2363 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2364 *
2365 * See CPLDecToPackedDMS().
2366 */
2367
GDALDecToPackedDMS(double dfDec)2368 double CPL_STDCALL GDALDecToPackedDMS( double dfDec )
2369
2370 {
2371 return CPLDecToPackedDMS( dfDec );
2372 }
2373
2374 /************************************************************************/
2375 /* GDALGCPsToGeoTransform() */
2376 /************************************************************************/
2377
2378 /**
2379 * \brief Generate Geotransform from GCPs.
2380 *
2381 * Given a set of GCPs perform first order fit as a geotransform.
2382 *
2383 * Due to imprecision in the calculations the fit algorithm will often
2384 * return non-zero rotational coefficients even if given perfectly non-rotated
2385 * inputs. A special case has been implemented for corner corner coordinates
2386 * given in TL, TR, BR, BL order. So when using this to get a geotransform
2387 * from 4 corner coordinates, pass them in this order.
2388 *
2389 * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the
2390 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
2391 * set to YES, then bApproxOK will be overridden with TRUE.
2392 * Starting with GDAL 2.2.2, when exact fit is asked, the
2393 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
2394 * give the maximum error threshold in pixel. The default is 0.25.
2395 *
2396 * @param nGCPCount the number of GCPs being passed in.
2397 * @param pasGCPs the list of GCP structures.
2398 * @param padfGeoTransform the six double array in which the affine
2399 * geotransformation will be returned.
2400 * @param bApproxOK If FALSE the function will fail if the geotransform is not
2401 * essentially an exact fit (within 0.25 pixel) for all GCPs.
2402 *
2403 * @return TRUE on success or FALSE if there aren't enough points to prepare a
2404 * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
2405 * and the fit is poor.
2406 */
2407
2408 // TODO(schwehr): Add consts to args.
2409 int CPL_STDCALL
GDALGCPsToGeoTransform(int nGCPCount,const GDAL_GCP * pasGCPs,double * padfGeoTransform,int bApproxOK)2410 GDALGCPsToGeoTransform( int nGCPCount, const GDAL_GCP *pasGCPs,
2411 double *padfGeoTransform, int bApproxOK )
2412
2413 {
2414 double dfPixelThreshold = 0.25;
2415 if( !bApproxOK )
2416 {
2417 bApproxOK =
2418 CPLTestBool(CPLGetConfigOption(
2419 "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
2420 if( !bApproxOK )
2421 {
2422 // coverity[tainted_data]
2423 dfPixelThreshold =
2424 CPLAtof(CPLGetConfigOption(
2425 "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25"));
2426 }
2427 }
2428
2429 /* -------------------------------------------------------------------- */
2430 /* Recognise a few special cases. */
2431 /* -------------------------------------------------------------------- */
2432 if( nGCPCount < 2 )
2433 return FALSE;
2434
2435 if( nGCPCount == 2 )
2436 {
2437 if( pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel
2438 || pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine )
2439 return FALSE;
2440
2441 padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX)
2442 / (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
2443 padfGeoTransform[2] = 0.0;
2444
2445 padfGeoTransform[4] = 0.0;
2446 padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY)
2447 / (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
2448
2449 padfGeoTransform[0] = pasGCPs[0].dfGCPX
2450 - pasGCPs[0].dfGCPPixel * padfGeoTransform[1]
2451 - pasGCPs[0].dfGCPLine * padfGeoTransform[2];
2452
2453 padfGeoTransform[3] = pasGCPs[0].dfGCPY
2454 - pasGCPs[0].dfGCPPixel * padfGeoTransform[4]
2455 - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
2456
2457 return TRUE;
2458 }
2459
2460 /* -------------------------------------------------------------------- */
2461 /* Special case of 4 corner coordinates of a non-rotated */
2462 /* image. The points must be in TL-TR-BR-BL order for now. */
2463 /* This case helps avoid some imprecision in the general */
2464 /* calculations. */
2465 /* -------------------------------------------------------------------- */
2466 if( nGCPCount == 4
2467 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine
2468 && pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine
2469 && pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel
2470 && pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel
2471 && pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine
2472 && pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel
2473 && pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY
2474 && pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY
2475 && pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX
2476 && pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX
2477 && pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY
2478 && pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX )
2479 {
2480 padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX)
2481 / (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
2482 padfGeoTransform[2] = 0.0;
2483 padfGeoTransform[4] = 0.0;
2484 padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY)
2485 / (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
2486
2487 padfGeoTransform[0] =
2488 pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
2489 padfGeoTransform[3] =
2490 pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
2491 return TRUE;
2492 }
2493
2494 /* -------------------------------------------------------------------- */
2495 /* Compute source and destination ranges so we can normalize */
2496 /* the values to make the least squares computation more stable. */
2497 /* -------------------------------------------------------------------- */
2498 double min_pixel = pasGCPs[0].dfGCPPixel;
2499 double max_pixel = pasGCPs[0].dfGCPPixel;
2500 double min_line = pasGCPs[0].dfGCPLine;
2501 double max_line = pasGCPs[0].dfGCPLine;
2502 double min_geox = pasGCPs[0].dfGCPX;
2503 double max_geox = pasGCPs[0].dfGCPX;
2504 double min_geoy = pasGCPs[0].dfGCPY;
2505 double max_geoy = pasGCPs[0].dfGCPY;
2506
2507 for( int i = 1; i < nGCPCount; ++i ) {
2508 min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
2509 max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
2510 min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
2511 max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
2512 min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
2513 max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
2514 min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
2515 max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
2516 }
2517
2518 double EPS = 1.0e-12;
2519
2520 if( std::abs(max_pixel - min_pixel) < EPS
2521 || std::abs(max_line - min_line) < EPS
2522 || std::abs(max_geox - min_geox) < EPS
2523 || std::abs(max_geoy - min_geoy) < EPS)
2524 {
2525 return FALSE; // degenerate in at least one dimension.
2526 }
2527
2528 double pl_normalize[6], geo_normalize[6];
2529
2530 pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
2531 pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
2532 pl_normalize[2] = 0.0;
2533 pl_normalize[3] = -min_line / (max_line - min_line);
2534 pl_normalize[4] = 0.0;
2535 pl_normalize[5] = 1.0 / (max_line - min_line);
2536
2537 geo_normalize[0] = -min_geox / (max_geox - min_geox);
2538 geo_normalize[1] = 1.0 / (max_geox - min_geox);
2539 geo_normalize[2] = 0.0;
2540 geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
2541 geo_normalize[4] = 0.0;
2542 geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
2543
2544 /* -------------------------------------------------------------------- */
2545 /* In the general case, do a least squares error approximation by */
2546 /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum */
2547 /* -------------------------------------------------------------------- */
2548
2549 double sum_x = 0.0;
2550 double sum_y = 0.0;
2551 double sum_xy = 0.0;
2552 double sum_xx = 0.0;
2553 double sum_yy = 0.0;
2554 double sum_Lon = 0.0;
2555 double sum_Lonx = 0.0;
2556 double sum_Lony = 0.0;
2557 double sum_Lat = 0.0;
2558 double sum_Latx = 0.0;
2559 double sum_Laty = 0.0;
2560
2561 for ( int i = 0; i < nGCPCount; ++i ) {
2562 double pixel, line, geox, geoy;
2563
2564 GDALApplyGeoTransform(pl_normalize,
2565 pasGCPs[i].dfGCPPixel,
2566 pasGCPs[i].dfGCPLine,
2567 &pixel, &line);
2568 GDALApplyGeoTransform(geo_normalize,
2569 pasGCPs[i].dfGCPX,
2570 pasGCPs[i].dfGCPY,
2571 &geox, &geoy);
2572
2573 sum_x += pixel;
2574 sum_y += line;
2575 sum_xy += pixel * line;
2576 sum_xx += pixel * pixel;
2577 sum_yy += line * line;
2578 sum_Lon += geox;
2579 sum_Lonx += geox * pixel;
2580 sum_Lony += geox * line;
2581 sum_Lat += geoy;
2582 sum_Latx += geoy * pixel;
2583 sum_Laty += geoy * line;
2584 }
2585
2586 const double divisor =
2587 nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy)
2588 + 2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx
2589 - sum_x * sum_x * sum_yy;
2590
2591 /* -------------------------------------------------------------------- */
2592 /* If the divisor is zero, there is no valid solution. */
2593 /* -------------------------------------------------------------------- */
2594 if (divisor == 0.0)
2595 return FALSE;
2596
2597 /* -------------------------------------------------------------------- */
2598 /* Compute top/left origin. */
2599 /* -------------------------------------------------------------------- */
2600 double gt_normalized[6] = { 0.0 };
2601 gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy)
2602 + sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy)
2603 + sum_Lony * (sum_x * sum_xy - sum_y * sum_xx))
2604 / divisor;
2605
2606 gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy)
2607 + sum_Latx * (sum_y * sum_xy - sum_x * sum_yy)
2608 + sum_Laty * (sum_x * sum_xy - sum_y * sum_xx))
2609 / divisor;
2610
2611 /* -------------------------------------------------------------------- */
2612 /* Compute X related coefficients. */
2613 /* -------------------------------------------------------------------- */
2614 gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy)
2615 + sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y)
2616 + sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount))
2617 / divisor;
2618
2619 gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx)
2620 + sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy)
2621 + sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x))
2622 / divisor;
2623
2624 /* -------------------------------------------------------------------- */
2625 /* Compute Y related coefficients. */
2626 /* -------------------------------------------------------------------- */
2627 gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy)
2628 + sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y)
2629 + sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount))
2630 / divisor;
2631
2632 gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx)
2633 + sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy)
2634 + sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x))
2635 / divisor;
2636
2637 /* -------------------------------------------------------------------- */
2638 /* Compose the resulting transformation with the normalization */
2639 /* geotransformations. */
2640 /* -------------------------------------------------------------------- */
2641 double gt1p2[6] = { 0.0 };
2642 double inv_geo_normalize[6] = { 0.0 };
2643 if( !GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
2644 return FALSE;
2645
2646 GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
2647 GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
2648
2649 /* -------------------------------------------------------------------- */
2650 /* Now check if any of the input points fit this poorly. */
2651 /* -------------------------------------------------------------------- */
2652 if( !bApproxOK )
2653 {
2654 // FIXME? Not sure if it is the more accurate way of computing
2655 // pixel size
2656 double dfPixelSize =
2657 0.5 * (std::abs(padfGeoTransform[1])
2658 + std::abs(padfGeoTransform[2])
2659 + std::abs(padfGeoTransform[4])
2660 + std::abs(padfGeoTransform[5]));
2661 if( dfPixelSize == 0.0 )
2662 {
2663 CPLDebug("GDAL", "dfPixelSize = 0");
2664 return FALSE;
2665 }
2666
2667 for( int i = 0; i < nGCPCount; i++ )
2668 {
2669 const double dfErrorX =
2670 (pasGCPs[i].dfGCPPixel * padfGeoTransform[1]
2671 + pasGCPs[i].dfGCPLine * padfGeoTransform[2]
2672 + padfGeoTransform[0])
2673 - pasGCPs[i].dfGCPX;
2674 const double dfErrorY =
2675 (pasGCPs[i].dfGCPPixel * padfGeoTransform[4]
2676 + pasGCPs[i].dfGCPLine * padfGeoTransform[5]
2677 + padfGeoTransform[3])
2678 - pasGCPs[i].dfGCPY;
2679
2680 if( std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize
2681 || std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize )
2682 {
2683 CPLDebug("GDAL", "dfErrorX/dfPixelSize = %.2f, "
2684 "dfErrorY/dfPixelSize = %.2f",
2685 std::abs(dfErrorX)/dfPixelSize,
2686 std::abs(dfErrorY)/dfPixelSize);
2687 return FALSE;
2688 }
2689 }
2690 }
2691
2692 return TRUE;
2693 }
2694
2695 /************************************************************************/
2696 /* GDALComposeGeoTransforms() */
2697 /************************************************************************/
2698
2699 /**
2700 * \brief Compose two geotransforms.
2701 *
2702 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
2703 * being applied to a point.
2704 *
2705 * @param padfGT1 the first geotransform, six values.
2706 * @param padfGT2 the second geotransform, six values.
2707 * @param padfGTOut the output geotransform, six values, may safely be the same
2708 * array as padfGT1 or padfGT2.
2709 */
2710
GDALComposeGeoTransforms(const double * padfGT1,const double * padfGT2,double * padfGTOut)2711 void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
2712 double *padfGTOut)
2713
2714 {
2715 double gtwrk[6] = { 0.0 };
2716 // We need to think of the geotransform in a more normal form to do
2717 // the matrix multiple:
2718 //
2719 // __ __
2720 // | gt[1] gt[2] gt[0] |
2721 // | gt[4] gt[5] gt[3] |
2722 // | 0.0 0.0 1.0 |
2723 // -- --
2724 //
2725 // Then we can use normal matrix multiplication to produce the
2726 // composed transformation. I don't actually reform the matrix
2727 // explicitly which is why the following may seem kind of spagettish.
2728
2729 gtwrk[1] =
2730 padfGT2[1] * padfGT1[1]
2731 + padfGT2[2] * padfGT1[4];
2732 gtwrk[2] =
2733 padfGT2[1] * padfGT1[2]
2734 + padfGT2[2] * padfGT1[5];
2735 gtwrk[0] =
2736 padfGT2[1] * padfGT1[0]
2737 + padfGT2[2] * padfGT1[3]
2738 + padfGT2[0] * 1.0;
2739
2740 gtwrk[4] =
2741 padfGT2[4] * padfGT1[1]
2742 + padfGT2[5] * padfGT1[4];
2743 gtwrk[5] =
2744 padfGT2[4] * padfGT1[2]
2745 + padfGT2[5] * padfGT1[5];
2746 gtwrk[3] =
2747 padfGT2[4] * padfGT1[0]
2748 + padfGT2[5] * padfGT1[3]
2749 + padfGT2[3] * 1.0;
2750 memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
2751 }
2752
2753 /************************************************************************/
2754 /* StripIrrelevantOptions() */
2755 /************************************************************************/
2756
StripIrrelevantOptions(CPLXMLNode * psCOL,int nOptions)2757 static void StripIrrelevantOptions(CPLXMLNode* psCOL, int nOptions)
2758 {
2759 if( psCOL == nullptr )
2760 return;
2761 if( nOptions == 0 )
2762 nOptions = GDAL_OF_RASTER;
2763 if( (nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0 )
2764 return;
2765
2766 CPLXMLNode* psPrev = nullptr;
2767 for( CPLXMLNode* psIter = psCOL->psChild; psIter; )
2768 {
2769 if( psIter->eType == CXT_Element )
2770 {
2771 CPLXMLNode* psScope = CPLGetXMLNode(psIter, "scope");
2772 bool bStrip = false;
2773 if( nOptions == GDAL_OF_RASTER &&
2774 psScope && psScope->psChild &&
2775 psScope->psChild->pszValue &&
2776 EQUAL(psScope->psChild->pszValue, "vector") )
2777 {
2778 bStrip = true;
2779 }
2780 else if( nOptions == GDAL_OF_VECTOR &&
2781 psScope && psScope->psChild &&
2782 psScope->psChild->pszValue &&
2783 EQUAL(psScope->psChild->pszValue, "raster") )
2784 {
2785 bStrip = true;
2786 }
2787 if( psScope )
2788 {
2789 CPLRemoveXMLChild(psIter, psScope);
2790 CPLDestroyXMLNode(psScope);
2791 }
2792
2793 CPLXMLNode* psNext = psIter->psNext;
2794 if( bStrip )
2795 {
2796 if( psPrev )
2797 psPrev->psNext = psNext;
2798 else if( psCOL->psChild == psIter )
2799 psCOL->psChild = psNext;
2800 psIter->psNext = nullptr;
2801 CPLDestroyXMLNode(psIter);
2802 psIter = psNext;
2803 }
2804 else
2805 {
2806 psPrev = psIter;
2807 psIter = psNext;
2808 }
2809 }
2810 else
2811 {
2812 psIter = psIter->psNext;
2813 }
2814 }
2815 }
2816
2817 /************************************************************************/
2818 /* GDALGeneralCmdLineProcessor() */
2819 /************************************************************************/
2820
2821 /**
2822 * \brief General utility option processing.
2823 *
2824 * This function is intended to provide a variety of generic commandline
2825 * options for all GDAL commandline utilities. It takes care of the following
2826 * commandline options:
2827 *
2828 * --version: report version of GDAL in use.
2829 * --build: report build info about GDAL in use.
2830 * --license: report GDAL license info.
2831 * --formats: report all format drivers configured.
2832 * --format [format]: report details of one format driver.
2833 * --optfile filename: expand an option file into the argument list.
2834 * --config key value: set system configuration option.
2835 * --debug [on/off/value]: set debug level.
2836 * --mempreload dir: preload directory contents into /vsimem
2837 * --pause: Pause for user input (allows time to attach debugger)
2838 * --locale [locale]: Install a locale using setlocale() (debugging)
2839 * --help-general: report detailed help on general options.
2840 *
2841 * The argument array is replaced "in place" and should be freed with
2842 * CSLDestroy() when no longer needed. The typical usage looks something
2843 * like the following. Note that the formats should be registered so that
2844 * the --formats and --format options will work properly.
2845 *
2846 * int main( int argc, char ** argv )
2847 * {
2848 * GDALAllRegister();
2849 *
2850 * argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
2851 * if( argc < 1 )
2852 * exit( -argc );
2853 *
2854 * @param nArgc number of values in the argument list.
2855 * @param ppapszArgv pointer to the argument list array (will be updated in place).
2856 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
2857 * to determine which drivers should be displayed by --formats.
2858 * If set to 0, GDAL_OF_RASTER is assumed.
2859 *
2860 * @return updated nArgc argument count. Return of 0 requests terminate
2861 * without error, return of -1 requests exit with error code.
2862 */
2863
2864 int CPL_STDCALL
GDALGeneralCmdLineProcessor(int nArgc,char *** ppapszArgv,int nOptions)2865 GDALGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions )
2866
2867 {
2868 char **papszReturn = nullptr;
2869 int iArg;
2870 char **papszArgv = *ppapszArgv;
2871
2872 /* -------------------------------------------------------------------- */
2873 /* Preserve the program name. */
2874 /* -------------------------------------------------------------------- */
2875 papszReturn = CSLAddString( papszReturn, papszArgv[0] );
2876
2877 /* ==================================================================== */
2878 /* Loop over all arguments. */
2879 /* ==================================================================== */
2880 for( iArg = 1; iArg < nArgc; iArg++ )
2881 {
2882 /* -------------------------------------------------------------------- */
2883 /* --version */
2884 /* -------------------------------------------------------------------- */
2885 if( EQUAL(papszArgv[iArg],"--version") )
2886 {
2887 printf( "%s\n", GDALVersionInfo( "--version" ) );/*ok*/
2888 CSLDestroy( papszReturn );
2889 return 0;
2890 }
2891
2892 /* -------------------------------------------------------------------- */
2893 /* --build */
2894 /* -------------------------------------------------------------------- */
2895 else if( EQUAL(papszArgv[iArg],"--build") )
2896 {
2897 printf( "%s", GDALVersionInfo( "BUILD_INFO" ) );/*ok*/
2898 CSLDestroy( papszReturn );
2899 return 0;
2900 }
2901
2902 /* -------------------------------------------------------------------- */
2903 /* --license */
2904 /* -------------------------------------------------------------------- */
2905 else if( EQUAL(papszArgv[iArg],"--license") )
2906 {
2907 printf( "%s\n", GDALVersionInfo( "LICENSE" ) );/*ok*/
2908 CSLDestroy( papszReturn );
2909 return 0;
2910 }
2911
2912 /* -------------------------------------------------------------------- */
2913 /* --config */
2914 /* -------------------------------------------------------------------- */
2915 else if( EQUAL(papszArgv[iArg],"--config") )
2916 {
2917 if( iArg + 2 >= nArgc )
2918 {
2919 CPLError( CE_Failure, CPLE_AppDefined,
2920 "--config option given without a key and value argument." );
2921 CSLDestroy( papszReturn );
2922 return -1;
2923 }
2924
2925 CPLSetConfigOption( papszArgv[iArg+1], papszArgv[iArg+2] );
2926
2927 iArg += 2;
2928 }
2929
2930 /* -------------------------------------------------------------------- */
2931 /* --mempreload */
2932 /* -------------------------------------------------------------------- */
2933 else if( EQUAL(papszArgv[iArg],"--mempreload") )
2934 {
2935 if( iArg + 1 >= nArgc )
2936 {
2937 CPLError( CE_Failure, CPLE_AppDefined,
2938 "--mempreload option given without directory path.");
2939 CSLDestroy( papszReturn );
2940 return -1;
2941 }
2942
2943 char **papszFiles = VSIReadDir( papszArgv[iArg+1] );
2944 if( CSLCount(papszFiles) == 0 )
2945 {
2946 CPLError( CE_Failure, CPLE_AppDefined,
2947 "--mempreload given invalid or empty directory.");
2948 CSLDestroy( papszReturn );
2949 return -1;
2950 }
2951
2952 for( int i = 0; papszFiles[i] != nullptr; i++ )
2953 {
2954 if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") )
2955 continue;
2956
2957 CPLString osOldPath, osNewPath;
2958 osOldPath = CPLFormFilename( papszArgv[iArg+1],
2959 papszFiles[i], nullptr );
2960 osNewPath.Printf( "/vsimem/%s", papszFiles[i] );
2961
2962 VSIStatBufL sStatBuf;
2963 if( VSIStatL( osOldPath, &sStatBuf ) != 0
2964 || VSI_ISDIR( sStatBuf.st_mode ) )
2965 {
2966 CPLDebug( "VSI", "Skipping preload of %s.",
2967 osOldPath.c_str() );
2968 continue;
2969 }
2970
2971 CPLDebug( "VSI", "Preloading %s to %s.",
2972 osOldPath.c_str(), osNewPath.c_str() );
2973
2974 if( CPLCopyFile( osNewPath, osOldPath ) != 0 )
2975 {
2976 CPLError( CE_Failure, CPLE_AppDefined,
2977 "Failed to copy %s to /vsimem",
2978 osOldPath.c_str() );
2979 CSLDestroy( papszReturn );
2980 return -1;
2981 }
2982 }
2983
2984 CSLDestroy( papszFiles );
2985 iArg += 1;
2986 }
2987
2988 /* -------------------------------------------------------------------- */
2989 /* --debug */
2990 /* -------------------------------------------------------------------- */
2991 else if( EQUAL(papszArgv[iArg],"--debug") )
2992 {
2993 if( iArg + 1 >= nArgc )
2994 {
2995 CPLError( CE_Failure, CPLE_AppDefined,
2996 "--debug option given without debug level." );
2997 CSLDestroy( papszReturn );
2998 return -1;
2999 }
3000
3001 CPLSetConfigOption( "CPL_DEBUG", papszArgv[iArg+1] );
3002 iArg += 1;
3003 }
3004
3005 /* -------------------------------------------------------------------- */
3006 /* --optfile */
3007 /* -------------------------------------------------------------------- */
3008 else if( EQUAL(papszArgv[iArg],"--optfile") )
3009 {
3010 if( iArg + 1 >= nArgc )
3011 {
3012 CPLError( CE_Failure, CPLE_AppDefined,
3013 "--optfile option given without filename." );
3014 CSLDestroy( papszReturn );
3015 return -1;
3016 }
3017
3018 VSILFILE *fpOptFile = VSIFOpenL( papszArgv[iArg+1], "rb" );
3019
3020 if( fpOptFile == nullptr )
3021 {
3022 CPLError( CE_Failure, CPLE_AppDefined,
3023 "Unable to open optfile '%s'.\n%s",
3024 papszArgv[iArg+1], VSIStrerror( errno ) );
3025 CSLDestroy( papszReturn );
3026 return -1;
3027 }
3028
3029 const char *pszLine;
3030 // dummy value as first argument to please
3031 // GDALGeneralCmdLineProcessor()
3032 char** papszArgvOptfile = CSLAddString(nullptr, "");
3033 bool bHasOptfile = false;
3034 while( (pszLine = CPLReadLineL( fpOptFile )) != nullptr )
3035 {
3036 if( pszLine[0] == '#' || strlen(pszLine) == 0 )
3037 continue;
3038
3039 char **papszTokens = CSLTokenizeString( pszLine );
3040 for( int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
3041 {
3042 if( EQUAL( papszTokens[i], "--optfile") )
3043 {
3044 // To avoid potential recursion
3045 CPLError(CE_Warning, CPLE_AppDefined,
3046 "--optfile not supported in a option file");
3047 bHasOptfile = true;
3048 }
3049 papszArgvOptfile = CSLAddString( papszArgvOptfile, papszTokens[i] );
3050 }
3051 CSLDestroy( papszTokens );
3052 }
3053
3054 VSIFCloseL( fpOptFile );
3055
3056 if( !bHasOptfile )
3057 {
3058 char** papszArgvOptfileBefore = papszArgvOptfile;
3059 if( GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
3060 &papszArgvOptfile, nOptions) < 0 )
3061 {
3062 CSLDestroy( papszReturn );
3063 CSLDestroy(papszArgvOptfile);
3064 return -1;
3065 }
3066 CSLDestroy(papszArgvOptfileBefore);
3067 }
3068
3069 char** papszIter = papszArgvOptfile + 1;
3070 while( *papszIter )
3071 {
3072 papszReturn = CSLAddString(papszReturn, *papszIter);
3073 ++ papszIter;
3074 }
3075 CSLDestroy(papszArgvOptfile);
3076
3077 iArg += 1;
3078 }
3079
3080 /* -------------------------------------------------------------------- */
3081 /* --formats */
3082 /* -------------------------------------------------------------------- */
3083 else if( EQUAL(papszArgv[iArg], "--formats") )
3084 {
3085 if( nOptions == 0 )
3086 nOptions = GDAL_OF_RASTER;
3087
3088 printf( "Supported Formats:\n" );/*ok*/
3089 for( int iDr = 0; iDr < GDALGetDriverCount(); iDr++ )
3090 {
3091 GDALDriverH hDriver = GDALGetDriver(iDr);
3092
3093 const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets;
3094 char** papszMD = GDALGetMetadata( hDriver, nullptr );
3095
3096 if( nOptions == GDAL_OF_RASTER &&
3097 !CPLFetchBool( papszMD, GDAL_DCAP_RASTER, false ) )
3098 continue;
3099 if( nOptions == GDAL_OF_VECTOR &&
3100 !CPLFetchBool( papszMD, GDAL_DCAP_VECTOR, false ) )
3101 continue;
3102 if( nOptions == GDAL_OF_GNM &&
3103 !CPLFetchBool( papszMD, GDAL_DCAP_GNM, false ) )
3104 continue;
3105 if( nOptions == GDAL_OF_MULTIDIM_RASTER &&
3106 !CPLFetchBool( papszMD, GDAL_DCAP_MULTIDIM_RASTER, false ) )
3107 continue;
3108
3109 if( CPLFetchBool( papszMD, GDAL_DCAP_OPEN, false ) )
3110 pszRFlag = "r";
3111
3112 if( CPLFetchBool( papszMD, GDAL_DCAP_CREATE, false ) )
3113 pszWFlag = "w+";
3114 else if( CPLFetchBool( papszMD, GDAL_DCAP_CREATECOPY, false ) )
3115 pszWFlag = "w";
3116 else
3117 pszWFlag = "o";
3118
3119 if( CPLFetchBool( papszMD, GDAL_DCAP_VIRTUALIO, false ) )
3120 pszVirtualIO = "v";
3121 else
3122 pszVirtualIO = "";
3123
3124 if( CPLFetchBool( papszMD, GDAL_DMD_SUBDATASETS, false ) )
3125 pszSubdatasets = "s";
3126 else
3127 pszSubdatasets = "";
3128
3129 CPLString osKind;
3130 if( CPLFetchBool( papszMD, GDAL_DCAP_RASTER, false ) )
3131 osKind = "raster";
3132 if( CPLFetchBool( papszMD, GDAL_DCAP_MULTIDIM_RASTER, false ) )
3133 {
3134 if( !osKind.empty() ) osKind += ',';
3135 osKind += "multidimensional raster";
3136 }
3137 if( CPLFetchBool( papszMD, GDAL_DCAP_VECTOR, false ) )
3138 {
3139 if( !osKind.empty() ) osKind += ',';
3140 osKind += "vector";
3141 }
3142 if( CPLFetchBool( papszMD, GDAL_DCAP_GNM, false ) )
3143 {
3144 if( !osKind.empty() ) osKind += ',';
3145 osKind += "geography network";
3146 }
3147 if( osKind.empty() )
3148 osKind = "unknown kind";
3149
3150 printf( " %s -%s- (%s%s%s%s): %s\n",/*ok*/
3151 GDALGetDriverShortName( hDriver ),
3152 osKind.c_str(),
3153 pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets,
3154 GDALGetDriverLongName( hDriver ) );
3155 }
3156
3157 CSLDestroy( papszReturn );
3158 return 0;
3159 }
3160
3161 /* -------------------------------------------------------------------- */
3162 /* --format */
3163 /* -------------------------------------------------------------------- */
3164 else if( EQUAL(papszArgv[iArg], "--format") )
3165 {
3166 GDALDriverH hDriver;
3167 char **papszMD;
3168
3169 CSLDestroy( papszReturn );
3170
3171 if( iArg + 1 >= nArgc )
3172 {
3173 CPLError( CE_Failure, CPLE_AppDefined,
3174 "--format option given without a format code." );
3175 return -1;
3176 }
3177
3178 hDriver = GDALGetDriverByName( papszArgv[iArg+1] );
3179 if( hDriver == nullptr )
3180 {
3181 CPLError( CE_Failure, CPLE_AppDefined,
3182 "--format option given with format '%s', but that "
3183 "format not\nrecognised. Use the --formats option "
3184 "to get a list of available formats,\n"
3185 "and use the short code (i.e. GTiff or HFA) as the "
3186 "format identifier.\n",
3187 papszArgv[iArg+1] );
3188 return -1;
3189 }
3190
3191 printf( "Format Details:\n" );/*ok*/
3192 printf( " Short Name: %s\n", GDALGetDriverShortName( hDriver ) );/*ok*/
3193 printf( " Long Name: %s\n", GDALGetDriverLongName( hDriver ) );/*ok*/
3194
3195 papszMD = GDALGetMetadata( hDriver, nullptr );
3196 if( CPLFetchBool( papszMD, GDAL_DCAP_RASTER, false ) )
3197 printf( " Supports: Raster\n" );/*ok*/
3198 if( CPLFetchBool( papszMD, GDAL_DCAP_MULTIDIM_RASTER, false ) )
3199 printf( " Supports: Multidimensional raster\n" );/*ok*/
3200 if( CPLFetchBool( papszMD, GDAL_DCAP_VECTOR, false ) )
3201 printf( " Supports: Vector\n" );/*ok*/
3202 if( CPLFetchBool( papszMD, GDAL_DCAP_GNM, false ) )
3203 printf( " Supports: Geography Network\n" );/*ok*/
3204
3205 const char* pszExt = CSLFetchNameValue( papszMD, GDAL_DMD_EXTENSIONS );
3206 if( pszExt != nullptr )
3207 printf( " Extension%s: %s\n", (strchr(pszExt, ' ') ? "s" : ""),/*ok*/
3208 pszExt );
3209
3210 if( CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) )
3211 printf( " Mime Type: %s\n",/*ok*/
3212 CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) );
3213 if( CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) )
3214 printf( " Help Topic: %s\n",/*ok*/
3215 CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) );
3216
3217 if( CPLFetchBool( papszMD, GDAL_DMD_SUBDATASETS, false ) )
3218 printf( " Supports: Subdatasets\n" );/*ok*/
3219 if( CPLFetchBool( papszMD, GDAL_DCAP_OPEN, false ) )
3220 printf( " Supports: Open() - Open existing dataset.\n" );/*ok*/
3221 if( CPLFetchBool( papszMD, GDAL_DCAP_CREATE, false ) )
3222 printf( " Supports: Create() - Create writable dataset.\n" );/*ok*/
3223 if( CPLFetchBool( papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false ) )
3224 printf( " Supports: CreateMultiDimensional() - Create multidimensional dataset.\n" );/*ok*/
3225 if( CPLFetchBool( papszMD, GDAL_DCAP_CREATECOPY, false ) )
3226 printf( " Supports: CreateCopy() - Create dataset by copying another.\n" );/*ok*/
3227 if( CPLFetchBool( papszMD, GDAL_DCAP_VIRTUALIO, false ) )
3228 printf( " Supports: Virtual IO - eg. /vsimem/\n" );/*ok*/
3229 if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) )
3230 printf( " Creation Datatypes: %s\n",/*ok*/
3231 CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) );
3232 if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATATYPES ) )
3233 printf( " Creation Field Datatypes: %s\n",/*ok*/
3234 CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATATYPES ) );
3235 if ( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES ) )
3236 printf( " Creation Field Data Sub-types: %s\n",/*ok*/
3237 CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES ) );
3238 if( CPLFetchBool( papszMD, GDAL_DCAP_NOTNULL_FIELDS, false ) )
3239 printf( " Supports: Creating fields with NOT NULL constraint.\n" );/*ok*/
3240 if( CPLFetchBool( papszMD, GDAL_DCAP_UNIQUE_FIELDS, false ) )
3241 printf( " Supports: Creating fields with UNIQUE constraint.\n" );/*ok*/
3242 if( CPLFetchBool( papszMD, GDAL_DCAP_DEFAULT_FIELDS, false ) )
3243 printf( " Supports: Creating fields with DEFAULT values.\n" );/*ok*/
3244 if( CPLFetchBool( papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false ) )
3245 printf( " Supports: Creating geometry fields with NOT NULL constraint.\n" );/*ok*/
3246 if( CPLFetchBool( papszMD, GDAL_DCAP_NONSPATIAL, false ) )
3247 printf( " No support for geometries.\n" );/*ok*/
3248 if( CPLFetchBool( papszMD, GDAL_DCAP_FEATURE_STYLES, false ) )
3249 printf( " Supports: Feature styles.\n" );/*ok*/
3250
3251 for( const char* key: { GDAL_DMD_CREATIONOPTIONLIST,
3252 GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
3253 GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
3254 GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
3255 GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
3256 GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
3257 GDAL_DS_LAYER_CREATIONOPTIONLIST } )
3258 {
3259 if( CSLFetchNameValue( papszMD, key ) )
3260 {
3261 CPLXMLNode *psCOL =
3262 CPLParseXMLString(
3263 CSLFetchNameValue( papszMD,
3264 key ) );
3265 StripIrrelevantOptions(psCOL, nOptions);
3266 char *pszFormattedXML =
3267 CPLSerializeXMLTree( psCOL );
3268
3269 CPLDestroyXMLNode( psCOL );
3270
3271 printf( "\n%s\n", pszFormattedXML );/*ok*/
3272 CPLFree( pszFormattedXML );
3273 }
3274 }
3275
3276 if( CSLFetchNameValue( papszMD, GDAL_DMD_CONNECTION_PREFIX ) )
3277 printf( " Connection prefix: %s\n",/*ok*/
3278 CSLFetchNameValue( papszMD, GDAL_DMD_CONNECTION_PREFIX ) );
3279
3280 if( CSLFetchNameValue( papszMD, GDAL_DMD_OPENOPTIONLIST ) )
3281 {
3282 CPLXMLNode *psCOL =
3283 CPLParseXMLString(
3284 CSLFetchNameValue( papszMD,
3285 GDAL_DMD_OPENOPTIONLIST ) );
3286 StripIrrelevantOptions(psCOL, nOptions);
3287 char *pszFormattedXML =
3288 CPLSerializeXMLTree( psCOL );
3289
3290 CPLDestroyXMLNode( psCOL );
3291
3292 printf( "%s\n", pszFormattedXML );/*ok*/
3293 CPLFree( pszFormattedXML );
3294 }
3295
3296 bool bFirstOtherOption = true;
3297 for( char** papszIter = papszMD;
3298 papszIter && *papszIter; ++papszIter )
3299 {
3300 if( !STARTS_WITH(*papszIter, "DCAP_") &&
3301 !STARTS_WITH(*papszIter, "DMD_") &&
3302 !STARTS_WITH(*papszIter, "DS_") &&
3303 !STARTS_WITH(*papszIter, "OGR_DRIVER=") )
3304 {
3305 if( bFirstOtherOption )
3306 printf(" Other metadata items:\n"); /*ok*/
3307 bFirstOtherOption = false;
3308 printf(" %s\n", *papszIter); /*ok*/
3309 }
3310 }
3311
3312 return 0;
3313 }
3314
3315 /* -------------------------------------------------------------------- */
3316 /* --help-general */
3317 /* -------------------------------------------------------------------- */
3318 else if( EQUAL(papszArgv[iArg],"--help-general") )
3319 {
3320 printf( "Generic GDAL utility command options:\n" );/*ok*/
3321 printf( " --version: report version of GDAL in use.\n" );/*ok*/
3322 printf( " --license: report GDAL license info.\n" );/*ok*/
3323 printf( " --formats: report all configured format drivers.\n" );/*ok*/
3324 printf( " --format [format]: details of one format.\n" );/*ok*/
3325 printf( " --optfile filename: expand an option file into the argument list.\n" );/*ok*/
3326 printf( " --config key value: set system configuration option.\n" );/*ok*/
3327 printf( " --debug [on/off/value]: set debug level.\n" );/*ok*/
3328 printf( " --pause: wait for user input, time to attach debugger\n" );/*ok*/
3329 printf( " --locale [locale]: install locale for debugging "/*ok*/
3330 "(i.e. en_US.UTF-8)\n" );
3331 printf( " --help-general: report detailed help on general options.\n" );/*ok*/
3332 CSLDestroy( papszReturn );
3333 return 0;
3334 }
3335
3336 /* -------------------------------------------------------------------- */
3337 /* --locale */
3338 /* -------------------------------------------------------------------- */
3339 else if( iArg < nArgc-1 && EQUAL(papszArgv[iArg],"--locale") )
3340 {
3341 CPLsetlocale( LC_ALL, papszArgv[++iArg] );
3342 }
3343
3344 /* -------------------------------------------------------------------- */
3345 /* --pause */
3346 /* -------------------------------------------------------------------- */
3347 else if( EQUAL(papszArgv[iArg],"--pause") )
3348 {
3349 std::cout << "Hit <ENTER> to Continue." << std::endl;
3350 std::cin.clear();
3351 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
3352 }
3353
3354 /* -------------------------------------------------------------------- */
3355 /* Carry through unrecognized options. */
3356 /* -------------------------------------------------------------------- */
3357 else
3358 {
3359 papszReturn = CSLAddString( papszReturn, papszArgv[iArg] );
3360 }
3361 }
3362
3363 *ppapszArgv = papszReturn;
3364
3365 return CSLCount( *ppapszArgv );
3366 }
3367
3368 /************************************************************************/
3369 /* _FetchDblFromMD() */
3370 /************************************************************************/
3371
_FetchDblFromMD(CSLConstList papszMD,const char * pszKey,double * padfTarget,int nCount,double dfDefault)3372 static bool _FetchDblFromMD( CSLConstList papszMD, const char *pszKey,
3373 double *padfTarget, int nCount, double dfDefault )
3374
3375 {
3376 char szFullKey[200];
3377
3378 snprintf( szFullKey, sizeof(szFullKey), "%s", pszKey );
3379
3380 const char *pszValue = CSLFetchNameValue( papszMD, szFullKey );
3381
3382 for( int i = 0; i < nCount; i++ )
3383 padfTarget[i] = dfDefault;
3384
3385 if( pszValue == nullptr )
3386 return false;
3387
3388 if( nCount == 1 )
3389 {
3390 *padfTarget = CPLAtofM( pszValue );
3391 return true;
3392 }
3393
3394 char **papszTokens = CSLTokenizeStringComplex( pszValue, " ,",
3395 FALSE, FALSE );
3396
3397 if( CSLCount( papszTokens ) != nCount )
3398 {
3399 CSLDestroy( papszTokens );
3400 return false;
3401 }
3402
3403 for( int i = 0; i < nCount; i++ )
3404 padfTarget[i] = CPLAtofM(papszTokens[i]);
3405
3406 CSLDestroy( papszTokens );
3407
3408 return true;
3409 }
3410
3411 /************************************************************************/
3412 /* GDALExtractRPCInfo() */
3413 /************************************************************************/
3414
3415 /** Extract RPC info from metadata, and apply to an RPCInfo structure.
3416 *
3417 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
3418 *
3419 * @param papszMD Dictionary of metadata representing RPC
3420 * @param psRPC (output) Pointer to structure to hold the RPC values.
3421 * @return TRUE in case of success. FALSE in case of failure.
3422 */
GDALExtractRPCInfoV1(CSLConstList papszMD,GDALRPCInfoV1 * psRPC)3423 int CPL_STDCALL GDALExtractRPCInfoV1( CSLConstList papszMD, GDALRPCInfoV1 *psRPC )
3424
3425 {
3426 GDALRPCInfoV2 sRPC;
3427 if( !GDALExtractRPCInfoV2(papszMD, &sRPC) )
3428 return FALSE;
3429 memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1) );
3430 return TRUE;
3431 }
3432
3433 /** Extract RPC info from metadata, and apply to an RPCInfo structure.
3434 *
3435 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
3436 *
3437 * @param papszMD Dictionary of metadata representing RPC
3438 * @param psRPC (output) Pointer to structure to hold the RPC values.
3439 * @return TRUE in case of success. FALSE in case of failure.
3440 */
GDALExtractRPCInfoV2(CSLConstList papszMD,GDALRPCInfoV2 * psRPC)3441 int CPL_STDCALL GDALExtractRPCInfoV2( CSLConstList papszMD, GDALRPCInfoV2 *psRPC )
3442
3443 {
3444 if( CSLFetchNameValue( papszMD, RPC_LINE_NUM_COEFF ) == nullptr )
3445 return FALSE;
3446
3447 if( CSLFetchNameValue( papszMD, RPC_LINE_NUM_COEFF ) == nullptr
3448 || CSLFetchNameValue( papszMD, RPC_LINE_DEN_COEFF ) == nullptr
3449 || CSLFetchNameValue( papszMD, RPC_SAMP_NUM_COEFF ) == nullptr
3450 || CSLFetchNameValue( papszMD, RPC_SAMP_DEN_COEFF ) == nullptr )
3451 {
3452 CPLError( CE_Failure, CPLE_AppDefined,
3453 "Some required RPC metadata missing in GDALExtractRPCInfo()");
3454 return FALSE;
3455 }
3456
3457 _FetchDblFromMD( papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0 );
3458 _FetchDblFromMD( papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0 );
3459 _FetchDblFromMD( papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0 );
3460 _FetchDblFromMD( papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0 );
3461 _FetchDblFromMD( papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0 );
3462 _FetchDblFromMD( papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0 );
3463 _FetchDblFromMD( papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0 );
3464 _FetchDblFromMD( papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE),1, 1.0);
3465 _FetchDblFromMD( papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0 );
3466 _FetchDblFromMD( papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0 );
3467 _FetchDblFromMD( papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0 );
3468 _FetchDblFromMD( papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0 );
3469
3470 _FetchDblFromMD( papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF,
3471 20, 0.0 );
3472 _FetchDblFromMD( papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF,
3473 20, 0.0 );
3474 _FetchDblFromMD( papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF,
3475 20, 0.0 );
3476 _FetchDblFromMD( papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF,
3477 20, 0.0 );
3478
3479 _FetchDblFromMD( papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0 );
3480 _FetchDblFromMD( papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0 );
3481 _FetchDblFromMD( papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0 );
3482 _FetchDblFromMD( papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0 );
3483
3484 return TRUE;
3485 }
3486
3487 /************************************************************************/
3488 /* GDALFindAssociatedAuxFile() */
3489 /************************************************************************/
3490
GDALFindAssociatedAuxFile(const char * pszBasename,GDALAccess eAccess,GDALDataset * poDependentDS)3491 GDALDataset *GDALFindAssociatedAuxFile( const char *pszBasename,
3492 GDALAccess eAccess,
3493 GDALDataset *poDependentDS )
3494
3495 {
3496 const char *pszAuxSuffixLC = "aux";
3497 const char *pszAuxSuffixUC = "AUX";
3498
3499 if( EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC) )
3500 return nullptr;
3501
3502 /* -------------------------------------------------------------------- */
3503 /* Don't even try to look for an .aux file if we don't have a */
3504 /* path of any kind. */
3505 /* -------------------------------------------------------------------- */
3506 if( strlen(pszBasename) == 0 )
3507 return nullptr;
3508
3509 /* -------------------------------------------------------------------- */
3510 /* We didn't find that, so try and find a corresponding aux */
3511 /* file. Check that we are the dependent file of the aux */
3512 /* file, or if we aren't verify that the dependent file does */
3513 /* not exist, likely mean it is us but some sort of renaming */
3514 /* has occurred. */
3515 /* -------------------------------------------------------------------- */
3516 CPLString osJustFile = CPLGetFilename(pszBasename); // without dir
3517 CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC);
3518 GDALDataset *poODS = nullptr;
3519 GByte abyHeader[32];
3520
3521 VSILFILE *fp = VSIFOpenL( osAuxFilename, "rb" );
3522
3523 if ( fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
3524 {
3525 // Can't found file with lower case suffix. Try the upper case one.
3526 osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC);
3527 fp = VSIFOpenL( osAuxFilename, "rb" );
3528 }
3529
3530 if( fp != nullptr )
3531 {
3532 if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 &&
3533 STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader), "EHFA_HEADER_TAG") )
3534 {
3535 /* Avoid causing failure in opening of main file from SWIG bindings */
3536 /* when auxiliary file cannot be opened (#3269) */
3537 CPLTurnFailureIntoWarning(TRUE);
3538 if( poDependentDS != nullptr && poDependentDS->GetShared() )
3539 poODS = GDALDataset::FromHandle(GDALOpenShared( osAuxFilename, eAccess ));
3540 else
3541 poODS = GDALDataset::FromHandle(GDALOpen( osAuxFilename, eAccess ));
3542 CPLTurnFailureIntoWarning(FALSE);
3543 }
3544 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
3545 }
3546
3547 /* -------------------------------------------------------------------- */
3548 /* Try replacing extension with .aux */
3549 /* -------------------------------------------------------------------- */
3550 if( poODS != nullptr )
3551 {
3552 const char *pszDep
3553 = poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" );
3554 if( pszDep == nullptr )
3555 {
3556 CPLDebug( "AUX",
3557 "Found %s but it has no dependent file, ignoring.",
3558 osAuxFilename.c_str() );
3559 GDALClose( poODS );
3560 poODS = nullptr;
3561 }
3562 else if( !EQUAL(pszDep,osJustFile) )
3563 {
3564 VSIStatBufL sStatBuf;
3565
3566 if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
3567 {
3568 CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.",
3569 osAuxFilename.c_str(),
3570 pszDep, osJustFile.c_str() );
3571 GDALClose( poODS );
3572 poODS = nullptr;
3573 }
3574 else
3575 {
3576 CPLDebug( "AUX", "%s is for file %s, not %s, but since\n"
3577 "%s does not exist, we will use .aux file as our own.",
3578 osAuxFilename.c_str(),
3579 pszDep, osJustFile.c_str(),
3580 pszDep );
3581 }
3582 }
3583
3584 /* -------------------------------------------------------------------- */
3585 /* Confirm that the aux file matches the configuration of the */
3586 /* dependent dataset. */
3587 /* -------------------------------------------------------------------- */
3588 if( poODS != nullptr && poDependentDS != nullptr
3589 && (poODS->GetRasterCount() != poDependentDS->GetRasterCount()
3590 || poODS->GetRasterXSize() != poDependentDS->GetRasterXSize()
3591 || poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) )
3592 {
3593 CPLDebug( "AUX",
3594 "Ignoring aux file %s as its raster configuration\n"
3595 "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
3596 osAuxFilename.c_str(),
3597 poODS->GetRasterXSize(),
3598 poODS->GetRasterYSize(),
3599 poODS->GetRasterCount(),
3600 poDependentDS->GetRasterXSize(),
3601 poDependentDS->GetRasterYSize(),
3602 poDependentDS->GetRasterCount() );
3603
3604 GDALClose( poODS );
3605 poODS = nullptr;
3606 }
3607 }
3608
3609 /* -------------------------------------------------------------------- */
3610 /* Try appending .aux to the end of the filename. */
3611 /* -------------------------------------------------------------------- */
3612 if( poODS == nullptr )
3613 {
3614 osAuxFilename = pszBasename;
3615 osAuxFilename += ".";
3616 osAuxFilename += pszAuxSuffixLC;
3617 fp = VSIFOpenL( osAuxFilename, "rb" );
3618 if ( fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename) )
3619 {
3620 // Can't found file with lower case suffix. Try the upper case one.
3621 osAuxFilename = pszBasename;
3622 osAuxFilename += ".";
3623 osAuxFilename += pszAuxSuffixUC;
3624 fp = VSIFOpenL( osAuxFilename, "rb" );
3625 }
3626
3627 if( fp != nullptr )
3628 {
3629 if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 &&
3630 STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader), "EHFA_HEADER_TAG") )
3631 {
3632 /* Avoid causing failure in opening of main file from SWIG bindings */
3633 /* when auxiliary file cannot be opened (#3269) */
3634 CPLTurnFailureIntoWarning(TRUE);
3635 if( poDependentDS != nullptr && poDependentDS->GetShared() )
3636 poODS = GDALDataset::FromHandle(GDALOpenShared( osAuxFilename, eAccess ));
3637 else
3638 poODS = GDALDataset::FromHandle(GDALOpen( osAuxFilename, eAccess ));
3639 CPLTurnFailureIntoWarning(FALSE);
3640 }
3641 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
3642 }
3643
3644 if( poODS != nullptr )
3645 {
3646 const char *pszDep
3647 = poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" );
3648 if( pszDep == nullptr )
3649 {
3650 CPLDebug( "AUX",
3651 "Found %s but it has no dependent file, ignoring.",
3652 osAuxFilename.c_str() );
3653 GDALClose( poODS );
3654 poODS = nullptr;
3655 }
3656 else if( !EQUAL(pszDep,osJustFile) )
3657 {
3658 VSIStatBufL sStatBuf;
3659
3660 if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
3661 {
3662 CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.",
3663 osAuxFilename.c_str(),
3664 pszDep, osJustFile.c_str() );
3665 GDALClose( poODS );
3666 poODS = nullptr;
3667 }
3668 else
3669 {
3670 CPLDebug( "AUX", "%s is for file %s, not %s, but since\n"
3671 "%s does not exist, we will use .aux file as our own.",
3672 osAuxFilename.c_str(),
3673 pszDep, osJustFile.c_str(),
3674 pszDep );
3675 }
3676 }
3677 }
3678 }
3679
3680 /* -------------------------------------------------------------------- */
3681 /* Confirm that the aux file matches the configuration of the */
3682 /* dependent dataset. */
3683 /* -------------------------------------------------------------------- */
3684 if( poODS != nullptr && poDependentDS != nullptr
3685 && (poODS->GetRasterCount() != poDependentDS->GetRasterCount()
3686 || poODS->GetRasterXSize() != poDependentDS->GetRasterXSize()
3687 || poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) )
3688 {
3689 CPLDebug( "AUX",
3690 "Ignoring aux file %s as its raster configuration\n"
3691 "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
3692 osAuxFilename.c_str(),
3693 poODS->GetRasterXSize(),
3694 poODS->GetRasterYSize(),
3695 poODS->GetRasterCount(),
3696 poDependentDS->GetRasterXSize(),
3697 poDependentDS->GetRasterYSize(),
3698 poDependentDS->GetRasterCount() );
3699
3700 GDALClose( poODS );
3701 poODS = nullptr;
3702 }
3703
3704 return poODS;
3705 }
3706
3707 /************************************************************************/
3708 /* Infrastructure to check that dataset characteristics are valid */
3709 /************************************************************************/
3710
3711 CPL_C_START
3712
3713 /**
3714 * \brief Return TRUE if the dataset dimensions are valid.
3715 *
3716 * @param nXSize raster width
3717 * @param nYSize raster height
3718 *
3719 * @since GDAL 1.7.0
3720 */
GDALCheckDatasetDimensions(int nXSize,int nYSize)3721 int GDALCheckDatasetDimensions( int nXSize, int nYSize )
3722 {
3723 if (nXSize <= 0 || nYSize <= 0)
3724 {
3725 CPLError(CE_Failure, CPLE_AppDefined,
3726 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
3727 return FALSE;
3728 }
3729 return TRUE;
3730 }
3731
3732 /**
3733 * \brief Return TRUE if the band count is valid.
3734 *
3735 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
3736 * the band count will be compared to the maximum number of band allowed.
3737 * If not defined, the maximum number allowed is 65536.
3738 *
3739 * @param nBands the band count
3740 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
3741 *
3742 * @since GDAL 1.7.0
3743 */
3744
GDALCheckBandCount(int nBands,int bIsZeroAllowed)3745 int GDALCheckBandCount( int nBands, int bIsZeroAllowed )
3746 {
3747 if (nBands < 0 || (!bIsZeroAllowed && nBands == 0) )
3748 {
3749 CPLError(CE_Failure, CPLE_AppDefined,
3750 "Invalid band count : %d", nBands);
3751 return FALSE;
3752 }
3753 const char* pszMaxBandCount = CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
3754 /* coverity[tainted_data] */
3755 int nMaxBands = atoi(pszMaxBandCount);
3756 if ( nBands > nMaxBands )
3757 {
3758 CPLError(CE_Failure, CPLE_AppDefined,
3759 "Invalid band count : %d. Maximum allowed currently is %d. "
3760 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a legitimate number.",
3761 nBands, nMaxBands);
3762 return FALSE;
3763 }
3764 return TRUE;
3765 }
3766
3767 CPL_C_END
3768
3769 /************************************************************************/
3770 /* GDALSerializeGCPListToXML() */
3771 /************************************************************************/
3772
GDALSerializeGCPListToXML(CPLXMLNode * psParentNode,GDAL_GCP * pasGCPList,int nGCPCount,const OGRSpatialReference * poGCP_SRS)3773 void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode,
3774 GDAL_GCP* pasGCPList,
3775 int nGCPCount,
3776 const OGRSpatialReference* poGCP_SRS )
3777 {
3778 CPLString oFmt;
3779
3780 CPLXMLNode *psPamGCPList = CPLCreateXMLNode( psParentNode, CXT_Element,
3781 "GCPList" );
3782
3783 CPLXMLNode* psLastChild = nullptr;
3784
3785 if( poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty() )
3786 {
3787 char* pszWKT = nullptr;
3788 poGCP_SRS->exportToWkt(&pszWKT);
3789 CPLSetXMLValue( psPamGCPList, "#Projection",
3790 pszWKT );
3791 CPLFree(pszWKT);
3792 const auto& mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
3793 CPLString osMapping;
3794 for( size_t i = 0; i < mapping.size(); ++i )
3795 {
3796 if( !osMapping.empty() )
3797 osMapping += ",";
3798 osMapping += CPLSPrintf("%d", mapping[i]);
3799 }
3800 CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
3801 osMapping.c_str());
3802
3803 psLastChild = psPamGCPList->psChild->psNext;
3804 }
3805
3806 for( int iGCP = 0; iGCP < nGCPCount; iGCP++ )
3807 {
3808 GDAL_GCP *psGCP = pasGCPList + iGCP;
3809
3810 CPLXMLNode *psXMLGCP = CPLCreateXMLNode( nullptr, CXT_Element, "GCP" );
3811
3812 if( psLastChild == nullptr )
3813 psPamGCPList->psChild = psXMLGCP;
3814 else
3815 psLastChild->psNext = psXMLGCP;
3816 psLastChild = psXMLGCP;
3817
3818 CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId );
3819
3820 if( psGCP->pszInfo != nullptr && strlen(psGCP->pszInfo) > 0 )
3821 CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo );
3822
3823 CPLSetXMLValue( psXMLGCP, "#Pixel",
3824 oFmt.Printf( "%.4f", psGCP->dfGCPPixel ) );
3825
3826 CPLSetXMLValue( psXMLGCP, "#Line",
3827 oFmt.Printf( "%.4f", psGCP->dfGCPLine ) );
3828
3829 CPLSetXMLValue( psXMLGCP, "#X",
3830 oFmt.Printf( "%.12E", psGCP->dfGCPX ) );
3831
3832 CPLSetXMLValue( psXMLGCP, "#Y",
3833 oFmt.Printf( "%.12E", psGCP->dfGCPY ) );
3834
3835 /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */
3836 if( psGCP->dfGCPZ != 0.0 )
3837 CPLSetXMLValue( psXMLGCP, "#Z",
3838 oFmt.Printf( "%.12E", psGCP->dfGCPZ ) );
3839 }
3840 }
3841
3842 /************************************************************************/
3843 /* GDALDeserializeGCPListFromXML() */
3844 /************************************************************************/
3845
GDALDeserializeGCPListFromXML(CPLXMLNode * psGCPList,GDAL_GCP ** ppasGCPList,int * pnGCPCount,OGRSpatialReference ** ppoGCP_SRS)3846 void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList,
3847 GDAL_GCP** ppasGCPList,
3848 int* pnGCPCount,
3849 OGRSpatialReference** ppoGCP_SRS )
3850 {
3851 if( ppoGCP_SRS )
3852 {
3853 const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", nullptr);
3854
3855 *ppoGCP_SRS = nullptr;
3856 if( pszRawProj && pszRawProj[0] )
3857 {
3858 *ppoGCP_SRS = new OGRSpatialReference();
3859 (*ppoGCP_SRS)->SetFromUserInput( pszRawProj );
3860
3861 const char* pszMapping =
3862 CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
3863 if( pszMapping )
3864 {
3865 char** papszTokens = CSLTokenizeStringComplex( pszMapping, ",", FALSE, FALSE);
3866 std::vector<int> anMapping;
3867 for( int i = 0; papszTokens && papszTokens[i]; i++ )
3868 {
3869 anMapping.push_back(atoi(papszTokens[i]));
3870 }
3871 CSLDestroy(papszTokens);
3872 (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
3873 }
3874 else
3875 {
3876 (*ppoGCP_SRS)->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3877 }
3878 }
3879 }
3880
3881 // Count GCPs.
3882 int nGCPMax = 0;
3883
3884 for( CPLXMLNode *psXMLGCP = psGCPList->psChild;
3885 psXMLGCP != nullptr;
3886 psXMLGCP = psXMLGCP->psNext )
3887 {
3888
3889 if( !EQUAL(psXMLGCP->pszValue,"GCP") ||
3890 psXMLGCP->eType != CXT_Element )
3891 continue;
3892
3893 nGCPMax++;
3894 }
3895
3896 *ppasGCPList = static_cast<GDAL_GCP *>(
3897 nGCPMax ? CPLCalloc(sizeof(GDAL_GCP), nGCPMax) : nullptr );
3898 *pnGCPCount = 0;
3899
3900 if( nGCPMax == 0 )
3901 return;
3902
3903 for( CPLXMLNode *psXMLGCP = psGCPList->psChild;
3904 *ppasGCPList != nullptr && psXMLGCP != nullptr;
3905 psXMLGCP = psXMLGCP->psNext )
3906 {
3907 GDAL_GCP *psGCP = *ppasGCPList + *pnGCPCount;
3908
3909 if( !EQUAL(psXMLGCP->pszValue,"GCP") ||
3910 psXMLGCP->eType != CXT_Element )
3911 continue;
3912
3913 GDALInitGCPs( 1, psGCP );
3914
3915 CPLFree( psGCP->pszId );
3916 psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id",""));
3917
3918 CPLFree( psGCP->pszInfo );
3919 psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info",""));
3920
3921 psGCP->dfGCPPixel = CPLAtof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0"));
3922 psGCP->dfGCPLine = CPLAtof(CPLGetXMLValue(psXMLGCP,"Line","0.0"));
3923
3924 psGCP->dfGCPX = CPLAtof(CPLGetXMLValue(psXMLGCP,"X","0.0"));
3925 psGCP->dfGCPY = CPLAtof(CPLGetXMLValue(psXMLGCP,"Y","0.0"));
3926 const char* pszZ = CPLGetXMLValue(psXMLGCP,"Z",nullptr);
3927 if( pszZ == nullptr )
3928 {
3929 // Note: GDAL 1.10.1 and older generated #GCPZ,
3930 // but could not read it back.
3931 pszZ = CPLGetXMLValue(psXMLGCP,"GCPZ","0.0");
3932 }
3933 psGCP->dfGCPZ = CPLAtof(pszZ);
3934
3935 (*pnGCPCount)++;
3936 }
3937 }
3938
3939 /************************************************************************/
3940 /* GDALSerializeOpenOptionsToXML() */
3941 /************************************************************************/
3942
GDALSerializeOpenOptionsToXML(CPLXMLNode * psParentNode,char ** papszOpenOptions)3943 void GDALSerializeOpenOptionsToXML( CPLXMLNode* psParentNode, char** papszOpenOptions)
3944 {
3945 if( papszOpenOptions != nullptr )
3946 {
3947 CPLXMLNode* psOpenOptions = CPLCreateXMLNode( psParentNode, CXT_Element, "OpenOptions" );
3948 CPLXMLNode* psLastChild = nullptr;
3949
3950 for(char** papszIter = papszOpenOptions; *papszIter != nullptr; papszIter ++ )
3951 {
3952 const char *pszRawValue;
3953 char *pszKey = nullptr;
3954 CPLXMLNode *psOOI;
3955
3956 pszRawValue = CPLParseNameValue( *papszIter, &pszKey );
3957
3958 psOOI = CPLCreateXMLNode( nullptr, CXT_Element, "OOI" );
3959 if( psLastChild == nullptr )
3960 psOpenOptions->psChild = psOOI;
3961 else
3962 psLastChild->psNext = psOOI;
3963 psLastChild = psOOI;
3964
3965 CPLSetXMLValue( psOOI, "#key", pszKey );
3966 CPLCreateXMLNode( psOOI, CXT_Text, pszRawValue );
3967
3968 CPLFree( pszKey );
3969 }
3970 }
3971 }
3972
3973 /************************************************************************/
3974 /* GDALDeserializeOpenOptionsFromXML() */
3975 /************************************************************************/
3976
GDALDeserializeOpenOptionsFromXML(CPLXMLNode * psParentNode)3977 char** GDALDeserializeOpenOptionsFromXML( CPLXMLNode* psParentNode )
3978 {
3979 char** papszOpenOptions = nullptr;
3980 CPLXMLNode* psOpenOptions = CPLGetXMLNode(psParentNode, "OpenOptions");
3981 if( psOpenOptions != nullptr )
3982 {
3983 CPLXMLNode* psOOI;
3984 for( psOOI = psOpenOptions->psChild; psOOI != nullptr;
3985 psOOI = psOOI->psNext )
3986 {
3987 if( !EQUAL(psOOI->pszValue,"OOI")
3988 || psOOI->eType != CXT_Element
3989 || psOOI->psChild == nullptr
3990 || psOOI->psChild->psNext == nullptr
3991 || psOOI->psChild->eType != CXT_Attribute
3992 || psOOI->psChild->psChild == nullptr )
3993 continue;
3994
3995 char* pszName = psOOI->psChild->psChild->pszValue;
3996 char* pszValue = psOOI->psChild->psNext->pszValue;
3997 if( pszName != nullptr && pszValue != nullptr )
3998 papszOpenOptions = CSLSetNameValue( papszOpenOptions, pszName, pszValue );
3999 }
4000 }
4001 return papszOpenOptions;
4002 }
4003
4004 /************************************************************************/
4005 /* GDALRasterIOGetResampleAlg() */
4006 /************************************************************************/
4007
GDALRasterIOGetResampleAlg(const char * pszResampling)4008 GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char* pszResampling)
4009 {
4010 GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
4011 if( STARTS_WITH_CI(pszResampling, "NEAR") )
4012 eResampleAlg = GRIORA_NearestNeighbour;
4013 else if( EQUAL(pszResampling, "BILINEAR") )
4014 eResampleAlg = GRIORA_Bilinear;
4015 else if( EQUAL(pszResampling, "CUBIC") )
4016 eResampleAlg = GRIORA_Cubic;
4017 else if( EQUAL(pszResampling, "CUBICSPLINE") )
4018 eResampleAlg = GRIORA_CubicSpline;
4019 else if( EQUAL(pszResampling, "LANCZOS") )
4020 eResampleAlg = GRIORA_Lanczos;
4021 else if( EQUAL(pszResampling, "AVERAGE") )
4022 eResampleAlg = GRIORA_Average;
4023 else if( EQUAL(pszResampling, "RMS") )
4024 eResampleAlg = GRIORA_RMS;
4025 else if( EQUAL(pszResampling, "MODE") )
4026 eResampleAlg = GRIORA_Mode;
4027 else if( EQUAL(pszResampling, "GAUSS") )
4028 eResampleAlg = GRIORA_Gauss;
4029 else
4030 CPLError(CE_Warning, CPLE_NotSupported,
4031 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
4032 return eResampleAlg;
4033 }
4034
4035 /************************************************************************/
4036 /* GDALRasterIOGetResampleAlgStr() */
4037 /************************************************************************/
4038
GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)4039 const char* GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
4040 {
4041 switch(eResampleAlg)
4042 {
4043 case GRIORA_NearestNeighbour:
4044 return "NearestNeighbour";
4045 case GRIORA_Bilinear:
4046 return "Bilinear";
4047 case GRIORA_Cubic:
4048 return "Cubic";
4049 case GRIORA_CubicSpline:
4050 return "CubicSpline";
4051 case GRIORA_Lanczos:
4052 return "Lanczos";
4053 case GRIORA_Average:
4054 return "Average";
4055 case GRIORA_RMS:
4056 return "RMS";
4057 case GRIORA_Mode:
4058 return "Mode";
4059 case GRIORA_Gauss:
4060 return "Gauss";
4061 default:
4062 CPLAssert(false);
4063 return "Unknown";
4064 }
4065 }
4066
4067 /************************************************************************/
4068 /* GDALRasterIOExtraArgSetResampleAlg() */
4069 /************************************************************************/
4070
GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg * psExtraArg,int nXSize,int nYSize,int nBufXSize,int nBufYSize)4071 void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg* psExtraArg,
4072 int nXSize, int nYSize,
4073 int nBufXSize, int nBufYSize)
4074 {
4075 if( (nBufXSize != nXSize || nBufYSize != nYSize) &&
4076 psExtraArg->eResampleAlg == GRIORA_NearestNeighbour )
4077 {
4078 const char* pszResampling = CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
4079 if( pszResampling != nullptr )
4080 {
4081 psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(pszResampling);
4082 }
4083 }
4084 }
4085
4086 /************************************************************************/
4087 /* GDALCanFileAcceptSidecarFile() */
4088 /************************************************************************/
4089
GDALCanFileAcceptSidecarFile(const char * pszFilename)4090 int GDALCanFileAcceptSidecarFile(const char* pszFilename)
4091 {
4092 if( strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?') )
4093 return FALSE;
4094 // Do no attempt reading side-car files on /vsisubfile/ (#6241)
4095 if( strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0 )
4096 return FALSE;
4097 return TRUE;
4098 }
4099
4100 /************************************************************************/
4101 /* GDALCanReliablyUseSiblingFileList() */
4102 /************************************************************************/
4103
4104 /* Try to address https://github.com/OSGeo/gdal/issues/2903 */
4105 /* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
4106 /* (normalization form decomposed). The filesystem takes care of converting */
4107 /* precomposed form as often coming from user interface to this NFD variant */
4108 /* See https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x */
4109 /* And readdir() will return such NFD variant encoding. Consequently comparing */
4110 /* the user filename with ones with readdir() is not reliable */
4111 /* - APFS preserves both case and normalization of the filename on disk in all */
4112 /* variants. In macOS High Sierra, APFS is normalization-insensitive in both */
4113 /* the case-insensitive and case-sensitive variants, using a hash-based native */
4114 /* normalization scheme. APFS preserves the normalization of the filename and */
4115 /* uses hashes of the normalized form of the filename to provide normalization */
4116 /* insensitivity. */
4117 /* From https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html */
4118 /* Issues might still arise if the file has been created using one of the UTF-8 */
4119 /* encoding (likely the decomposed one if using MacOS specific API), but the */
4120 /* string passed to GDAL for opening would be with another one (likely the precomposed one) */
GDALCanReliablyUseSiblingFileList(const char * pszFilename)4121 bool GDALCanReliablyUseSiblingFileList(const char* pszFilename)
4122 {
4123 #ifdef __APPLE__
4124 for( int i = 0; pszFilename[i] != 0; ++i )
4125 {
4126 if( reinterpret_cast<const unsigned char*>(pszFilename)[i] > 127 )
4127 {
4128 // non-ASCII character found
4129
4130 // if this is a network storage, assume no issue
4131 if( strstr(pszFilename, "/vsicurl/") ||
4132 strstr(pszFilename, "/vsis3/") ||
4133 strstr(pszFilename, "/vsicurl/") )
4134 {
4135 return true;
4136 }
4137 return false;
4138 }
4139 }
4140 return true;
4141 #else
4142 (void) pszFilename;
4143 return true;
4144 #endif
4145 }
4146
4147 /************************************************************************/
4148 /* GDALAdjustNoDataCloseToFloatMax() */
4149 /************************************************************************/
4150
GDALAdjustNoDataCloseToFloatMax(double dfVal)4151 double GDALAdjustNoDataCloseToFloatMax(double dfVal)
4152 {
4153 const auto kMaxFloat = std::numeric_limits<float>::max();
4154 if( std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat )
4155 return -kMaxFloat;
4156 if( std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat )
4157 return kMaxFloat;
4158 return dfVal;
4159 }
4160