diff --git a/.gitignore b/.gitignore index 8df9393..e2e0954 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,26 @@ *.slo *.lo *.o +*.obj # Compiled Dynamic libraries -*.so +*.so* +*.dll +*.pdb +*.exe +*.exp +*.ilk # Compiled Static libraries *.lai *.la *.a +*.lib + +mitab/ogrinfo +mitab/tab2tab +mitab/tabdump +mitab/tabindex +mitab/mitabc_test +mitab/ogr2ogr +cpl/cpl_config.h diff --git a/GNUmake.opt b/GNUmake.opt index e1a8e9f..5609b45 100755 --- a/GNUmake.opt +++ b/GNUmake.opt @@ -6,7 +6,7 @@ # BYTE_ORDER_FL = -DCPL_MSB BYTE_ORDER_FL = -DCPL_LSB -OPTFLAGS = -g -Wall -DDEBUG $(BYTE_ORDER_FL) +OPTFLAGS = -g -Wall -DDEBUG $(BYTE_ORDER_FL) -DOGR_ENABLED INCLUDE = -I. -I.. -I../cpl #CXXFLAGS = $(INCLUDE) -fPIC --no-rtti -fno-exceptions $(OPTFLAGS) CFLAGS = $(INCLUDE) -fPIC $(OPTFLAGS) @@ -34,7 +34,7 @@ else CXX = gcc endif endif - CXXFLAGS = $(INCLUDE) -fPIC --no-rtti -fno-exceptions $(OPTFLAGS) + CXXFLAGS = $(INCLUDE) -fPIC --no-rtti $(OPTFLAGS) endif diff --git a/cpl/GNUmakefile b/cpl/GNUmakefile index 2b12d70..cd2469e 100755 --- a/cpl/GNUmakefile +++ b/cpl/GNUmakefile @@ -7,7 +7,10 @@ OBJ = cpl_conv.o cpl_error.o cpl_string.o cpl_vsisimple.o \ cpl_minixml.o cpl_vsil.o cpl_vsi_mem.o \ cpl_vsil_unix_stdio_64.o cpl_multiproc.o cplstring.o \ cpl_getexecpath.o cpl_atomic_ops.o cpl_http.o cpl_strtod.o \ - cpl_vsil_subfile.o cpl_recode_stub.o cpl_vsil_stdout.o + cpl_vsil_subfile.o cpl_recode_stub.o cpl_vsil_stdout.o \ + cplstringlist.o cpl_recode.o cpl_recode_stub.o cpl_vsil_stdin.o cpl_vsil_stdout.o \ + cpl_vsil_sparsefile.o cpl_vsil_tar.o cpl_vsil_cache.o cpl_vsil_abstract_archive.o cpl_time.o \ + cpl_hash_set.o cpl_list.o cpl_progress.o cpl_spawn.o cpl_virtualmem.o LIB = cpl.a diff --git a/cpl/cpl_atomic_ops.cpp b/cpl/cpl_atomic_ops.cpp index 6cf3994..fee48b5 100644 --- a/cpl/cpl_atomic_ops.cpp +++ b/cpl/cpl_atomic_ops.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_atomic_ops.cpp,v 1.1 2010-07-08 19:29:35 aboudreault Exp $ + * $Id: cpl_atomic_ops.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Name: cpl_atomic_ops.cpp * Project: CPL - Common Portability Library @@ -7,7 +7,7 @@ * Author: Even Rouault, * ********************************************************************** - * Copyright (c) 2009, Even Rouault, + * Copyright (c) 2009-2010, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -110,3 +110,17 @@ int CPLAtomicAdd(volatile int* ptr, int increment) } #endif + +#ifndef HAS_CPL_INLINE + +int CPLAtomicInc(volatile int* ptr) +{ + return CPLAtomicAdd(ptr, 1); +} + +int CPLAtomicDec(volatile int* ptr) +{ + return CPLAtomicAdd(ptr, -1); +} + +#endif diff --git a/cpl/cpl_atomic_ops.h b/cpl/cpl_atomic_ops.h index 14806fd..29416be 100644 --- a/cpl/cpl_atomic_ops.h +++ b/cpl/cpl_atomic_ops.h @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_atomic_ops.h,v 1.1 2010-07-08 19:29:35 aboudreault Exp $ + * $Id: cpl_atomic_ops.h 27044 2014-03-16 23:41:27Z rouault $ * * Name: cpl_atomic_ops.h * Project: CPL - Common Portability Library @@ -7,7 +7,7 @@ * Author: Even Rouault, * ********************************************************************** - * Copyright (c) 2009, Even Rouault, + * Copyright (c) 2009-2010, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -69,10 +69,14 @@ int CPL_DLL CPLAtomicAdd(volatile int* ptr, int increment); * @param ptr a pointer to an integer to increment * @return the pointed value AFTER the opeation: *ptr + 1 */ +#ifdef HAS_CPL_INLINE CPL_INLINE int CPLAtomicInc(volatile int* ptr) { return CPLAtomicAdd(ptr, 1); } +#else +int CPL_DLL CPLAtomicInc(volatile int* ptr); +#endif /** Decrement of 1 the pointed integer in a thread and SMP-safe way * and return the resulting value of the operation. @@ -83,10 +87,14 @@ CPL_INLINE int CPLAtomicInc(volatile int* ptr) * @param ptr a pointer to an integer to decrement * @return the pointed value AFTER the opeation: *ptr - 1 */ +#ifdef HAS_CPL_INLINE CPL_INLINE int CPLAtomicDec(volatile int* ptr) { return CPLAtomicAdd(ptr, -1); } +#else +int CPL_DLL CPLAtomicDec(volatile int* ptr); +#endif CPL_C_END diff --git a/cpl/cpl_config.h.in b/cpl/cpl_config.h.in index d84e1dc..352e8ca 100755 --- a/cpl/cpl_config.h.in +++ b/cpl/cpl_config.h.in @@ -1,51 +1,224 @@ -/* port/cpl_config.h.in. Generated automatically from configure.in by autoheader. */ +/* port/cpl_config.h. Generated from cpl_config.h.in by configure. */ +/* port/cpl_config.h.in. Generated from configure.in by autoheader. */ -/* Define if you don't have vprintf but do have _doprnt. */ -#undef HAVE_DOPRNT +/* Define if you want to use pthreads based multiprocessing support */ +#define CPL_MULTIPROC_PTHREAD 1 -/* Define if you have the vprintf function. */ -#undef HAVE_VPRINTF +/* Define to 1 if you have the `PTHREAD_MUTEX_RECURSIVE' constant. */ +#define HAVE_PTHREAD_MUTEX_RECURSIVE 1 -/* Define if you have the vsnprintf function. */ -#undef HAVE_VSNPRINTF +/* Define to 1 if you have the 5 args `mremap' function. */ +#define HAVE_5ARGS_MREMAP 1 -/* Define if you have the ANSI C header files. */ -#undef STDC_HEADERS +/* --prefix directory for GDAL install */ +#define GDAL_PREFIX "/home/even/gdal/svn/trunk/dummyinstall" + +/* Define to 1 if you have the header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the `atoll' function. */ +#define HAVE_ATOLL 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CSF_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DBMALLOC_H */ + +/* Define to 1 if you have the declaration of `strtof', and to 0 if you don't. + */ +#define HAVE_DECL_STRTOF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DIRECT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ /* Define to 1 if you have the header file. */ #define HAVE_ERRNO_H 1 -/* Define if you have the header file. */ -#undef HAVE_FCNTL_H +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define if you have the iconv() function and it works. */ +#undef HAVE_ICONV + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#define HAVE_IEEEFP 1 + +/* Define to 1 if the system has the type `int16'. */ +/* #undef HAVE_INT16 */ + +/* Define to 1 if the system has the type `int32'. */ +/* #undef HAVE_INT32 */ + +/* Define to 1 if the system has the type `int8'. */ +/* #undef HAVE_INT8 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_JPEGLIB_H 1 + +/* Define to 1 if you have the `dl' library (-ldl). */ +#define HAVE_LIBDL 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `pq' library (-lpq). */ +#define HAVE_LIBPQ 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +#define HAVE_LIBRT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1, if your compiler supports long long data type */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PNG_H 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtof' function. */ +#define HAVE_STRTOF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_VALUES_H 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `readlink' function. */ +#define HAVE_READLINK 1 + +/* Define to 1 if you have the `posix_spawnp' function. */ +#define HAVE_POSIX_SPAWNP 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the `lstat' function. */ +#define HAVE_LSTAT 1 + +/* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ +#define HOST_FILLORDER FILLORDER_LSB2MSB + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST + +/* For .cpp files, define as const if the declaration of iconv() needs const. */ +#define ICONV_CPP_CONST + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define for Mac OSX Framework build */ +/* #undef MACOSX_FRAMEWORK */ + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of `long', as computed by sizeof. */ +/* #undef SIZEOF_LONG */ + +/* The size of `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 8 + +/* The size of `void*', as computed by sizeof. */ +#define SIZEOF_VOIDP 8 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you have fseek64, ftell64 */ +#define UNIX_STDIO_64 1 + +/* Define to 1 if you want to use the -fvisibility GCC flag */ +/* #undef USE_GCC_VISIBILITY_FLAG */ -/* Define if you have the header file. */ -#undef HAVE_UNISTD_H +/* Define to 1 if GCC atomic builtins are available */ +#define HAVE_GCC_ATOMIC_BUILTINS 1 -/* Define if you have the header file. */ -#undef HAVE_STDINT_H +/* Define to name of 64bit fopen function */ +#define VSI_FOPEN64 fopen64 -#undef HAVE_LIBDL +/* Define to name of 64bit ftruncate function */ +#define VSI_FTRUNCATE64 ftruncate64 -#undef HAVE_DLFCN_H -#undef HAVE_DBMALLOC_H -#undef HAVE_LIBDBMALLOC -#undef WORDS_BIGENDIAN +/* Define to name of 64bit fseek func */ +#define VSI_FSEEK64 fseeko64 -/* Define if you have ftell64 and fseek64 */ -#undef UNIX_STDIO_64 +/* Define to name of 64bit ftell func */ +#define VSI_FTELL64 ftello64 -/* What 64bit stdio api entry points to use */ -#undef VSI_FTELL64 -#undef VSI_FSEEK64 +/* Define to 1, if you have 64 bit STDIO API */ +#define VSI_LARGE_API_SUPPORTED 1 -/* Define if you have 64 bit long long type */ -#undef HAVE_LONG_LONG +/* Define to 1, if you have LARGEFILE64_SOURCE */ +/* #undef VSI_NEED_LARGEFILE64_SOURCE */ -/* Define if large file api should be enabled, normally with UNIX_STDIO_64 */ -#undef VSI_LARGE_API_SUPPORTED +/* Define to name of 64bit stat function */ +#define VSI_STAT64 stat64 -/* Define if _LARGEFILE64_SOURCE needs to be defined. */ -#undef VSI_NEED_LARGEFILE64_SOURCE +/* Define to name of 64bit stat structure */ +#define VSI_STAT64_T stat64 +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ +/* Define to 1 if you have the `getaddrinfo' function. */ +#define HAVE_GETADDRINFO 1 +/* Use this file to override settings in instances where you're doing FAT compiles + on Apple. It is currently off by default because it doesn't seem to work with + newish ( XCode >= 3/28/11) XCodes */ +/* #include "cpl_config_extras.h" */ diff --git a/cpl/cpl_config.h.vc b/cpl/cpl_config.h.vc index 9dbf7cf..626798e 100755 --- a/cpl/cpl_config.h.vc +++ b/cpl/cpl_config.h.vc @@ -3,20 +3,30 @@ #undef HAVE_DOPRNT /* Define if you have the vprintf function. */ -#define HAVE_VPRINTF -#define HAVE_VSNPRINTF -#define vsnprintf _vsnprintf - -#define HAVE_SNPRINTF +#define HAVE_VPRINTF 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_SNPRINTF 1 +#if defined(_MSC_VER) && (_MSC_VER < 1500) +# define vsnprintf _vsnprintf +#endif #define snprintf _snprintf +#define HAVE_GETCWD 1 +/* gmt_notunix.h from GMT project also redefines getcwd. See #3138 */ +#ifndef getcwd +#define getcwd _getcwd +#endif + /* Define if you have the ANSI C header files. */ #ifndef STDC_HEADERS -# define STDC_HEADERS +# define STDC_HEADERS 1 #endif -/* Define if you have the header file. */ -#undef HAVE_FCNTL_H +/* Define to 1 if you have the header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 /* Define if you have the header file. */ #undef HAVE_UNISTD_H @@ -24,8 +34,26 @@ /* Define if you have the header file. */ #undef HAVE_STDINT_H +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + #undef HAVE_LIBDL +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +#define HAVE_FLOAT_H 1 + +#define HAVE_ERRNO_H 1 + +#define HAVE_SEARCH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + #undef HAVE_DLFCN_H #undef HAVE_DBMALLOC_H #undef HAVE_LIBDBMALLOC @@ -35,3 +63,54 @@ /* Define to 1 if you have the header file. */ #define HAVE_ERRNO_H 1 + +/* The size of `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The size of `void*', as computed by sizeof. */ +#ifdef _WIN64 +# define SIZEOF_VOIDP 8 +#else +# define SIZEOF_VOIDP 4 +#endif + +/* Set the native cpu bit order */ +#define HOST_FILLORDER FILLORDER_LSB2MSB + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#define HAVE_IEEEFP 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +# ifndef inline +# define inline __inline +# endif +#endif + +#define lfind _lfind + +#if defined(_MSC_VER) && (_MSC_VER < 1310) +# define VSI_STAT64 _stat +# define VSI_STAT64_T _stat +#else +# define VSI_STAT64 _stat64 +# define VSI_STAT64_T __stat64 +#endif + +/* VC6 doesn't known intptr_t */ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) + typedef int intptr_t; +#endif + +#pragma warning(disable: 4786) + +/* #define CPL_DISABLE_DLL */ + diff --git a/cpl/cpl_config_extras.h b/cpl/cpl_config_extras.h new file mode 100644 index 0000000..654f2fc --- /dev/null +++ b/cpl/cpl_config_extras.h @@ -0,0 +1,40 @@ + +#ifndef INCLUDED_CPL_CONFIG_EXTRAS +#define INCLUDED_CPL_CONFIG_EXTRAS + +#if defined(__APPLE__) + +#ifdef __BIG_ENDIAN__ + #define HOST_FILLORDER FILLORDER_MSB2LSB +#else + #define HOST_FILLORDER FILLORDER_LSB2MSB +#endif + + +#ifdef __LP64__ + #define SIZEOF_UNSIGNED_LONG 8 +#else + #define SIZEOF_UNSIGNED_LONG 4 +#endif + +#ifdef __LP64__ + #define SIZEOF_VOIDP 8 +#else + #define SIZEOF_VOIDP 4 +#endif + +#ifdef __BIG_ENDIAN__ + #define WORDS_BIGENDIAN 1 +#else + #undef WORDS_BIGENDIAN +#endif + +#undef VSI_STAT64 +#undef VSI_STAT64_T + +#define VSI_STAT64 stat +#define VSI_STAT64_T stat + +#endif // APPLE + +#endif //INCLUDED_CPL_CONFIG_EXTRAS diff --git a/cpl/cpl_conv.cpp b/cpl/cpl_conv.cpp index 3403097..446e8d5 100644 --- a/cpl/cpl_conv.cpp +++ b/cpl/cpl_conv.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_conv.cpp 18060 2009-11-21 20:26:25Z rouault $ + * $Id: cpl_conv.cpp 27121 2014-04-03 22:08:55Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Convenience functions. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -34,7 +35,7 @@ #include "cpl_vsi.h" #include "cpl_multiproc.h" -CPL_CVSID("$Id: cpl_conv.cpp 18060 2009-11-21 20:26:25Z rouault $"); +CPL_CVSID("$Id: cpl_conv.cpp 27121 2014-04-03 22:08:55Z rouault $"); #if defined(WIN32CE) # include "cpl_wince.h" @@ -43,10 +44,23 @@ CPL_CVSID("$Id: cpl_conv.cpp 18060 2009-11-21 20:26:25Z rouault $"); static void *hConfigMutex = NULL; static volatile char **papszConfigOptions = NULL; +/* Used by CPLOpenShared() and friends */ static void *hSharedFileMutex = NULL; static volatile int nSharedFileCount = 0; static volatile CPLSharedFileInfo *pasSharedFileList = NULL; +/* Used by CPLsetlocale() */ +static void *hSetLocaleMutex = NULL; + +/* Note: ideally this should be added in CPLSharedFileInfo* */ +/* but CPLSharedFileInfo is exposed in the API, hence that trick */ +/* to hide this detail */ +typedef struct +{ + GIntBig nPID; // pid of opening thread +} CPLSharedFileInfoExtra; + +static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = NULL; /************************************************************************/ /* CPLCalloc() */ @@ -77,14 +91,8 @@ void *CPLCalloc( size_t nCount, size_t nSize ) if( nSize * nCount == 0 ) return NULL; - pReturn = VSICalloc( nCount, nSize ); - if( pReturn == NULL ) - { - CPLError( CE_Fatal, CPLE_OutOfMemory, - "CPLCalloc(): Out of memory allocating %ld bytes.\n", - (long) (nSize * nCount) ); - } - + pReturn = CPLMalloc( nCount * nSize ); + memset( pReturn, 0, nCount * nSize ); return pReturn; } @@ -117,7 +125,7 @@ void *CPLMalloc( size_t nSize ) if( nSize == 0 ) return NULL; - if( nSize < 0 ) + if( long(nSize) < 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "CPLMalloc(%ld): Silly size requested.\n", @@ -128,9 +136,19 @@ void *CPLMalloc( size_t nSize ) pReturn = VSIMalloc( nSize ); if( pReturn == NULL ) { - CPLError( CE_Fatal, CPLE_OutOfMemory, - "CPLMalloc(): Out of memory allocating %ld bytes.\n", - (long) nSize ); + if( nSize > 0 && nSize < 2000 ) + { + char szSmallMsg[60]; + + sprintf( szSmallMsg, + "CPLMalloc(): Out of memory allocating %ld bytes.", + (long) nSize ); + CPLEmergencyError( szSmallMsg ); + } + else + CPLError( CE_Fatal, CPLE_OutOfMemory, + "CPLMalloc(): Out of memory allocating %ld bytes.\n", + (long) nSize ); } return pReturn; @@ -171,7 +189,7 @@ void * CPLRealloc( void * pData, size_t nNewSize ) return NULL; } - if( nNewSize < 0 ) + if( long(nNewSize) < 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "CPLRealloc(%ld): Silly size requested.\n", @@ -186,9 +204,19 @@ void * CPLRealloc( void * pData, size_t nNewSize ) if( pReturn == NULL ) { - CPLError( CE_Fatal, CPLE_OutOfMemory, - "CPLRealloc(): Out of memory allocating %ld bytes.\n", - (long)nNewSize ); + if( nNewSize > 0 && nNewSize < 2000 ) + { + char szSmallMsg[60]; + + sprintf( szSmallMsg, + "CPLRealloc(): Out of memory allocating %ld bytes.", + (long) nNewSize ); + CPLEmergencyError( szSmallMsg ); + } + else + CPLError( CE_Fatal, CPLE_OutOfMemory, + "CPLRealloc(): Out of memory allocating %ld bytes.\n", + (long) nNewSize ); } return pReturn; @@ -224,8 +252,7 @@ char *CPLStrdup( const char * pszString ) if( pszString == NULL ) pszString = ""; - pszReturn = VSIStrdup( pszString ); - + pszReturn = (char *) CPLMalloc(strlen(pszString)+1); if( pszReturn == NULL ) { CPLError( CE_Fatal, CPLE_OutOfMemory, @@ -233,7 +260,8 @@ char *CPLStrdup( const char * pszString ) (long) strlen(pszString) ); } - + + strcpy( pszReturn, pszString ); return( pszReturn ); } @@ -394,6 +422,9 @@ char *CPLFGets( char *pszBuffer, int nBufferSize, FILE * fp ) /* Fetch readline buffer, and ensure it is the desired size, */ /* reallocating if needed. Manages TLS (thread local storage) */ /* issues for the buffer. */ +/* We use a special trick to track the actual size of the buffer */ +/* The first 4 bytes are reserved to store it as a int, hence the */ +/* -4 / +4 hacks with the size and pointer. */ /************************************************************************/ static char *CPLReadLineBuffer( int nRequiredSize ) @@ -427,15 +458,26 @@ static char *CPLReadLineBuffer( int nRequiredSize ) /* -------------------------------------------------------------------- */ /* If it is too small, grow it bigger. */ /* -------------------------------------------------------------------- */ - if( (int) *pnAlloc < nRequiredSize+1 ) + if( ((int) *pnAlloc) -1 < nRequiredSize ) { int nNewSize = nRequiredSize + 4 + 500; + if (nNewSize <= 0) + { + VSIFree( pnAlloc ); + CPLSetTLS( CTLS_RLBUFFERINFO, NULL, FALSE ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "CPLReadLineBuffer(): Trying to allocate more than 2 GB." ); + return NULL; + } GUInt32* pnAllocNew = (GUInt32 *) VSIRealloc(pnAlloc,nNewSize); if( pnAllocNew == NULL ) { VSIFree( pnAlloc ); CPLSetTLS( CTLS_RLBUFFERINFO, NULL, FALSE ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "CPLReadLineBuffer(): Out of memory allocating %ld bytes.", + (long) nNewSize ); return NULL; } pnAlloc = pnAllocNew; @@ -500,6 +542,8 @@ const char *CPLReadLine( FILE * fp ) /* of read line if we can't reallocate it big enough (for */ /* instance for a _very large_ file with no newlines). */ /* -------------------------------------------------------------------- */ + if( nReadSoFar > 100 * 1024 * 1024 ) + return NULL; /* it is dubious that we need to read a line longer than 100 MB ! */ pszRLBuffer = CPLReadLineBuffer( nReadSoFar + 129 ); if( pszRLBuffer == NULL ) return NULL; @@ -536,7 +580,7 @@ const char *CPLReadLine( FILE * fp ) * from the file or NULL if the end of file was encountered. */ -const char *CPLReadLineL( FILE * fp ) +const char *CPLReadLineL( VSILFILE * fp ) { return CPLReadLine2L( fp, -1, NULL ); } @@ -561,9 +605,11 @@ const char *CPLReadLineL( FILE * fp ) * @since GDAL 1.7.0 */ -const char *CPLReadLine2L( FILE * fp, int nMaxCars, char** papszOptions ) +const char *CPLReadLine2L( VSILFILE * fp, int nMaxCars, char** papszOptions ) { + (void) papszOptions; + /* -------------------------------------------------------------------- */ /* Cleanup case. */ /* -------------------------------------------------------------------- */ @@ -589,7 +635,17 @@ const char *CPLReadLine2L( FILE * fp, int nMaxCars, char** papszOptions ) /* -------------------------------------------------------------------- */ /* Read a chunk from the input file. */ /* -------------------------------------------------------------------- */ + if ( nBufLength > INT_MAX - (int)nChunkSize - 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too big line : more than 2 billion characters!." ); + CPLReadLineBuffer( -1 ); + return NULL; + } + pszRLBuffer = CPLReadLineBuffer( nBufLength + nChunkSize + 1 ); + if( pszRLBuffer == NULL ) + return NULL; if( nChunkBytesRead == nChunkBytesConsumed + 1 ) { @@ -917,6 +973,12 @@ void *CPLScanPointer( const char *pszString, int nMaxLength ) sscanf( szTemp+2, "%p", &pResult ); #else sscanf( szTemp, "%p", &pResult ); + + /* Solaris actually behaves like MSVCRT... */ + if (pResult == NULL) + { + sscanf( szTemp+2, "%p", &pResult ); + } #endif } @@ -1237,9 +1299,9 @@ int CPLPrintDouble( char *pszBuffer, const char *pszFormat, if ( pszLocale || EQUAL( pszLocale, "" ) ) { // Save the current locale - pszCurLocale = setlocale(LC_ALL, NULL ); + pszCurLocale = CPLsetlocale(LC_ALL, NULL ); // Set locale to the specified value - setlocale( LC_ALL, pszLocale ); + CPLsetlocale( LC_ALL, pszLocale ); } #else (void) pszLocale; @@ -1261,7 +1323,7 @@ int CPLPrintDouble( char *pszBuffer, const char *pszFormat, #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) // Restore stored locale back if ( pszCurLocale ) - setlocale( LC_ALL, pszCurLocale ); + CPLsetlocale( LC_ALL, pszCurLocale ); #endif return CPLPrintString( pszBuffer, szTemp, 64 ); @@ -1321,9 +1383,9 @@ int CPLPrintTime( char *pszBuffer, int nMaxLen, const char *pszFormat, if ( pszLocale || EQUAL( pszLocale, "" ) ) { // Save the current locale - pszCurLocale = setlocale(LC_ALL, NULL ); + pszCurLocale = CPLsetlocale(LC_ALL, NULL ); // Set locale to the specified value - setlocale( LC_ALL, pszLocale ); + CPLsetlocale( LC_ALL, pszLocale ); } #else (void) pszLocale; @@ -1335,7 +1397,7 @@ int CPLPrintTime( char *pszBuffer, int nMaxLen, const char *pszFormat, #if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) // Restore stored locale back if ( pszCurLocale ) - setlocale( LC_ALL, pszCurLocale ); + CPLsetlocale( LC_ALL, pszCurLocale ); #endif nChars = CPLPrintString( pszBuffer, pszTemp, nMaxLen ); @@ -1383,6 +1445,69 @@ void CPLVerifyConfiguration() "CPLVerifyConfiguration(): byte order set wrong.\n" ); } +/* Uncomment to get list of options that have been fetched and set */ +//#define DEBUG_CONFIG_OPTIONS + +#ifdef DEBUG_CONFIG_OPTIONS + +#include +#include "cpl_multiproc.h" + +static void* hRegisterConfigurationOptionMutex = 0; +static std::set* paoGetKeys = NULL; +static std::set* paoSetKeys = NULL; + +/************************************************************************/ +/* CPLShowAccessedOptions() */ +/************************************************************************/ + +static void CPLShowAccessedOptions() +{ + std::set::iterator aoIter; + + printf("Configuration options accessed in reading : "), + aoIter = paoGetKeys->begin(); + while(aoIter != paoGetKeys->end()) + { + printf("%s, ", (*aoIter).c_str()); + aoIter ++; + } + printf("\n"); + + printf("Configuration options accessed in writing : "), + aoIter = paoSetKeys->begin(); + while(aoIter != paoSetKeys->end()) + { + printf("%s, ", (*aoIter).c_str()); + aoIter ++; + } + printf("\n"); + + delete paoGetKeys; + delete paoSetKeys; + paoGetKeys = paoSetKeys = NULL; +} + +/************************************************************************/ +/* CPLAccessConfigOption() */ +/************************************************************************/ + +static void CPLAccessConfigOption(const char* pszKey, int bGet) +{ + CPLMutexHolderD(&hRegisterConfigurationOptionMutex); + if (paoGetKeys == NULL) + { + paoGetKeys = new std::set; + paoSetKeys = new std::set; + atexit(CPLShowAccessedOptions); + } + if (bGet) + paoGetKeys->insert(pszKey); + else + paoSetKeys->insert(pszKey); +} +#endif + /************************************************************************/ /* CPLGetConfigOption() */ /************************************************************************/ @@ -1394,16 +1519,38 @@ void CPLVerifyConfiguration() * If the given option was no defined with CPLSetConfigOption(), it tries to find * it in environment variables. * + * Note: the string returned by CPLGetConfigOption() might be short-lived, and in + * particular it will become invalid after a call to CPLSetConfigOption() with the + * same key. + * + * To override temporary a potentially existing option with a new value, you can + * use the following snippet : + *
+  *     // backup old value
+  *     const char* pszOldValTmp = CPLGetConfigOption(pszKey, NULL);
+  *     char* pszOldVal = pszOldValTmp ? CPLStrdup(pszOldValTmp) : NULL;
+  *     // override with new value
+  *     CPLSetConfigOption(pszKey, pszNewVal);
+  *     // do something usefull
+  *     // restore old value
+  *     CPLSetConfigOption(pszKey, pszOldVal);
+  *     CPLFree(pszOldVal);
+  * 
+ * * @param pszKey the key of the option to retrieve * @param pszDefault a default value if the key does not match existing defined options (may be NULL) * @return the value associated to the key, or the default value if not found * - * @see CPLSetConfigOption() + * @see CPLSetConfigOption(), http://trac.osgeo.org/gdal/wiki/ConfigOptions */ const char * CPL_STDCALL CPLGetConfigOption( const char *pszKey, const char *pszDefault ) { +#ifdef DEBUG_CONFIG_OPTIONS + CPLAccessConfigOption(pszKey, TRUE); +#endif + const char *pszResult = NULL; char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); @@ -1449,13 +1596,22 @@ CPLGetConfigOption( const char *pszKey, const char *pszDefault ) * with the with '--config KEY VALUE'. For example, * ogrinfo --config CPL_DEBUG ON ~/data/test/point.shp * + * This function can also be used to clear a setting by passing NULL as the + * value (note: passing NULL will not unset an existing environment variable; + * it will just unset a value previously set by CPLSetConfigOption()). + * * @param pszKey the key of the option - * @param pszValue the value of the option + * @param pszValue the value of the option, or NULL to clear a setting. + * + * @see http://trac.osgeo.org/gdal/wiki/ConfigOptions */ void CPL_STDCALL CPLSetConfigOption( const char *pszKey, const char *pszValue ) { +#ifdef DEBUG_CONFIG_OPTIONS + CPLAccessConfigOption(pszKey, FALSE); +#endif CPLMutexHolderD( &hConfigMutex ); papszConfigOptions = (volatile char **) @@ -1476,20 +1632,28 @@ CPLSetConfigOption( const char *pszKey, const char *pszValue ) * current thread, as opposed to CPLSetConfigOption() which sets an option * that applies on all threads. * + * This function can also be used to clear a setting by passing NULL as the + * value (note: passing NULL will not unset an existing environment variable; + * it will just unset a value previously set by CPLSetThreadLocalConfigOption()). + * * @param pszKey the key of the option - * @param pszValue the value of the option + * @param pszValue the value of the option, or NULL to clear a setting. */ void CPL_STDCALL CPLSetThreadLocalConfigOption( const char *pszKey, const char *pszValue ) { +#ifdef DEBUG_CONFIG_OPTIONS + CPLAccessConfigOption(pszKey, FALSE); +#endif + char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); papszTLConfigOptions = CSLSetNameValue( papszTLConfigOptions, pszKey, pszValue ); - CPLSetTLS( CTLS_CONFIGOPTIONS, papszTLConfigOptions, FALSE ); + CPLSetTLSWithFreeFunc( CTLS_CONFIGOPTIONS, papszTLConfigOptions, (CPLTLSFreeFunc)CSLDestroy ); } /************************************************************************/ @@ -1499,17 +1663,21 @@ CPLSetThreadLocalConfigOption( const char *pszKey, const char *pszValue ) void CPL_STDCALL CPLFreeConfig() { - CPLMutexHolderD( &hConfigMutex ); - - CSLDestroy( (char **) papszConfigOptions); - papszConfigOptions = NULL; - - char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); - if( papszTLConfigOptions != NULL ) { - CSLDestroy( papszTLConfigOptions ); - CPLSetTLS( CTLS_CONFIGOPTIONS, NULL, FALSE ); + CPLMutexHolderD( &hConfigMutex ); + + CSLDestroy( (char **) papszConfigOptions); + papszConfigOptions = NULL; + + char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); + if( papszTLConfigOptions != NULL ) + { + CSLDestroy( papszTLConfigOptions ); + CPLSetTLS( CTLS_CONFIGOPTIONS, NULL, FALSE ); + } } + CPLDestroyMutex( hConfigMutex ); + hConfigMutex = NULL; } /************************************************************************/ @@ -1623,7 +1791,7 @@ double CPLDMSToDec( const char *is ) ++s; } /* postfix sign */ - if (*s && (p = (char *) strchr(sym, *s))) { + if (*s && ((p = (char *) strchr(sym, *s))) != NULL) { sign = (p - sym) >= 4 ? '-' : '+'; ++s; } @@ -1678,7 +1846,7 @@ const char *CPLDecToDMS( double dfAngle, const char * pszAxis, else pszHemisphere = "N"; - sprintf( szFormat, "%%3dd%%2d\'%%.%df\"%s", nPrecision, pszHemisphere ); + sprintf( szFormat, "%%3dd%%2d\'%%%d.%df\"%s", nPrecision+3, nPrecision, pszHemisphere ); sprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds ); return( szBuffer ); @@ -1795,7 +1963,7 @@ void CPL_DLL CPLStringToComplex( const char *pszString, while( *pszString == ' ' ) pszString++; - *pdfReal = atof(pszString); + *pdfReal = CPLAtof(pszString); *pdfImag = 0.0; for( i = 0; pszString[i] != '\0' && pszString[i] != ' ' && i < 100; i++ ) @@ -1810,7 +1978,7 @@ void CPL_DLL CPLStringToComplex( const char *pszString, if( iPlus > -1 && iImagEnd > -1 && iPlus < iImagEnd ) { - *pdfImag = atof(pszString + iPlus); + *pdfImag = CPLAtof(pszString + iPlus); } return; @@ -1852,6 +2020,7 @@ FILE *CPLOpenShared( const char *pszFilename, const char *pszAccess, int i; int bReuse; CPLMutexHolderD( &hSharedFileMutex ); + GIntBig nPID = CPLGetPID(); /* -------------------------------------------------------------------- */ /* Is there an existing file we can use? */ @@ -1862,7 +2031,8 @@ FILE *CPLOpenShared( const char *pszFilename, const char *pszAccess, { if( strcmp(pasSharedFileList[i].pszFilename,pszFilename) == 0 && !bLarge == !pasSharedFileList[i].bLarge - && EQUAL(pasSharedFileList[i].pszAccess,pszAccess) ) + && EQUAL(pasSharedFileList[i].pszAccess,pszAccess) + && nPID == pasSharedFileListExtra[i].nPID) { pasSharedFileList[i].nRefCount++; return pasSharedFileList[i].fp; @@ -1875,7 +2045,7 @@ FILE *CPLOpenShared( const char *pszFilename, const char *pszAccess, FILE *fp; if( bLarge ) - fp = VSIFOpenL( pszFilename, pszAccess ); + fp = (FILE*) VSIFOpenL( pszFilename, pszAccess ); else fp = VSIFOpen( pszFilename, pszAccess ); @@ -1890,12 +2060,16 @@ FILE *CPLOpenShared( const char *pszFilename, const char *pszAccess, pasSharedFileList = (CPLSharedFileInfo *) CPLRealloc( (void *) pasSharedFileList, sizeof(CPLSharedFileInfo) * nSharedFileCount ); + pasSharedFileListExtra = (CPLSharedFileInfoExtra *) + CPLRealloc( (void *) pasSharedFileListExtra, + sizeof(CPLSharedFileInfoExtra) * nSharedFileCount ); pasSharedFileList[nSharedFileCount-1].fp = fp; pasSharedFileList[nSharedFileCount-1].nRefCount = 1; pasSharedFileList[nSharedFileCount-1].bLarge = bLarge; pasSharedFileList[nSharedFileCount-1].pszFilename =CPLStrdup(pszFilename); pasSharedFileList[nSharedFileCount-1].pszAccess = CPLStrdup(pszAccess); + pasSharedFileListExtra[nSharedFileCount-1].nPID = nPID; return fp; } @@ -1943,22 +2117,41 @@ void CPLCloseShared( FILE * fp ) /* Close the file, and remove the information. */ /* -------------------------------------------------------------------- */ if( pasSharedFileList[i].bLarge ) - VSIFCloseL( pasSharedFileList[i].fp ); + VSIFCloseL( (VSILFILE*) pasSharedFileList[i].fp ); else VSIFClose( pasSharedFileList[i].fp ); CPLFree( pasSharedFileList[i].pszFilename ); CPLFree( pasSharedFileList[i].pszAccess ); -// pasSharedFileList[i] = pasSharedFileList[--nSharedFileCount]; - memcpy( (void *) (pasSharedFileList + i), - (void *) (pasSharedFileList + --nSharedFileCount), - sizeof(CPLSharedFileInfo) ); + nSharedFileCount --; + memmove( (void *) (pasSharedFileList + i), + (void *) (pasSharedFileList + nSharedFileCount), + sizeof(CPLSharedFileInfo) ); + memmove( (void *) (pasSharedFileListExtra + i), + (void *) (pasSharedFileListExtra + nSharedFileCount), + sizeof(CPLSharedFileInfoExtra) ); if( nSharedFileCount == 0 ) { CPLFree( (void *) pasSharedFileList ); pasSharedFileList = NULL; + CPLFree( (void *) pasSharedFileListExtra ); + pasSharedFileListExtra = NULL; + } +} + + +/************************************************************************/ +/* CPLCleanupSharedFileMutex() */ +/************************************************************************/ + +void CPLCleanupSharedFileMutex() +{ + if( hSharedFileMutex != NULL ) + { + CPLDestroyMutex(hSharedFileMutex); + hSharedFileMutex = NULL; } } @@ -2042,9 +2235,9 @@ int CPLUnlinkTree( const char *pszPath ) /* -------------------------------------------------------------------- */ /* First, ensure there isn't any such file yet. */ /* -------------------------------------------------------------------- */ - VSIStatBuf sStatBuf; + VSIStatBufL sStatBuf; - if( VSIStat( pszPath, &sStatBuf ) != 0 ) + if( VSIStatL( pszPath, &sStatBuf ) != 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "It seems no file system object called '%s' exists.", @@ -2130,7 +2323,7 @@ int CPLUnlinkTree( const char *pszPath ) int CPLCopyFile( const char *pszNewPath, const char *pszOldPath ) { - FILE *fpOld, *fpNew; + VSILFILE *fpOld, *fpNew; GByte *pabyBuffer; size_t nBufferSize; size_t nBytesRead; @@ -2161,7 +2354,7 @@ int CPLCopyFile( const char *pszNewPath, const char *pszOldPath ) /* -------------------------------------------------------------------- */ do { nBytesRead = VSIFReadL( pabyBuffer, 1, nBufferSize, fpOld ); - if( nBytesRead < 0 ) + if( long(nBytesRead) < 0 ) nRet = -1; if( nRet == 0 @@ -2187,7 +2380,7 @@ int CPLCopyFile( const char *pszNewPath, const char *pszOldPath ) int CPLMoveFile( const char *pszNewPath, const char *pszOldPath ) { - if( VSIRename( pszNewPath, pszOldPath ) == 0 ) + if( VSIRename( pszOldPath, pszNewPath ) == 0 ) return 0; int nRet = CPLCopyFile( pszNewPath, pszOldPath ); @@ -2210,14 +2403,24 @@ int CPLMoveFile( const char *pszNewPath, const char *pszOldPath ) /* CPLLocaleC() */ /************************************************************************/ -CPLLocaleC::CPLLocaleC() : pszOldLocale(CPLStrdup(setlocale(LC_NUMERIC,NULL))) +CPLLocaleC::CPLLocaleC() { - if( setlocale(LC_NUMERIC,"C") == NULL ) + if( CSLTestBoolean(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC","NO")) ) { - CPLFree( pszOldLocale ); pszOldLocale = NULL; } + else + { + pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC,NULL)); + if( EQUAL(pszOldLocale,"C") + || EQUAL(pszOldLocale,"POSIX") + || CPLsetlocale(LC_NUMERIC,"C") == NULL ) + { + CPLFree( pszOldLocale ); + pszOldLocale = NULL; + } + } } /************************************************************************/ @@ -2229,11 +2432,46 @@ CPLLocaleC::~CPLLocaleC() { if( pszOldLocale != NULL ) { - setlocale( LC_NUMERIC, pszOldLocale ); + CPLsetlocale( LC_NUMERIC, pszOldLocale ); CPLFree( pszOldLocale ); } } + +/************************************************************************/ +/* CPLsetlocale() */ +/************************************************************************/ + +/** + * Prevents parallel executions of setlocale(). + * + * Calling setlocale() concurrently from two or more threads is a + * potential data race. A mutex is used to provide a critical region so + * that only one thread at a time can be executing setlocale(). + * + * @param category See your compiler's documentation on setlocale. + * @param locale See your compiler's documentation on setlocale. + * + * @return See your compiler's documentation on setlocale. + */ +char* CPLsetlocale (int category, const char* locale) +{ + CPLMutexHolder oHolder(&hSetLocaleMutex); + return setlocale(category, locale); +} + + +/************************************************************************/ +/* CPLCleanupSetlocaleMutex() */ +/************************************************************************/ + +void CPLCleanupSetlocaleMutex(void) +{ + if( hSetLocaleMutex != NULL ) + CPLDestroyMutex(hSetLocaleMutex); + hSetLocaleMutex = NULL; +} + /************************************************************************/ /* CPLCheckForFile() */ /************************************************************************/ @@ -2293,3 +2531,62 @@ int CPLCheckForFile( char *pszFilename, char **papszSiblingFiles ) return FALSE; } + +/************************************************************************/ +/* Stub implementation of zip services if we don't have libz. */ +/************************************************************************/ + +#if !defined(HAVE_LIBZ) + +void *CPLCreateZip( const char *pszZipFilename, char **papszOptions ) + +{ + CPLError( CE_Failure, CPLE_NotSupported, + "This GDAL/OGR build does not include zlib and zip services." ); + return NULL; +} + +CPLErr CPLCreateFileInZip( void *hZip, const char *pszFilename, + char **papszOptions ) + +{ + return CE_Failure; +} + +CPLErr CPLWriteFileInZip( void *hZip, const void *pBuffer, int nBufferSize ) + +{ + return CE_Failure; +} + +CPLErr CPLCloseFileInZip( void *hZip ) + +{ + return CE_Failure; +} + +CPLErr CPLCloseZip( void *hZip ) + +{ + return CE_Failure; +} + +void* CPLZLibDeflate( const void* ptr, size_t nBytes, int nLevel, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ) +{ + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; +} + +void* CPLZLibInflate( const void* ptr, size_t nBytes, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ) +{ + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; +} + +#endif /* !defined(HAVE_LIBZ) */ diff --git a/cpl/cpl_conv.h b/cpl/cpl_conv.h index f9f94ca..660652a 100644 --- a/cpl/cpl_conv.h +++ b/cpl/cpl_conv.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_conv.h 17742 2009-10-03 16:13:16Z rouault $ + * $Id: cpl_conv.h 27121 2014-04-03 22:08:55Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Convenience functions declarations. @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -50,7 +51,7 @@ CPL_C_START void CPL_DLL CPLVerifyConfiguration(void); const char CPL_DLL * CPL_STDCALL -CPLGetConfigOption( const char *, const char * ); +CPLGetConfigOption( const char *, const char * ) CPL_WARN_UNUSED_RESULT; void CPL_DLL CPL_STDCALL CPLSetConfigOption( const char *, const char * ); void CPL_DLL CPL_STDCALL CPLSetThreadLocalConfigOption( const char *pszKey, const char *pszValue ); @@ -60,10 +61,10 @@ void CPL_DLL CPL_STDCALL CPLFreeConfig(void); /* Safe malloc() API. Thin cover over VSI functions with fatal */ /* error reporting if memory allocation fails. */ /* -------------------------------------------------------------------- */ -void CPL_DLL *CPLMalloc( size_t ); -void CPL_DLL *CPLCalloc( size_t, size_t ); -void CPL_DLL *CPLRealloc( void *, size_t ); -char CPL_DLL *CPLStrdup( const char * ); +void CPL_DLL *CPLMalloc( size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL *CPLCalloc( size_t, size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL *CPLRealloc( void *, size_t ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *CPLStrdup( const char * ) CPL_WARN_UNUSED_RESULT; char CPL_DLL *CPLStrlwr( char *); #define CPLFree VSIFree @@ -73,8 +74,8 @@ char CPL_DLL *CPLStrlwr( char *); /* -------------------------------------------------------------------- */ char CPL_DLL *CPLFGets( char *, int, FILE *); const char CPL_DLL *CPLReadLine( FILE * ); -const char CPL_DLL *CPLReadLineL( FILE * ); -const char CPL_DLL *CPLReadLine2L( FILE * , int nMaxCols, char** papszOptions); +const char CPL_DLL *CPLReadLineL( VSILFILE * ); +const char CPL_DLL *CPLReadLine2L( VSILFILE * , int nMaxCols, char** papszOptions); /* -------------------------------------------------------------------- */ /* Convert ASCII string to floationg point number */ @@ -190,6 +191,7 @@ FILE CPL_DLL *CPLOpenShared( const char *, const char *, int ); void CPL_DLL CPLCloseShared( FILE * ); CPLSharedFileInfo CPL_DLL *CPLGetSharedList( int * ); void CPL_DLL CPLDumpSharedList( FILE * ); +void CPL_DLL CPLCleanupSharedFileMutex( void ); /* -------------------------------------------------------------------- */ /* DMS to Dec to DMS conversion. */ @@ -210,6 +212,41 @@ int CPL_DLL CPLUnlinkTree( const char * ); int CPL_DLL CPLCopyFile( const char *pszNewPath, const char *pszOldPath ); int CPL_DLL CPLMoveFile( const char *pszNewPath, const char *pszOldPath ); +/* -------------------------------------------------------------------- */ +/* ZIP Creation. */ +/* -------------------------------------------------------------------- */ +#define CPL_ZIP_API_OFFERED +void CPL_DLL *CPLCreateZip( const char *pszZipFilename, char **papszOptions ); +CPLErr CPL_DLL CPLCreateFileInZip( void *hZip, const char *pszFilename, + char **papszOptions ); +CPLErr CPL_DLL CPLWriteFileInZip( void *hZip, const void *pBuffer, int nBufferSize ); +CPLErr CPL_DLL CPLCloseFileInZip( void *hZip ); +CPLErr CPL_DLL CPLCloseZip( void *hZip ); + +/* -------------------------------------------------------------------- */ +/* ZLib compression */ +/* -------------------------------------------------------------------- */ + +void CPL_DLL *CPLZLibDeflate( const void* ptr, size_t nBytes, int nLevel, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ); +void CPL_DLL *CPLZLibInflate( const void* ptr, size_t nBytes, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ); + +/* -------------------------------------------------------------------- */ +/* XML validation. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLValidateXML(const char* pszXMLFilename, + const char* pszXSDFilename, + char** papszOptions); + +/* -------------------------------------------------------------------- */ +/* Locale handling. Prevents parallel executions of setlocale(). */ +/* -------------------------------------------------------------------- */ +char* CPLsetlocale (int category, const char* locale); +void CPLCleanupSetlocaleMutex(void); + CPL_C_END /* -------------------------------------------------------------------- */ @@ -218,7 +255,7 @@ CPL_C_END #if defined(__cplusplus) && !defined(CPL_SUPRESS_CPLUSPLUS) -class CPLLocaleC +class CPL_DLL CPLLocaleC { public: CPLLocaleC(); @@ -227,7 +264,7 @@ class CPLLocaleC private: char *pszOldLocale; - // Make it non-copyable + /* Make it non-copyable */ CPLLocaleC(CPLLocaleC&); CPLLocaleC& operator=(CPLLocaleC&); }; diff --git a/cpl/cpl_csv.cpp b/cpl/cpl_csv.cpp index 6fd5cb1..72307e6 100644 --- a/cpl/cpl_csv.cpp +++ b/cpl/cpl_csv.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_csv.cpp 17148 2009-05-29 20:45:45Z rouault $ + * $Id: cpl_csv.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Purpose: CSV (comma separated value) file access. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,12 +31,9 @@ #include "cpl_csv.h" #include "cpl_conv.h" #include "cpl_multiproc.h" +#include "gdal_csv.h" -CPL_CVSID("$Id: cpl_csv.cpp 17148 2009-05-29 20:45:45Z rouault $"); - -CPL_C_START -const char * GDALDefaultCSVFilename( const char *pszBasename ); -CPL_C_END +CPL_CVSID("$Id: cpl_csv.cpp 27044 2014-03-16 23:41:27Z rouault $"); /* ==================================================================== */ /* The CSVTable is a persistant set of info about an open CSV */ @@ -65,6 +63,18 @@ typedef struct ctb { char *pszRawData; } CSVTable; + +static void CSVDeaccessInternal( CSVTable **ppsCSVTableList, int bCanUseTLS, const char * pszFilename ); + +/************************************************************************/ +/* CSVFreeTLS() */ +/************************************************************************/ +static void CSVFreeTLS(void* pData) +{ + CSVDeaccessInternal( (CSVTable **)pData, FALSE, NULL ); + CPLFree(pData); +} + /* It would likely be better to share this list between threads, but that will require some rework. */ @@ -95,7 +105,7 @@ static CSVTable *CSVAccess( const char * pszFilename ) if( ppsCSVTableList == NULL ) { ppsCSVTableList = (CSVTable **) CPLCalloc(1,sizeof(CSVTable*)); - CPLSetTLS( CTLS_CSVTABLEPTR, ppsCSVTableList, TRUE ); + CPLSetTLSWithFreeFunc( CTLS_CSVTABLEPTR, ppsCSVTableList, CSVFreeTLS ); } /* -------------------------------------------------------------------- */ @@ -111,7 +121,7 @@ static CSVTable *CSVAccess( const char * pszFilename ) * Eventually we should consider promoting to the front of * the list to accelerate frequently accessed tables. */ - + return( psTable ); } } @@ -148,18 +158,11 @@ static CSVTable *CSVAccess( const char * pszFilename ) /* CSVDeaccess() */ /************************************************************************/ -void CSVDeaccess( const char * pszFilename ) +static void CSVDeaccessInternal( CSVTable **ppsCSVTableList, int bCanUseTLS, const char * pszFilename ) { CSVTable *psLast, *psTable; -/* -------------------------------------------------------------------- */ -/* Fetch the table, and allocate the thread-local pointer to it */ -/* if there isn't already one. */ -/* -------------------------------------------------------------------- */ - CSVTable **ppsCSVTableList; - - ppsCSVTableList = (CSVTable **) CPLGetTLS( CTLS_CSVTABLEPTR ); if( ppsCSVTableList == NULL ) return; @@ -169,7 +172,7 @@ void CSVDeaccess( const char * pszFilename ) if( pszFilename == NULL ) { while( *ppsCSVTableList != NULL ) - CSVDeaccess( (*ppsCSVTableList)->pszFilename ); + CSVDeaccessInternal( ppsCSVTableList, bCanUseTLS, (*ppsCSVTableList)->pszFilename ); return; } @@ -187,7 +190,8 @@ void CSVDeaccess( const char * pszFilename ) if( psTable == NULL ) { - CPLDebug( "CPL_CSV", "CPLDeaccess( %s ) - no match.", pszFilename ); + if (bCanUseTLS) + CPLDebug( "CPL_CSV", "CPLDeaccess( %s ) - no match.", pszFilename ); return; } @@ -214,7 +218,20 @@ void CSVDeaccess( const char * pszFilename ) CPLFree( psTable ); - CPLReadLine( NULL ); + if (bCanUseTLS) + CPLReadLine( NULL ); +} + +void CSVDeaccess( const char * pszFilename ) +{ + CSVTable **ppsCSVTableList; +/* -------------------------------------------------------------------- */ +/* Fetch the table, and allocate the thread-local pointer to it */ +/* if there isn't already one. */ +/* -------------------------------------------------------------------- */ + ppsCSVTableList = (CSVTable **) CPLGetTLS( CTLS_CSVTABLEPTR ); + + CSVDeaccessInternal(ppsCSVTableList, TRUE, pszFilename); } /************************************************************************/ @@ -513,11 +530,12 @@ char **CSVReadParseLine2( FILE * fp, char chDelimiter ) /* -------------------------------------------------------------------- */ pszWorkLine = CPLStrdup( pszLine ); + int i = 0, nCount = 0; + int nWorkLineLength = strlen(pszWorkLine); + while( TRUE ) { - int i, nCount = 0; - - for( i = 0; pszWorkLine[i] != '\0'; i++ ) + for( ; pszWorkLine[i] != '\0'; i++ ) { if( pszWorkLine[i] == '\"' && (i == 0 || pszWorkLine[i-1] != '\\') ) @@ -531,11 +549,18 @@ char **CSVReadParseLine2( FILE * fp, char chDelimiter ) if( pszLine == NULL ) break; - pszWorkLine = (char *) - CPLRealloc(pszWorkLine, - strlen(pszWorkLine) + strlen(pszLine) + 2); - strcat( pszWorkLine, "\n" ); // This gets lost in CPLReadLine(). - strcat( pszWorkLine, pszLine ); + int nLineLen = strlen(pszLine); + + char* pszWorkLineTmp = (char *) + VSIRealloc(pszWorkLine, + nWorkLineLength + nLineLen + 2); + if (pszWorkLineTmp == NULL) + break; + pszWorkLine = pszWorkLineTmp; + strcat( pszWorkLine + nWorkLineLength, "\n" ); // This gets lost in CPLReadLine(). + strcat( pszWorkLine + nWorkLineLength, pszLine ); + + nWorkLineLength += nLineLen + 1; } papszReturn = CSVSplitLine( pszWorkLine, chDelimiter ); @@ -1125,7 +1150,7 @@ const char * CSVFilename( const char *pszBasename ) /** * Override CSV file search method. * - * @param CSVFileOverride The pointer to a function which will return the + * @param pfnNewHook The pointer to a function which will return the * full path for a given filename. * @@ -1173,4 +1198,3 @@ void SetCSVFilenameHook( const char *(*pfnNewHook)( const char * ) ) pfnCSVFilenameHook = pfnNewHook; } CPL_C_END - diff --git a/cpl/cpl_dir.cpp b/cpl/cpl_dir.cpp deleted file mode 100644 index 49f1e27..0000000 --- a/cpl/cpl_dir.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/********************************************************************** - * $Id: cpl_dir.cpp 10646 2007-01-18 02:38:10Z warmerdam $ - * - * Name: cpl_dir.cpp - * Project: CPL - Common Portability Library - * Purpose: Directory manipulation. - * Author: Daniel Morissette, danmo@videotron.ca - * - ********************************************************************** - * Copyright (c) 1998, Daniel Morissette - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - ****************************************************************************/ - -#include "cpl_conv.h" -#include "cpl_string.h" - -CPL_CVSID("$Id: cpl_dir.cpp 10646 2007-01-18 02:38:10Z warmerdam $"); - -#if defined(WIN32) || defined(WIN32CE) - -/*===================================================================== - WIN32 / MSVC++ implementation - *====================================================================*/ - -#ifndef WIN32CE -# include -#else -# include -#endif - - - -/********************************************************************** - * CPLReadDir() - * - * Return a stringlist with the list of files in a directory. - * The returned stringlist should be freed with CSLDestroy(). - * - * Returns NULL if an error happened or if the directory could not - * be read. - **********************************************************************/ - -char **CPLReadDir(const char *pszPath) -{ - struct _finddata_t c_file; - long hFile; - char *pszFileSpec, **papszDir = NULL; - - if (strlen(pszPath) == 0) - pszPath = "."; - - pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath)); - - if ( (hFile = _findfirst( pszFileSpec, &c_file )) != -1L ) - { - do - { - papszDir = CSLAddString(papszDir, c_file.name); - } while( _findnext( hFile, &c_file ) == 0 ); - - _findclose( hFile ); - } - else - { - /* Should we generate an error??? - * For now we'll just return NULL (at the end of the function) - */ - } - - CPLFree(pszFileSpec); - - return papszDir; -} - -#else - -/*===================================================================== - POSIX (Unix) implementation - *====================================================================*/ - -#include -#include - -/********************************************************************** - * CPLReadDir() - * - * Return a stringlist with the list of files in a directory. - * The returned stringlist should be freed with CSLDestroy(). - * - * Returns NULL if an error happened or if the directory could not - * be read. - **********************************************************************/ - -/** - * Read names in a directory. - * - * This function abstracts access to directory contains. It returns a - * list of strings containing the names of files, and directories in this - * directory. The resulting string list becomes the responsibility of the - * application and should be freed with CSLDestroy() when no longer needed. - * - * Note that no error is issued via CPLError() if the directory path is - * invalid, though NULL is returned. - * - * @param pszPath the relative, or absolute path of a directory to read. - * @return The list of entries in the directory, or NULL if the directory - * doesn't exist. - */ - -char **CPLReadDir(const char *pszPath) -{ - DIR *hDir; - struct dirent *psDirEntry; - char **papszDir = NULL; - - if (strlen(pszPath) == 0) - pszPath = "."; - - if ( (hDir = opendir(pszPath)) != NULL ) - { - while( (psDirEntry = readdir(hDir)) != NULL ) - { - papszDir = CSLAddString(papszDir, psDirEntry->d_name); - } - - closedir( hDir ); - } - else - { - /* Should we generate an error??? - * For now we'll just return NULL (at the end of the function) - */ - } - - return papszDir; -} - -#endif diff --git a/cpl/cpl_error.cpp b/cpl/cpl_error.cpp index 2d2e0eb..2a8586c 100644 --- a/cpl/cpl_error.cpp +++ b/cpl/cpl_error.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_error.cpp 17293 2009-06-26 14:55:34Z warmerdam $ + * $Id: cpl_error.cpp 27493 2014-07-06 10:22:35Z rouault $ * * Name: cpl_error.cpp * Project: CPL - Common Portability Library @@ -8,6 +8,7 @@ * ********************************************************************** * Copyright (c) 1998, Daniel Morissette + * Copyright (c) 2007-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -39,10 +40,12 @@ #endif #define TIMESTAMP_DEBUG +//#define MEMORY_DEBUG -CPL_CVSID("$Id: cpl_error.cpp 17293 2009-06-26 14:55:34Z warmerdam $"); +CPL_CVSID("$Id: cpl_error.cpp 27493 2014-07-06 10:22:35Z rouault $"); static void *hErrorMutex = NULL; +static void *pErrorHandlerUserData = NULL; static CPLErrorHandler pfnErrorHandler = CPLDefaultErrorHandler; #if !defined(HAVE_VSNPRINTF) @@ -54,6 +57,7 @@ static CPLErrorHandler pfnErrorHandler = CPLDefaultErrorHandler; typedef struct errHandler { struct errHandler *psNext; + void *pUserData; CPLErrorHandler pfnHandler; } CPLErrorHandlerNode; @@ -62,7 +66,9 @@ typedef struct { CPLErr eLastErrType; CPLErrorHandlerNode *psHandlerStack; int nLastErrMsgMax; + int nFailureIntoWarning; char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE]; + /* Do not add anything here. szLastErrMsg must be the last field. See CPLRealloc() below */ } CPLErrorContext; /************************************************************************/ @@ -77,7 +83,10 @@ static CPLErrorContext *CPLGetErrorContext() if( psCtx == NULL ) { - psCtx = (CPLErrorContext *) CPLCalloc(sizeof(CPLErrorContext),1); + psCtx = (CPLErrorContext *) VSICalloc(sizeof(CPLErrorContext),1); + if (psCtx == NULL) { + CPLEmergencyError("Out of memory attempting to report error"); + } psCtx->eLastErrType = CE_None; psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg); CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE ); @@ -86,6 +95,28 @@ static CPLErrorContext *CPLGetErrorContext() return psCtx; } +/************************************************************************/ +/* CPLGetErrorHandlerUserData() */ +/************************************************************************/ + +/** + * Fetch the user data for the error context + * + * Fetches the user data for the current error context. You can + * set the user data for the error context when you add your handler by + * issuing CPLSetErrorHandlerEx() and CPLPushErrorHandlerEx(). Note that + * user data is primarily intended for providing context within error handlers + * themselves, but they could potentially be abused in other useful ways with the usual + * caveat emptor understanding. + * + * @return the user data pointer for the error context + */ + +void* CPL_STDCALL CPLGetErrorHandlerUserData(void) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + return (void*) psCtx->psHandlerStack ? psCtx->psHandlerStack->pUserData : pErrorHandlerUserData; +} /********************************************************************** * CPLError() @@ -142,6 +173,9 @@ void CPLErrorV(CPLErr eErrClass, int err_no, const char *fmt, va_list args ) { CPLErrorContext *psCtx = CPLGetErrorContext(); + if (psCtx->nFailureIntoWarning > 0 && eErrClass == CE_Failure) + eErrClass = CE_Warning; + /* -------------------------------------------------------------------- */ /* Expand the error message */ /* -------------------------------------------------------------------- */ @@ -203,6 +237,21 @@ void CPLErrorV(CPLErr eErrClass, int err_no, const char *fmt, va_list args ) vsprintf( psCtx->szLastErrMsg, fmt, args); #endif +/* -------------------------------------------------------------------- */ +/* Obfuscate any password in error message */ +/* -------------------------------------------------------------------- */ + + char* pszPassword = strstr(psCtx->szLastErrMsg, "password="); + if( pszPassword != NULL ) + { + char* pszIter = pszPassword + strlen("password="); + while( *pszIter != ' ' && *pszIter != '\0' ) + { + *pszIter = 'X'; + pszIter ++; + } + } + /* -------------------------------------------------------------------- */ /* If the user provided his own error handling function, then */ /* call it, otherwise print the error to stderr and return. */ @@ -232,6 +281,89 @@ void CPLErrorV(CPLErr eErrClass, int err_no, const char *fmt, va_list args ) abort(); } +/************************************************************************/ +/* CPLEmergencyError() */ +/************************************************************************/ + +/** + * Fatal error when things are bad. + * + * This function should be called in an emergency situation where + * it is unlikely that a regular error report would work. This would + * include in the case of heap exhaustion for even small allocations, + * or any failure in the process of reporting an error (such as TLS + * allocations). + * + * This function should never return. After the error message has been + * reported as best possible, the application will abort() similarly to how + * CPLError() aborts on CE_Fatal class errors. + * + * @param pszMessage the error message to report. + */ + +void CPLEmergencyError( const char *pszMessage ) +{ + CPLErrorContext *psCtx = NULL; + static int bInEmergencyError = FALSE; + + // If we are already in emergency error then one of the + // following failed, so avoid them the second time through. + if( !bInEmergencyError ) + { + bInEmergencyError = TRUE; + psCtx = (CPLErrorContext *) CPLGetTLS( CTLS_ERRORCONTEXT ); + + if( psCtx != NULL && psCtx->psHandlerStack != NULL ) + { + psCtx->psHandlerStack->pfnHandler( CE_Fatal, CPLE_AppDefined, + pszMessage ); + } + else if( pfnErrorHandler != NULL ) + { + pfnErrorHandler( CE_Fatal, CPLE_AppDefined, pszMessage ); + } + } + + // Ultimate fallback. + fprintf( stderr, "FATAL: %s\n", pszMessage ); + + abort(); +} + +/************************************************************************/ +/* CPLGetProcessMemorySize() */ +/************************************************************************/ + +#ifdef MEMORY_DEBUG + +#ifdef __linux +static int CPLGetProcessMemorySize() +{ + FILE* fp = fopen("/proc/self/status", "r"); + if( fp == NULL ) + return -1; + int nRet = -1; + char szLine[128]; + while (fgets(szLine, sizeof(szLine), fp) != NULL) + { + if (strncmp(szLine, "VmSize:", 7) == 0) + { + const char* pszPtr = szLine; + while( !(*pszPtr == '\0' || (*pszPtr >= '0' && *pszPtr <= '9')) ) + pszPtr ++; + nRet = atoi(pszPtr); + break; + } + } + fclose(fp); + return nRet; +} +#else +#error CPLGetProcessMemorySize() unimplemented for this OS +#endif + +#endif // def MEMORY_DEBUG + /************************************************************************/ /* CPLDebug() */ /************************************************************************/ @@ -317,6 +449,17 @@ void CPLDebug( const char * pszCategory, const char * pszFormat, ... ) } #endif +/* -------------------------------------------------------------------- */ +/* Add the process memory size. */ +/* -------------------------------------------------------------------- */ +#ifdef MEMORY_DEBUG + char szVmSize[32]; + sprintf( szVmSize, "[VmSize: %d] ", CPLGetProcessMemorySize()); + strcat( pszMessage, szVmSize ); +#endif + + //sprintf(pszMessage,"[%d] ", (int)getpid()); + /* -------------------------------------------------------------------- */ /* Add the category. */ /* -------------------------------------------------------------------- */ @@ -335,6 +478,21 @@ void CPLDebug( const char * pszCategory, const char * pszFormat, ... ) #endif va_end(args); +/* -------------------------------------------------------------------- */ +/* Obfuscate any password in error message */ +/* -------------------------------------------------------------------- */ + + char* pszPassword = strstr(pszMessage, "password="); + if( pszPassword != NULL ) + { + char* pszIter = pszPassword + strlen("password="); + while( *pszIter != ' ' && *pszIter != '\0' ) + { + *pszIter = 'X'; + pszIter ++; + } + } + /* -------------------------------------------------------------------- */ /* Invoke the current error handler. */ /* -------------------------------------------------------------------- */ @@ -372,6 +530,28 @@ void CPL_STDCALL CPLErrorReset() psCtx->eLastErrType = CE_None; } +/********************************************************************** + * CPLErrorSetState() + **********************************************************************/ + +/** + * Restore an error state, without emitting an error. + * + * Can be usefull if a routine might call CPLErrorReset() and one wants to + * preserve the previous error state. + * + * @since GDAL 2.0 + */ + +void CPL_DLL CPLErrorSetState( CPLErr eErrClass, int err_no, const char* pszMsg ) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + psCtx->nLastErrNo = err_no; + strncpy(psCtx->szLastErrMsg, pszMsg, psCtx->nLastErrMsgMax); + psCtx->szLastErrMsg[MAX(psCtx->nLastErrMsgMax-1, (int)strlen(pszMsg))] = '\0'; + psCtx->eLastErrType = eErrClass; +} /********************************************************************** * CPLGetLastErrorNo() @@ -380,7 +560,8 @@ void CPL_STDCALL CPLErrorReset() /** * Fetch the last error number. * - * This is the error number, not the error class. + * Fetches the last error number posted with CPLError(), that hasn't + * been cleared by CPLErrorReset(). This is the error number, not the error class. * * @return the error number of the last error to occur, or CPLE_None (0) * if there are no posted errors. @@ -400,9 +581,10 @@ int CPL_STDCALL CPLGetLastErrorNo() /** * Fetch the last error type. * - * This is the error class, not the error number. + * Fetches the last error type posted with CPLError(), that hasn't + * been cleared by CPLErrorReset(). This is the error class, not the error number. * - * @return the error number of the last error to occur, or CE_None (0) + * @return the error type of the last error to occur, or CE_None (0) * if there are no posted errors. */ @@ -468,7 +650,10 @@ void CPL_STDCALL CPLDefaultErrorHandler( CPLErr eErrClass, int nError, fpLog = stderr; if( CPLGetConfigOption( "CPL_LOG", NULL ) != NULL ) { - fpLog = fopen( CPLGetConfigOption("CPL_LOG",""), "wt" ); + const char* pszAccess = "wt"; + if( CPLGetConfigOption( "CPL_LOG_APPEND", NULL ) != NULL ) + pszAccess = "at"; + fpLog = fopen( CPLGetConfigOption("CPL_LOG",""), pszAccess ); if( fpLog == NULL ) fpLog = stderr; } @@ -584,7 +769,68 @@ void CPL_STDCALL CPLLoggingErrorHandler( CPLErr eErrClass, int nError, } /********************************************************************** - * CPLSetErrorHandler() + * CPLTurnFailureIntoWarning() * + **********************************************************************/ + +void CPLTurnFailureIntoWarning(int bOn ) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + psCtx->nFailureIntoWarning += (bOn) ? 1 : -1; + if (psCtx->nFailureIntoWarning < 0) + { + CPLDebug("CPL", "Wrong nesting of CPLTurnFailureIntoWarning(TRUE) / CPLTurnFailureIntoWarning(FALSE)"); + } +} + +/********************************************************************** + * CPLSetErrorHandlerEx() * + **********************************************************************/ + +/** + * Install custom error handle with user's data. This method is + * essentially CPLSetErrorHandler with an added pointer to pUserData. + * The pUserData is not returned in the CPLErrorHandler, however, and + * must be fetched via CPLGetLastErrorUserData + * + * @param pfnErrorHandlerNew new error handler function. + * @param pUserData User data to carry along with the error context. + * @return returns the previously installed error handler. + */ + +CPLErrorHandler CPL_STDCALL +CPLSetErrorHandlerEx( CPLErrorHandler pfnErrorHandlerNew, + void* pUserData ) +{ + CPLErrorHandler pfnOldHandler = pfnErrorHandler; + CPLErrorContext *psCtx = CPLGetErrorContext(); + + if( psCtx->psHandlerStack != NULL ) + { + CPLDebug( "CPL", + "CPLSetErrorHandler() called with an error handler on\n" + "the local stack. New error handler will not be used immediately.\n" ); + } + + + { + CPLMutexHolderD( &hErrorMutex ); + + pfnOldHandler = pfnErrorHandler; + + if( pfnErrorHandler == NULL ) + pfnErrorHandler = CPLDefaultErrorHandler; + else + pfnErrorHandler = pfnErrorHandlerNew; + + pErrorHandlerUserData = pUserData; + } + + return pfnOldHandler; +} + + +/********************************************************************** + * CPLSetErrorHandler() * **********************************************************************/ /** @@ -625,33 +871,10 @@ void CPL_STDCALL CPLLoggingErrorHandler( CPLErr eErrClass, int nError, * @param pfnErrorHandlerNew new error handler function. * @return returns the previously installed error handler. */ - CPLErrorHandler CPL_STDCALL CPLSetErrorHandler( CPLErrorHandler pfnErrorHandlerNew ) { - CPLErrorHandler pfnOldHandler = pfnErrorHandler; - CPLErrorContext *psCtx = CPLGetErrorContext(); - - if( psCtx->psHandlerStack != NULL ) - { - CPLDebug( "CPL", - "CPLSetErrorHandler() called with an error handler on\n" - "the local stack. New error handler will not be used immediately.\n" ); - } - - - { - CPLMutexHolderD( &hErrorMutex ); - - pfnOldHandler = pfnErrorHandler; - - if( pfnErrorHandler == NULL ) - pfnErrorHandler = CPLDefaultErrorHandler; - else - pfnErrorHandler = pfnErrorHandlerNew; - } - - return pfnOldHandler; + return CPLSetErrorHandlerEx(pfnErrorHandlerNew, NULL); } /************************************************************************/ @@ -662,7 +885,7 @@ CPLSetErrorHandler( CPLErrorHandler pfnErrorHandlerNew ) * Push a new CPLError handler. * * This pushes a new error handler on the thread-local error handler - * stack. This handler will be used untill removed with CPLPopErrorHandler(). + * stack. This handler will be used until removed with CPLPopErrorHandler(). * * The CPLSetErrorHandler() docs have further information on how * CPLError handlers work. @@ -672,14 +895,39 @@ CPLSetErrorHandler( CPLErrorHandler pfnErrorHandlerNew ) void CPL_STDCALL CPLPushErrorHandler( CPLErrorHandler pfnErrorHandlerNew ) +{ + CPLPushErrorHandlerEx(pfnErrorHandlerNew, NULL); +} + + +/************************************************************************/ +/* CPLPushErrorHandlerEx() */ +/************************************************************************/ + +/** + * Push a new CPLError handler with user data on the error context. + * + * This pushes a new error handler on the thread-local error handler + * stack. This handler will be used until removed with CPLPopErrorHandler(). + * Obtain the user data back by using CPLGetErrorContext(). + * + * The CPLSetErrorHandler() docs have further information on how + * CPLError handlers work. + * + * @param pfnErrorHandlerNew new error handler function. + * @param pUserData User data to put on the error context. + */ +void CPL_STDCALL CPLPushErrorHandlerEx( CPLErrorHandler pfnErrorHandlerNew, + void* pUserData ) + { CPLErrorContext *psCtx = CPLGetErrorContext(); CPLErrorHandlerNode *psNode; - psNode = (CPLErrorHandlerNode *) VSIMalloc(sizeof(CPLErrorHandlerNode)); + psNode = (CPLErrorHandlerNode *) CPLMalloc(sizeof(CPLErrorHandlerNode)); psNode->psNext = psCtx->psHandlerStack; psNode->pfnHandler = pfnErrorHandlerNew; - + psNode->pUserData = pUserData; psCtx->psHandlerStack = psNode; } @@ -738,3 +986,16 @@ void CPL_STDCALL _CPLAssert( const char * pszExpression, const char * pszFile, pszExpression, pszFile, iLine ); } + +/************************************************************************/ +/* CPLCleanupErrorMutex() */ +/************************************************************************/ + +void CPLCleanupErrorMutex() +{ + if( hErrorMutex != NULL ) + { + CPLDestroyMutex(hErrorMutex); + hErrorMutex = NULL; + } +} diff --git a/cpl/cpl_error.h b/cpl/cpl_error.h index 6738713..4938960 100644 --- a/cpl/cpl_error.h +++ b/cpl/cpl_error.h @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_error.h 14047 2008-03-20 18:46:12Z rouault $ + * $Id: cpl_error.h 27384 2014-05-24 12:28:12Z rouault $ * * Name: cpl_error.h * Project: CPL - Common Portability Library @@ -56,19 +56,26 @@ typedef enum void CPL_DLL CPLError(CPLErr eErrClass, int err_no, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (3, 4); void CPL_DLL CPLErrorV(CPLErr, int, const char *, va_list ); +void CPL_DLL CPLEmergencyError( const char * ); void CPL_DLL CPL_STDCALL CPLErrorReset( void ); int CPL_DLL CPL_STDCALL CPLGetLastErrorNo( void ); CPLErr CPL_DLL CPL_STDCALL CPLGetLastErrorType( void ); const char CPL_DLL * CPL_STDCALL CPLGetLastErrorMsg( void ); +void CPL_DLL * CPL_STDCALL CPLGetErrorHandlerUserData(void); +void CPL_DLL CPLErrorSetState( CPLErr eErrClass, int err_no, const char* pszMsg ); +void CPL_DLL CPLCleanupErrorMutex( void ); typedef void (CPL_STDCALL *CPLErrorHandler)(CPLErr, int, const char*); void CPL_DLL CPL_STDCALL CPLLoggingErrorHandler( CPLErr, int, const char * ); void CPL_DLL CPL_STDCALL CPLDefaultErrorHandler( CPLErr, int, const char * ); void CPL_DLL CPL_STDCALL CPLQuietErrorHandler( CPLErr, int, const char * ); +void CPLTurnFailureIntoWarning(int bOn ); CPLErrorHandler CPL_DLL CPL_STDCALL CPLSetErrorHandler(CPLErrorHandler); +CPLErrorHandler CPL_DLL CPL_STDCALL CPLSetErrorHandlerEx(CPLErrorHandler, void*); void CPL_DLL CPL_STDCALL CPLPushErrorHandler( CPLErrorHandler ); +void CPL_DLL CPL_STDCALL CPLPushErrorHandlerEx( CPLErrorHandler, void* ); void CPL_DLL CPL_STDCALL CPLPopErrorHandler(void); void CPL_DLL CPL_STDCALL CPLDebug( const char *, const char *, ... ) CPL_PRINT_FUNC_FORMAT (2, 3); diff --git a/cpl/cpl_findfile.cpp b/cpl/cpl_findfile.cpp index 764440a..84184fe 100644 --- a/cpl/cpl_findfile.cpp +++ b/cpl/cpl_findfile.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_findfile.cpp 17143 2009-05-28 18:26:05Z rouault $ + * $Id: cpl_findfile.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Generic data file location finder, with application hooking. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,7 +32,7 @@ #include "cpl_string.h" #include "cpl_multiproc.h" -CPL_CVSID("$Id: cpl_findfile.cpp 17143 2009-05-28 18:26:05Z rouault $"); +CPL_CVSID("$Id: cpl_findfile.cpp 27044 2014-03-16 23:41:27Z rouault $"); typedef struct { @@ -42,6 +43,27 @@ typedef struct } FindFileTLS; +/************************************************************************/ +/* CPLFindFileDeinitTLS() */ +/************************************************************************/ + +static void CPLPopFinderLocationInternal(FindFileTLS* pTLSData); +static CPLFileFinder CPLPopFileFinderInternal(FindFileTLS* pTLSData); + +static void CPLFindFileFreeTLS(void* pData) +{ + FindFileTLS* pTLSData = (FindFileTLS*) pData; + if( pTLSData->bFinderInitialized ) + { + while( pTLSData->papszFinderLocations != NULL ) + CPLPopFinderLocationInternal(pTLSData); + while( CPLPopFileFinderInternal(pTLSData) != NULL ) {} + + pTLSData->bFinderInitialized = FALSE; + } + CPLFree(pTLSData); +} + /************************************************************************/ /* CPLGetFindFileTLS() */ /************************************************************************/ @@ -53,7 +75,7 @@ static FindFileTLS* CPLGetFindFileTLS() if (pTLSData == NULL) { pTLSData = (FindFileTLS*) CPLCalloc(1, sizeof(FindFileTLS)); - CPLSetTLS( CTLS_FINDFILE, pTLSData, TRUE ); + CPLSetTLSWithFreeFunc( CTLS_FINDFILE, pTLSData, CPLFindFileFreeTLS ); } return pTLSData; } @@ -101,14 +123,8 @@ void CPLFinderClean() { FindFileTLS* pTLSData = CPLGetFindFileTLS(); - if( pTLSData->bFinderInitialized ) - { - while( pTLSData->papszFinderLocations != NULL ) - CPLPopFinderLocation(); - while( CPLPopFileFinder() != NULL ) {} - - pTLSData->bFinderInitialized = FALSE; - } + CPLFindFileFreeTLS(pTLSData); + CPLSetTLS( CTLS_FINDFILE, NULL, FALSE ); } /************************************************************************/ @@ -180,13 +196,11 @@ void CPLPushFileFinder( CPLFileFinder pfnFinder ) /* CPLPopFileFinder() */ /************************************************************************/ -CPLFileFinder CPLPopFileFinder() +CPLFileFinder CPLPopFileFinderInternal(FindFileTLS* pTLSData) { CPLFileFinder pfnReturn; - FindFileTLS* pTLSData = CPLFinderInit(); - if( pTLSData->nFileFinders == 0 ) return NULL; @@ -201,6 +215,12 @@ CPLFileFinder CPLPopFileFinder() return pfnReturn; } +CPLFileFinder CPLPopFileFinder() + +{ + return CPLPopFileFinderInternal(CPLFinderInit()); +} + /************************************************************************/ /* CPLPushFinderLocation() */ /************************************************************************/ @@ -219,13 +239,11 @@ void CPLPushFinderLocation( const char *pszLocation ) /* CPLPopFinderLocation() */ /************************************************************************/ -void CPLPopFinderLocation() +static void CPLPopFinderLocationInternal(FindFileTLS* pTLSData) { int nCount; - FindFileTLS* pTLSData = CPLFinderInit(); - if( pTLSData->papszFinderLocations == NULL ) return; @@ -242,3 +260,8 @@ void CPLPopFinderLocation() pTLSData->papszFinderLocations = NULL; } } + +void CPLPopFinderLocation() +{ + CPLPopFinderLocationInternal(CPLFinderInit()); +} diff --git a/cpl/cpl_getexecpath.cpp b/cpl/cpl_getexecpath.cpp index 3a53cb3..e182b58 100644 --- a/cpl/cpl_getexecpath.cpp +++ b/cpl/cpl_getexecpath.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_getexecpath.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * $Id: cpl_getexecpath.cpp 21915 2011-03-08 21:58:16Z warmerdam $ * * Project: CPL - Common Portability Library * Purpose: Implement CPLGetExecPath(). @@ -28,11 +28,14 @@ ****************************************************************************/ #include "cpl_conv.h" +#include "cpl_string.h" -CPL_CVSID("$Id: cpl_getexecpath.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); +CPL_CVSID("$Id: cpl_getexecpath.cpp 21915 2011-03-08 21:58:16Z warmerdam $"); #if defined(WIN32) || defined(WIN32CE) +#define HAVE_IMPLEMENTATION 1 + #if defined(WIN32CE) # include "cpl_win32ce_api.h" #else @@ -46,16 +49,72 @@ CPL_CVSID("$Id: cpl_getexecpath.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) { #ifndef WIN32CE - if( GetModuleFileName( NULL, pszPathBuf, nMaxLength ) == 0 ) + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + wchar_t *pwszPathBuf = (wchar_t*) + CPLCalloc(nMaxLength+1,sizeof(wchar_t)); + + if( GetModuleFileNameW( NULL, pwszPathBuf, nMaxLength ) == 0 ) + { + CPLFree( pwszPathBuf ); + return FALSE; + } + else + { + char *pszDecoded = + CPLRecodeFromWChar(pwszPathBuf,CPL_ENC_UCS2,CPL_ENC_UTF8); + + strncpy( pszPathBuf, pszDecoded, nMaxLength ); + CPLFree( pszDecoded ); + CPLFree( pwszPathBuf ); + return TRUE; + } + } + else + { + if( GetModuleFileName( NULL, pszPathBuf, nMaxLength ) == 0 ) + return FALSE; + else + return TRUE; + } #else if( CE_GetModuleFileNameA( NULL, pszPathBuf, nMaxLength ) == 0 ) -#endif return FALSE; else return TRUE; +#endif } -#else /* ndef WIN32 */ +#endif + +/************************************************************************/ +/* CPLGetExecPath() */ +/************************************************************************/ + +#if !defined(HAVE_IMPLEMENTATION) && defined(__linux) + +#include "cpl_multiproc.h" + +#define HAVE_IMPLEMENTATION 1 + +int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) +{ + long nPID = getpid(); + CPLString osExeLink; + ssize_t nResultLen; + + osExeLink.Printf( "/proc/%ld/exe", nPID ); + nResultLen = readlink( osExeLink, pszPathBuf, nMaxLength ); + if( nResultLen >= 0 ) + pszPathBuf[nResultLen] = '\0'; + else + pszPathBuf[0] = '\0'; + + return nResultLen > 0; +} + +#endif /************************************************************************/ /* CPLGetExecPath() */ @@ -66,7 +125,7 @@ int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) * * The path to the executable currently running is returned. This path * includes the name of the executable. Currently this only works on - * win32 platform. + * win32 and linux platforms. The returned path is UTF-8 encoded. * * @param pszPathBuf the buffer into which the path is placed. * @param nMaxLength the buffer size, MAX_PATH+1 is suggested. @@ -74,10 +133,13 @@ int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) * @return FALSE on failure or TRUE on success. */ +#ifndef HAVE_IMPLEMENTATION + int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) { return FALSE; } + #endif diff --git a/cpl/cpl_hash_set.cpp b/cpl/cpl_hash_set.cpp new file mode 100644 index 0000000..01c1804 --- /dev/null +++ b/cpl/cpl_hash_set.cpp @@ -0,0 +1,455 @@ +/********************************************************************** + * $Id: cpl_hash_set.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_hash_set.cpp + * Project: CPL - Common Portability Library + * Purpose: Hash set functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_hash_set.h" +#include "cpl_list.h" + +struct _CPLHashSet +{ + CPLHashSetHashFunc fnHashFunc; + CPLHashSetEqualFunc fnEqualFunc; + CPLHashSetFreeEltFunc fnFreeEltFunc; + CPLList** tabList; + int nSize; + int nIndiceAllocatedSize; + int nAllocatedSize; +#ifdef HASH_DEBUG + int nCollisions; +#endif +}; + +static const int anPrimes[] = +{ 53, 97, 193, 389, 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, 196613, 393241, + 786433, 1572869, 3145739, 6291469, 12582917, + 25165843, 50331653, 100663319, 201326611, + 402653189, 805306457, 1610612741 }; + +/************************************************************************/ +/* CPLHashSetNew() */ +/************************************************************************/ + +/** + * Creates a new hash set + * + * The hash function must return a hash value for the elements to insert. + * If fnHashFunc is NULL, CPLHashSetHashPointer will be used. + * + * The equal function must return if two elements are equal. + * If fnEqualFunc is NULL, CPLHashSetEqualPointer will be used. + * + * The free function is used to free elements inserted in the hash set, + * when the hash set is destroyed, when elements are removed or replaced. + * If fnFreeEltFunc is NULL, elements inserted into the hash set will not be freed. + * + * @param fnHashFunc hash function. May be NULL. + * @param fnEqualFunc equal function. May be NULL. + * @param fnFreeEltFunc element free function. May be NULL. + * + * @return a new hash set + */ + +CPLHashSet* CPLHashSetNew(CPLHashSetHashFunc fnHashFunc, + CPLHashSetEqualFunc fnEqualFunc, + CPLHashSetFreeEltFunc fnFreeEltFunc) +{ + CPLHashSet* set = (CPLHashSet*) CPLMalloc(sizeof(CPLHashSet)); + set->fnHashFunc = (fnHashFunc) ? fnHashFunc : CPLHashSetHashPointer; + set->fnEqualFunc = (fnEqualFunc) ? fnEqualFunc : CPLHashSetEqualPointer; + set->fnFreeEltFunc = fnFreeEltFunc; + set->nSize = 0; + set->tabList = (CPLList**) CPLCalloc(sizeof(CPLList*), 53); + set->nIndiceAllocatedSize = 0; + set->nAllocatedSize = 53; +#ifdef HASH_DEBUG + set->nCollisions = 0; +#endif + return set; +} + + +/************************************************************************/ +/* CPLHashSetSize() */ +/************************************************************************/ + +/** + * Returns the number of elements inserted in the hash set + * + * Note: this is not the internal size of the hash set + * + * @param set the hash set + * + * @return the number of elements in the hash set + */ + +int CPLHashSetSize(const CPLHashSet* set) +{ + CPLAssert(set != NULL); + return set->nSize; +} + +/************************************************************************/ +/* CPLHashSetDestroy() */ +/************************************************************************/ + +/** + * Destroys an allocated hash set. + * + * This function also frees the elements if a free function was + * provided at the creation of the hash set. + * + * @param set the hash set + */ + +void CPLHashSetDestroy(CPLHashSet* set) +{ + CPLAssert(set != NULL); + for(int i=0;inAllocatedSize;i++) + { + if (set->fnFreeEltFunc) + { + CPLList* cur = set->tabList[i]; + while(cur) + { + set->fnFreeEltFunc(cur->pData); + cur = cur->psNext; + } + } + CPLListDestroy(set->tabList[i]); + } + CPLFree(set->tabList); + CPLFree(set); +} + +/************************************************************************/ +/* CPLHashSetForeach() */ +/************************************************************************/ + + +/** + * Walk through the hash set and runs the provided function on all the + * elements + * + * This function is provided the user_data argument of CPLHashSetForeach. + * It must return TRUE to go on the walk through the hash set, or FALSE to + * make it stop. + * + * Note : the structure of the hash set must *NOT* be modified during the + * walk. + * + * @param set the hash set. + * @param fnIterFunc the function called on each element. + * @param user_data the user data provided to the function. + */ + +void CPLHashSetForeach(CPLHashSet* set, + CPLHashSetIterEltFunc fnIterFunc, + void* user_data) +{ + CPLAssert(set != NULL); + if (!fnIterFunc) return; + + for(int i=0;inAllocatedSize;i++) + { + CPLList* cur = set->tabList[i]; + while(cur) + { + if (fnIterFunc(cur->pData, user_data) == FALSE) + return; + + cur = cur->psNext; + } + } +} + +/************************************************************************/ +/* CPLHashSetRehash() */ +/************************************************************************/ + +static void CPLHashSetRehash(CPLHashSet* set) +{ + int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize]; + CPLList** newTabList = (CPLList**) CPLCalloc(sizeof(CPLList*), nNewAllocatedSize); +#ifdef HASH_DEBUG + CPLDebug("CPLHASH", "hashSet=%p, nSize=%d, nCollisions=%d, fCollisionRate=%.02f", + set, set->nSize, set->nCollisions, set->nCollisions * 100.0 / set->nSize); + set->nCollisions = 0; +#endif + for(int i=0;inAllocatedSize;i++) + { + CPLList* cur = set->tabList[i]; + while(cur) + { + unsigned long nNewHashVal = set->fnHashFunc(cur->pData) % nNewAllocatedSize; +#ifdef HASH_DEBUG + if (newTabList[nNewHashVal]) + set->nCollisions ++; +#endif + newTabList[nNewHashVal] = CPLListInsert(newTabList[nNewHashVal], cur->pData, 0); + cur = cur->psNext; + } + CPLListDestroy(set->tabList[i]); + } + CPLFree(set->tabList); + set->tabList = newTabList; + set->nAllocatedSize = nNewAllocatedSize; +} + + +/************************************************************************/ +/* CPLHashSetFindPtr() */ +/************************************************************************/ + +static void** CPLHashSetFindPtr(CPLHashSet* set, const void* elt) +{ + unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize; + CPLList* cur = set->tabList[nHashVal]; + while(cur) + { + if (set->fnEqualFunc(cur->pData, elt)) + return &cur->pData; + cur = cur->psNext; + } + return NULL; +} + +/************************************************************************/ +/* CPLHashSetInsert() */ +/************************************************************************/ + +/** + * Inserts an element into a hash set. + * + * If the element was already inserted in the hash set, the previous + * element is replaced by the new element. If a free function was provided, + * it is used to free the previously inserted element + * + * @param set the hash set + * @param elt the new element to insert in the hash set + * + * @return TRUE if the element was not already in the hash set + */ + +int CPLHashSetInsert(CPLHashSet* set, void* elt) +{ + CPLAssert(set != NULL); + void** pElt = CPLHashSetFindPtr(set, elt); + if (pElt) + { + if (set->fnFreeEltFunc) + set->fnFreeEltFunc(*pElt); + + *pElt = elt; + return FALSE; + } + + if (set->nSize >= 2 * set->nAllocatedSize / 3) + { + set->nIndiceAllocatedSize++; + CPLHashSetRehash(set); + } + + unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize; +#ifdef HASH_DEBUG + if (set->tabList[nHashVal]) + set->nCollisions ++; +#endif + set->tabList[nHashVal] = CPLListInsert(set->tabList[nHashVal], (void*) elt, 0); + set->nSize++; + + return TRUE; +} + +/************************************************************************/ +/* CPLHashSetLookup() */ +/************************************************************************/ + +/** + * Returns the element found in the hash set corresponding to the element to look up + * The element must not be modified. + * + * @param set the hash set + * @param elt the element to look up in the hash set + * + * @return the element found in the hash set or NULL + */ + +void* CPLHashSetLookup(CPLHashSet* set, const void* elt) +{ + CPLAssert(set != NULL); + void** pElt = CPLHashSetFindPtr(set, elt); + if (pElt) + return *pElt; + else + return NULL; +} + +/************************************************************************/ +/* CPLHashSetRemove() */ +/************************************************************************/ + +/** + * Removes an element from a hash set + * + * @param set the hash set + * @param elt the new element to remove from the hash set + * + * @return TRUE if the element was in the hash set + */ + +int CPLHashSetRemove(CPLHashSet* set, const void* elt) +{ + CPLAssert(set != NULL); + if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2) + { + set->nIndiceAllocatedSize--; + CPLHashSetRehash(set); + } + + int nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize; + CPLList* cur = set->tabList[nHashVal]; + CPLList* prev = NULL; + while(cur) + { + if (set->fnEqualFunc(cur->pData, elt)) + { + if (prev) + prev->psNext = cur->psNext; + else + set->tabList[nHashVal] = cur->psNext; + + if (set->fnFreeEltFunc) + set->fnFreeEltFunc(cur->pData); + + CPLFree(cur); +#ifdef HASH_DEBUG + if (set->tabList[nHashVal]) + set->nCollisions --; +#endif + + set->nSize--; + return TRUE; + } + prev = cur; + cur = cur->psNext; + } + return FALSE; +} + + +/************************************************************************/ +/* CPLHashSetHashPointer() */ +/************************************************************************/ + +/** + * Hash function for an arbitrary pointer + * + * @param elt the arbitrary pointer to hash + * + * @return the hash value of the pointer + */ + +unsigned long CPLHashSetHashPointer(const void* elt) +{ + return (unsigned long)(GUIntBig) elt; +} + +/************************************************************************/ +/* CPLHashSetEqualPointer() */ +/************************************************************************/ + +/** + * Equality function for arbitrary pointers + * + * @param elt1 the first arbitrary pointer to compare + * @param elt2 the second arbitrary pointer to compare + * + * @return TRUE if the pointers are equal + */ + +int CPLHashSetEqualPointer(const void* elt1, const void* elt2) +{ + return elt1 == elt2; +} + +/************************************************************************/ +/* CPLHashSetHashStr() */ +/************************************************************************/ + +/** + * Hash function for a zero-terminated string + * + * @param elt the string to hash. May be NULL. + * + * @return the hash value of the string + */ + +unsigned long CPLHashSetHashStr(const void *elt) +{ + unsigned char* pszStr = (unsigned char*)elt; + unsigned long hash = 0; + int c; + + if (pszStr == NULL) + return 0; + + while ((c = *pszStr++) != '\0') + hash = c + (hash << 6) + (hash << 16) - hash; + + return hash; +} + +/************************************************************************/ +/* CPLHashSetEqualStr() */ +/************************************************************************/ + +/** + * Equality function for strings + * + * @param elt1 the first string to compare. May be NULL. + * @param elt2 the second string to compare. May be NULL. + * + * @return TRUE if the strings are equal + */ + +int CPLHashSetEqualStr(const void* elt1, const void* elt2) +{ + const char* pszStr1 = (const char*)elt1; + const char* pszStr2 = (const char*)elt2; + if (pszStr1 == NULL && pszStr2 != NULL) + return FALSE; + else if (pszStr1 != NULL && pszStr2 == NULL) + return FALSE; + else if (pszStr1 == NULL && pszStr2 == NULL) + return TRUE; + else + return strcmp(pszStr1, pszStr2) == 0; +} diff --git a/cpl/cpl_hash_set.h b/cpl/cpl_hash_set.h new file mode 100644 index 0000000..fad62fe --- /dev/null +++ b/cpl/cpl_hash_set.h @@ -0,0 +1,92 @@ +/********************************************************************** + * $Id: cpl_hash_set.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_hash_set.h + * Project: CPL - Common Portability Library + * Purpose: Hash set functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_HASH_SET_H_INCLUDED +#define _CPL_HASH_SET_H_INCLUDED + +#include "cpl_port.h" + +/** + * \file cpl_hash_set.h + * + * Hash set implementation. + * + * An hash set is a data structure that holds elements that are unique + * according to a comparison function. Operations on the hash set, such as + * insertion, removal or lookup, are supposed to be fast if an efficient + * "hash" function is provided. + */ + +CPL_C_START + +/* Types */ + +typedef struct _CPLHashSet CPLHashSet; + +typedef unsigned long (*CPLHashSetHashFunc)(const void* elt); + +typedef int (*CPLHashSetEqualFunc)(const void* elt1, const void* elt2); + +typedef void (*CPLHashSetFreeEltFunc)(void* elt); + +typedef int (*CPLHashSetIterEltFunc)(void* elt, void* user_data); + +/* Functions */ + +CPLHashSet CPL_DLL * CPLHashSetNew(CPLHashSetHashFunc fnHashFunc, + CPLHashSetEqualFunc fnEqualFunc, + CPLHashSetFreeEltFunc fnFreeEltFunc); + +void CPL_DLL CPLHashSetDestroy(CPLHashSet* set); + +int CPL_DLL CPLHashSetSize(const CPLHashSet* set); + +void CPL_DLL CPLHashSetForeach(CPLHashSet* set, + CPLHashSetIterEltFunc fnIterFunc, + void* user_data); + +int CPL_DLL CPLHashSetInsert(CPLHashSet* set, void* elt); + +void CPL_DLL * CPLHashSetLookup(CPLHashSet* set, const void* elt); + +int CPL_DLL CPLHashSetRemove(CPLHashSet* set, const void* elt); + +unsigned long CPL_DLL CPLHashSetHashPointer(const void* elt); + +int CPL_DLL CPLHashSetEqualPointer(const void* elt1, const void* elt2); + +unsigned long CPL_DLL CPLHashSetHashStr(const void * pszStr); + +int CPL_DLL CPLHashSetEqualStr(const void* pszStr1, const void* pszStr2); + +CPL_C_END + +#endif /* _CPL_HASH_SET_H_INCLUDED */ + diff --git a/cpl/cpl_http.cpp b/cpl/cpl_http.cpp index 016476c..36105bf 100644 --- a/cpl/cpl_http.cpp +++ b/cpl/cpl_http.cpp @@ -1,12 +1,13 @@ /****************************************************************************** - * $Id: cpl_http.cpp,v 1.1 2010-07-08 19:31:09 aboudreault Exp $ + * $Id: cpl_http.cpp 27044 2014-03-16 23:41:27Z rouault $ * - * Project: WCS Client Driver - * Purpose: Implementation of Dataset and RasterBand classes for WCS. + * Project: libcurl based HTTP client + * Purpose: libcurl based HTTP client * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -27,14 +28,30 @@ * DEALINGS IN THE SOFTWARE. ****************************************************************************/ -#include "cpl_conv.h" +#include #include "cpl_http.h" +#include "cpl_multiproc.h" #ifdef HAVE_CURL # include + +void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions); + +/* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */ +#if LIBCURL_VERSION_NUM < 0x070a07 +#define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE +#endif + #endif -CPL_CVSID("$Id: cpl_http.cpp,v 1.1 2010-07-08 19:31:09 aboudreault Exp $"); +CPL_CVSID("$Id: cpl_http.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +// list of named persistent http sessions + +#ifdef HAVE_CURL +static std::map oSessionMap; +static void *hSessionMapMutex = NULL; +#endif /************************************************************************/ /* CPLWriteFct() */ @@ -77,6 +94,24 @@ CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo) return nmemb; } + +/************************************************************************/ +/* CPLHdrWriteFct() */ +/************************************************************************/ +static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo) +{ + CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo; + // copy the buffer to a char* and initialize with zeros (zero terminate as well) + char* pszHdr = (char*)CPLCalloc(nmemb + 1, size); + CPLPrintString(pszHdr, (char *)buffer, nmemb * size); + char *pszKey = NULL; + const char *pszValue = CPLParseNameValue(pszHdr, &pszKey ); + psResult->papszHeaders = CSLSetNameValue(psResult->papszHeaders, pszKey, pszValue); + CPLFree(pszHdr); + CPLFree(pszKey); + return nmemb; +} + #endif /* def HAVE_CURL */ /************************************************************************/ @@ -93,37 +128,251 @@ CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo) *
  • TIMEOUT=val, where val is in seconds
  • *
  • HEADERS=val, where val is an extra header to use when getting a web page. * For example "Accept: application/x-ogcwkt" - *
  • HTTPAUTH=[BASIC/NTLM/ANY] to specify an authentication scheme to use. + *
  • HTTPAUTH=[BASIC/NTLM/GSSNEGOTIATE/ANY] to specify an authentication scheme to use. *
  • USERPWD=userid:password to specify a user and password for authentication + *
  • POSTFIELDS=val, where val is a nul-terminated string to be passed to the server + * with a POST request. + *
  • PROXY=val, to make requests go through a proxy server, where val is of the + * form proxy.server.com:port_number + *
  • PROXYUSERPWD=val, where val is of the form username:password + *
  • PROXYAUTH=[BASIC/NTLM/DIGEST/ANY] to specify an proxy authentication scheme to use. + *
  • NETRC=[YES/NO] to enable or disable use of $HOME/.netrc, default YES. + *
  • CUSTOMREQUEST=val, where val is GET, PUT, POST, DELETE, etc.. (GDAL >= 1.9.0) * * - * @return a CPLHTTPResult* structure that must be freed by CPLHTTPDestroyResult(), - * or NULL if libcurl support is diabled + * Alternatively, if not defined in the papszOptions arguments, the PROXY, + * PROXYUSERPWD, PROXYAUTH and NETRC values are searched in the configuration + * options named GDAL_HTTP_PROXY, GDAL_HTTP_PROXYUSERPWD, GDAL_PROXY_AUTH and + * GDAL_HTTP_NETRC, as proxy configuration belongs to networking setup and + * makes more sense at the configuration option level than at the connection + * level. + * + * @return a CPLHTTPResult* structure that must be freed by + * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled */ CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions ) { #ifndef HAVE_CURL + (void) papszOptions; + (void) pszURL; + CPLError( CE_Failure, CPLE_NotSupported, "GDAL/OGR not compiled with libcurl support, remote requests not supported." ); return NULL; #else - CURL *http_handle; +/* -------------------------------------------------------------------- */ +/* Are we using a persistent named session? If so, search for */ +/* or create it. */ +/* */ +/* Currently this code does not attempt to protect against */ +/* multiple threads asking for the same named session. If that */ +/* occurs it will be in use in multiple threads at once which */ +/* might have bad consequences depending on what guarantees */ +/* libcurl gives - which I have not investigated. */ +/* -------------------------------------------------------------------- */ + CURL *http_handle = NULL; + + const char *pszPersistent = CSLFetchNameValue( papszOptions, "PERSISTENT" ); + const char *pszClosePersistent = CSLFetchNameValue( papszOptions, "CLOSE_PERSISTENT" ); + if (pszPersistent) + { + CPLString osSessionName = pszPersistent; + CPLMutexHolder oHolder( &hSessionMapMutex ); + + if( oSessionMap.count( osSessionName ) == 0 ) + { + oSessionMap[osSessionName] = curl_easy_init(); + CPLDebug( "HTTP", "Establish persistent session named '%s'.", + osSessionName.c_str() ); + } + + http_handle = oSessionMap[osSessionName]; + } +/* -------------------------------------------------------------------- */ +/* Are we requested to close a persistent named session? */ +/* -------------------------------------------------------------------- */ + else if (pszClosePersistent) + { + CPLString osSessionName = pszClosePersistent; + CPLMutexHolder oHolder( &hSessionMapMutex ); + + std::map::iterator oIter = oSessionMap.find( osSessionName ); + if( oIter != oSessionMap.end() ) + { + curl_easy_cleanup(oIter->second); + oSessionMap.erase(oIter); + CPLDebug( "HTTP", "Ended persistent session named '%s'.", + osSessionName.c_str() ); + } + else + { + CPLDebug( "HTTP", "Could not find persistent session named '%s'.", + osSessionName.c_str() ); + } + + return NULL; + } + else + http_handle = curl_easy_init(); + +/* -------------------------------------------------------------------- */ +/* Setup the request. */ +/* -------------------------------------------------------------------- */ char szCurlErrBuf[CURL_ERROR_SIZE+1]; CPLHTTPResult *psResult; struct curl_slist *headers=NULL; - - CPLDebug( "HTTP", "Fetch(%s)", pszURL ); + const char* pszArobase = strchr(pszURL, '@'); + const char* pszSlash = strchr(pszURL, '/'); + const char* pszColon = (pszSlash) ? strchr(pszSlash, ':') : NULL; + if (pszArobase != NULL && pszColon != NULL && pszArobase - pszColon > 0) + { + /* http://user:password@www.example.com */ + char* pszSanitizedURL = CPLStrdup(pszURL); + pszSanitizedURL[pszColon-pszURL] = 0; + CPLDebug( "HTTP", "Fetch(%s:#password#%s)", pszSanitizedURL, pszArobase ); + CPLFree(pszSanitizedURL); + } + else + { + CPLDebug( "HTTP", "Fetch(%s)", pszURL ); + } psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult)); - http_handle = curl_easy_init(); - curl_easy_setopt(http_handle, CURLOPT_URL, pszURL ); + CPLHTTPSetOptions(http_handle, papszOptions); + + // turn off SSL verification, accept all servers with ssl + curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, FALSE); + + /* Set Headers.*/ + const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" ); + if( pszHeaders != NULL ) { + CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders); + headers = curl_slist_append(headers, pszHeaders); + curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers); + } + + // capture response headers + curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult); + curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION, CPLHdrWriteFct); + + curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult ); + curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct ); + + szCurlErrBuf[0] = '\0'; + + curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + static int bHasCheckVersion = FALSE; + static int bSupportGZip = FALSE; + if (!bHasCheckVersion) + { + bSupportGZip = strstr(curl_version(), "zlib/") != NULL; + bHasCheckVersion = TRUE; + } + int bGZipRequested = FALSE; + if (bSupportGZip && CSLTestBoolean(CPLGetConfigOption("CPL_CURL_GZIP", "YES"))) + { + bGZipRequested = TRUE; + curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip"); + } + +/* -------------------------------------------------------------------- */ +/* Execute the request, waiting for results. */ +/* -------------------------------------------------------------------- */ + psResult->nStatus = (int) curl_easy_perform( http_handle ); + +/* -------------------------------------------------------------------- */ +/* Fetch content-type if possible. */ +/* -------------------------------------------------------------------- */ + psResult->pszContentType = NULL; + curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE, + &(psResult->pszContentType) ); + if( psResult->pszContentType != NULL ) + psResult->pszContentType = CPLStrdup(psResult->pszContentType); + +/* -------------------------------------------------------------------- */ +/* Have we encountered some sort of error? */ +/* -------------------------------------------------------------------- */ + if( strlen(szCurlErrBuf) > 0 ) + { + int bSkipError = FALSE; + + /* Some servers such as http://115.113.193.14/cgi-bin/world/qgis_mapserv.fcgi?VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities */ + /* invalidly return Content-Length as the uncompressed size, with makes curl to wait for more data */ + /* and time-out finally. If we got the expected data size, then we don't emit an error */ + /* but turn off GZip requests */ + if (bGZipRequested && + strstr(szCurlErrBuf, "transfer closed with") && + strstr(szCurlErrBuf, "bytes remaining to read")) + { + const char* pszContentLength = + CSLFetchNameValue(psResult->papszHeaders, "Content-Length"); + if (pszContentLength && psResult->nDataLen != 0 && + atoi(pszContentLength) == psResult->nDataLen) + { + const char* pszCurlGZIPOption = CPLGetConfigOption("CPL_CURL_GZIP", NULL); + if (pszCurlGZIPOption == NULL) + { + CPLSetConfigOption("CPL_CURL_GZIP", "NO"); + CPLDebug("HTTP", "Disabling CPL_CURL_GZIP, because %s doesn't support it properly", + pszURL); + } + psResult->nStatus = 0; + bSkipError = TRUE; + } + } + if (!bSkipError) + { + psResult->pszErrBuf = CPLStrdup(szCurlErrBuf); + CPLError( CE_Failure, CPLE_AppDefined, + "%s", szCurlErrBuf ); + } + } + else + { + /* HTTP errors do not trigger curl errors. But we need to */ + /* propagate them to the caller though */ + long response_code = 0; + curl_easy_getinfo(http_handle, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code >= 400 && response_code < 600) + { + psResult->pszErrBuf = CPLStrdup(CPLSPrintf("HTTP error code : %d", (int)response_code)); + CPLError( CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf ); + } + } + + if (!pszPersistent) + curl_easy_cleanup( http_handle ); + + curl_slist_free_all(headers); + + return psResult; +#endif /* def HAVE_CURL */ +} + +#ifdef HAVE_CURL +/************************************************************************/ +/* CPLHTTPSetOptions() */ +/************************************************************************/ + +void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions) +{ + if (CSLTestBoolean(CPLGetConfigOption("CPL_CURL_VERBOSE", "NO"))) + curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1); + + const char *pszHttpVersion = CSLFetchNameValue( papszOptions, "HTTP_VERSION"); + if( pszHttpVersion && strcmp(pszHttpVersion, "1.0") == 0 ) + curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); + /* Support control over HTTPAUTH */ const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" ); + if( pszHttpAuth == NULL ) + pszHttpAuth = CPLGetConfigOption( "GDAL_HTTP_AUTH", NULL ); if( pszHttpAuth == NULL ) /* do nothing */; @@ -135,6 +384,10 @@ CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions ) curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM ); else if( EQUAL(pszHttpAuth,"ANY") ) curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); +#ifdef CURLAUTH_GSSNEGOTIATE + else if( EQUAL(pszHttpAuth,"NEGOTIATE") ) + curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE ); +#endif else { CPLError( CE_Warning, CPLE_AppDefined, @@ -149,29 +402,96 @@ CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions ) } #endif + /* Support use of .netrc - default enabled */ + const char *pszHttpNetrc = CSLFetchNameValue( papszOptions, "NETRC" ); + if( pszHttpNetrc == NULL ) + pszHttpNetrc = CPLGetConfigOption( "GDAL_HTTP_NETRC", "YES" ); + if( pszHttpNetrc == NULL || CSLTestBoolean(pszHttpNetrc) ) + curl_easy_setopt(http_handle, CURLOPT_NETRC, 1L); + /* Support setting userid:password */ const char *pszUserPwd = CSLFetchNameValue( papszOptions, "USERPWD" ); + if (pszUserPwd == NULL) + pszUserPwd = CPLGetConfigOption("GDAL_HTTP_USERPWD", NULL); if( pszUserPwd != NULL ) curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd ); + /* Set Proxy parameters */ + const char* pszProxy = CSLFetchNameValue( papszOptions, "PROXY" ); + if (pszProxy == NULL) + pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", NULL); + if (pszProxy) + curl_easy_setopt(http_handle,CURLOPT_PROXY,pszProxy); + + const char* pszProxyUserPwd = CSLFetchNameValue( papszOptions, "PROXYUSERPWD" ); + if (pszProxyUserPwd == NULL) + pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", NULL); + if (pszProxyUserPwd) + curl_easy_setopt(http_handle,CURLOPT_PROXYUSERPWD,pszProxyUserPwd); + + /* Support control over PROXYAUTH */ + const char *pszProxyAuth = CSLFetchNameValue( papszOptions, "PROXYAUTH" ); + if( pszProxyAuth == NULL ) + pszProxyAuth = CPLGetConfigOption( "GDAL_PROXY_AUTH", NULL ); + if( pszProxyAuth == NULL ) + /* do nothing */; + /* CURLOPT_PROXYAUTH is defined in curl 7.11.0 or newer */ +#if LIBCURL_VERSION_NUM >= 0x70B00 + else if( EQUAL(pszProxyAuth,"BASIC") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_BASIC ); + else if( EQUAL(pszProxyAuth,"NTLM") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM ); + else if( EQUAL(pszProxyAuth,"DIGEST") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST ); + else if( EQUAL(pszProxyAuth,"ANY") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported PROXYAUTH value '%s', ignored.", + pszProxyAuth ); + } +#else + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "PROXYAUTH option needs curl >= 7.11.0" ); + } +#endif + /* Enable following redirections. Requires libcurl 7.10.1 at least */ curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 ); curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 ); /* Set timeout.*/ const char *pszTimeout = CSLFetchNameValue( papszOptions, "TIMEOUT" ); + if (pszTimeout == NULL) + pszTimeout = CPLGetConfigOption("GDAL_HTTP_TIMEOUT", NULL); if( pszTimeout != NULL ) - curl_easy_setopt(http_handle, CURLOPT_TIMEOUT, - atoi(pszTimeout) ); + curl_easy_setopt(http_handle, CURLOPT_TIMEOUT, atoi(pszTimeout) ); - /* Set Headers.*/ - const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" ); - if( pszHeaders != NULL ) { - CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders); - headers = curl_slist_append(headers, pszHeaders); - curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers); + /* Disable some SSL verification */ + const char *pszUnsafeSSL = CSLFetchNameValue( papszOptions, "UNSAFESSL" ); + if (pszUnsafeSSL == NULL) + pszUnsafeSSL = CPLGetConfigOption("GDAL_HTTP_UNSAFESSL", NULL); + if (pszUnsafeSSL != NULL && CSLTestBoolean(pszUnsafeSSL)) + { + curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYHOST, 0L); } - + + /* Set Referer */ + const char *pszReferer = CSLFetchNameValue(papszOptions, "REFERER"); + if (pszReferer != NULL) + curl_easy_setopt(http_handle, CURLOPT_REFERER, pszReferer); + + /* Set User-Agent */ + const char *pszUserAgent = CSLFetchNameValue(papszOptions, "USERAGENT"); + if (pszUserAgent == NULL) + pszUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", NULL); + if (pszUserAgent != NULL) + curl_easy_setopt(http_handle, CURLOPT_USERAGENT, pszUserAgent); + /* NOSIGNAL should be set to true for timeout to work in multithread * environments on Unix, requires libcurl 7.10 or more recent. * (this force avoiding the use of sgnal handlers) @@ -180,42 +500,22 @@ CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions ) curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 ); #endif - curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult ); - curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct ); - - szCurlErrBuf[0] = '\0'; - - curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); - - psResult->nStatus = (int) curl_easy_perform( http_handle ); - -/* -------------------------------------------------------------------- */ -/* Fetch content-type if possible. */ -/* -------------------------------------------------------------------- */ - CURLcode err; - - psResult->pszContentType = NULL; - err = curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE, - &(psResult->pszContentType) ); - if( psResult->pszContentType != NULL ) - psResult->pszContentType = CPLStrdup(psResult->pszContentType); - -/* -------------------------------------------------------------------- */ -/* Have we encountered some sort of error? */ -/* -------------------------------------------------------------------- */ - if( strlen(szCurlErrBuf) > 0 ) + /* Set POST mode */ + const char* pszPost = CSLFetchNameValue( papszOptions, "POSTFIELDS" ); + if( pszPost != NULL ) { - psResult->pszErrBuf = CPLStrdup(szCurlErrBuf); - CPLError( CE_Failure, CPLE_AppDefined, - "%s", szCurlErrBuf ); + CPLDebug("HTTP", "These POSTFIELDS were sent:%.4000s", pszPost); + curl_easy_setopt(http_handle, CURLOPT_POST, 1 ); + curl_easy_setopt(http_handle, CURLOPT_POSTFIELDS, pszPost ); } - curl_easy_cleanup( http_handle ); - curl_slist_free_all(headers); - - return psResult; -#endif /* def HAVE_CURL */ + const char* pszCustomRequest = CSLFetchNameValue( papszOptions, "CUSTOMREQUEST" ); + if( pszCustomRequest != NULL ) + { + curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST, pszCustomRequest ); + } } +#endif /* def HAVE_CURL */ /************************************************************************/ /* CPLHTTPEnabled() */ @@ -248,8 +548,24 @@ int CPLHTTPEnabled() void CPLHTTPCleanup() { - /* nothing for now, but if we use the more complicated api later, - we will need to do cleanup, like mapserver maphttp.c does. */ +#ifdef HAVE_CURL + if( !hSessionMapMutex ) + return; + + { + CPLMutexHolder oHolder( &hSessionMapMutex ); + std::map::iterator oIt; + + for( oIt=oSessionMap.begin(); oIt != oSessionMap.end(); oIt++ ) + curl_easy_cleanup( oIt->second ); + + oSessionMap.clear(); + } + + // not quite a safe sequence. + CPLDestroyMutex( hSessionMapMutex ); + hSessionMapMutex = NULL; +#endif } /************************************************************************/ @@ -269,6 +585,15 @@ void CPLHTTPDestroyResult( CPLHTTPResult *psResult ) CPLFree( psResult->pabyData ); CPLFree( psResult->pszErrBuf ); CPLFree( psResult->pszContentType ); + CSLDestroy( psResult->papszHeaders ); + + int i; + for(i=0;inMimePartCount;i++) + { + CSLDestroy( psResult->pasMimePart[i].papszHeaders ); + } + CPLFree(psResult->pasMimePart); + CPLFree( psResult ); } } @@ -319,6 +644,7 @@ int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to parse multi-part mime, boundary not parsable." ); + CSLDestroy( papszTokens ); return FALSE; } @@ -340,7 +666,9 @@ int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ) } pszNext += strlen(osBoundary); - while( *pszNext != '\n' && *pszNext != '\0' ) + while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' ) + pszNext++; + if( *pszNext == '\r' ) pszNext++; if( *pszNext == '\n' ) pszNext++; @@ -362,24 +690,35 @@ int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ) /* -------------------------------------------------------------------- */ /* Collect headers. */ /* -------------------------------------------------------------------- */ - while( *pszNext != '\n' && *pszNext != '\0' ) + while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' ) { char *pszEOL = strstr(pszNext,"\n"); if( pszEOL == NULL ) { - CPLAssert( FALSE ); - break; + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + return FALSE; } *pszEOL = '\0'; + int bRestoreAntislashR = FALSE; + if (pszEOL - pszNext > 1 && pszEOL[-1] == '\r') + { + bRestoreAntislashR = TRUE; + pszEOL[-1] = '\0'; + } psPart->papszHeaders = CSLAddString( psPart->papszHeaders, pszNext ); + if (bRestoreAntislashR) + pszEOL[-1] = '\r'; *pszEOL = '\n'; pszNext = pszEOL + 1; } + if( *pszNext == '\r' ) + pszNext++; if( *pszNext == '\n' ) pszNext++; @@ -401,8 +740,9 @@ int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ) if( nBytesAvail == 0 ) { - CPLAssert( FALSE ); - break; + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + return FALSE; } psPart->nDataLen = pszNext - (const char *) psPart->pabyData; @@ -412,12 +752,16 @@ int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ) { break; } - else if( *pszNext == '\n' ) + + if( *pszNext == '\r' ) + pszNext++; + if( *pszNext == '\n' ) pszNext++; else { - CPLAssert( FALSE ); - break; + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + return FALSE; } } diff --git a/cpl/cpl_http.h b/cpl/cpl_http.h index 3b61d3e..6747853 100644 --- a/cpl/cpl_http.h +++ b/cpl/cpl_http.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_http.h,v 1.1 2010-07-08 19:31:09 aboudreault Exp $ + * $Id: cpl_http.h 27044 2014-03-16 23:41:27Z rouault $ * * Project: Common Portability Library * Purpose: Function wrapper for libcurl HTTP access. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2009, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -52,16 +53,31 @@ typedef struct { /*! Describe the result of a CPLHTTPFetch() call */ typedef struct { - /*! HTTP status code : 200=success, value < 0 if request failed */ int nStatus; - /*! Content-Type of the response */ char *pszContentType; - /*! Error message from curl, or NULL */ char *pszErrBuf; + /*! cURL error code : 0=success, non-zero if request failed */ + int nStatus; - /*! Length of the pabyData buffer */ int nDataLen; - int nDataAlloc; - /*! Buffer with downloaded data */ GByte *pabyData; + /*! Content-Type of the response */ + char *pszContentType; + + /*! Error message from curl, or NULL */ + char *pszErrBuf; + + /*! Length of the pabyData buffer */ + int nDataLen; + int nDataAlloc; + + /*! Buffer with downloaded data */ + GByte *pabyData; + + /*! Headers returned */ + char **papszHeaders; + + /*! Number of parts in a multipart message */ + int nMimePartCount; + + /*! Array of parts (resolved by CPLHTTPParseMultipartMime()) */ + CPLMimePart *pasMimePart; - /*! Number of parts in a multipart message */ int nMimePartCount; - /*! Array of parts (resolved by CPLHTTPParseMultipartMime()) */ CPLMimePart *pasMimePart; } CPLHTTPResult; int CPL_DLL CPLHTTPEnabled( void ); @@ -70,6 +86,20 @@ void CPL_DLL CPLHTTPCleanup( void ); void CPL_DLL CPLHTTPDestroyResult( CPLHTTPResult *psResult ); int CPL_DLL CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ); +/* -------------------------------------------------------------------- */ +/* The following is related to OAuth2 authorization around */ +/* google services like fusion tables, and potentially others */ +/* in the future. Code in cpl_google_oauth2.cpp. */ +/* */ +/* These services are built on CPL HTTP services. */ +/* -------------------------------------------------------------------- */ + +char CPL_DLL *GOA2GetAuthorizationURL( const char *pszScope ); +char CPL_DLL *GOA2GetRefreshToken( const char *pszAuthToken, + const char *pszScope ); +char CPL_DLL *GOA2GetAccessToken( const char *pszRefreshToken, + const char *pszScope ); + CPL_C_END #endif /* ndef CPL_HTTP_H_INCLUDED */ diff --git a/cpl/cpl_list.cpp b/cpl/cpl_list.cpp new file mode 100644 index 0000000..5059317 --- /dev/null +++ b/cpl/cpl_list.cpp @@ -0,0 +1,339 @@ +/********************************************************************** + * $Id: cpl_list.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_list.cpp + * Project: CPL - Common Portability Library + * Purpose: List functions. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ********************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_list.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: cpl_list.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/*===================================================================== + List manipulation functions. + =====================================================================*/ + +/************************************************************************/ +/* CPLListAppend() */ +/************************************************************************/ + +/** + * Append an object list and return a pointer to the modified list. + * If the input list is NULL, then a new list is created. + * + * @param psList pointer to list head. + * @param pData pointer to inserted data object. May be NULL. + * + * @return pointer to the head of modified list. + */ + +CPLList *CPLListAppend( CPLList *psList, void *pData ) +{ + CPLList *psLast; + + /* Allocate room for the new object */ + if ( psList == NULL ) + { + psLast = psList = (CPLList *)CPLMalloc( sizeof(CPLList) ); + } + else + { + psLast = CPLListGetLast( psList ); + psLast = psLast->psNext = (CPLList *)CPLMalloc( sizeof(CPLList) ); + } + + /* Append object to the end of list */ + psLast->pData = pData; + psLast->psNext = NULL; + + return psList; +} + +/************************************************************************/ +/* CPLListInsert() */ +/************************************************************************/ + +/** + * Insert an object into list at specified position (zero based). + * If the input list is NULL, then a new list is created. + * + * @param psList pointer to list head. + * @param pData pointer to inserted data object. May be NULL. + * @param nPosition position number to insert an object. + * + * @return pointer to the head of modified list. + */ + +CPLList *CPLListInsert( CPLList *psList, void *pData, int nPosition ) +{ + CPLList *psCurrent; + int i, nCount; + + if ( nPosition < 0 ) + return psList; /* Nothing to do!*/ + + nCount = CPLListCount( psList ); + + if ( nPosition == 0) + { + CPLList *psNew = (CPLList *)CPLMalloc( sizeof(CPLList) ); + psNew->pData = pData; + psNew->psNext = psList; + psList = psNew; + } + else if ( nCount < nPosition ) + { + /* Allocate room for the new object */ + CPLList* psLast = CPLListGetLast(psList); + for ( i = nCount; i <= nPosition - 1; i++ ) + { + psLast = CPLListAppend( psLast, NULL ); + if (psList == NULL) + psList = psLast; + else + psLast = psLast->psNext; + } + psLast = CPLListAppend( psLast, pData ); + if (psList == NULL) + psList = psLast; + } + else + { + CPLList *psNew = (CPLList *)CPLMalloc( sizeof(CPLList) ); + psNew->pData = pData; + + psCurrent = psList; + for ( i = 0; i < nPosition - 1; i++ ) + psCurrent = psCurrent->psNext; + psNew->psNext = psCurrent->psNext; + psCurrent->psNext = psNew; + } + + return psList; +} + +/************************************************************************/ +/* CPLListGetLast() */ +/************************************************************************/ + +/** + * Return the pointer to last element in a list. + * + * @param psList pointer to list head. + * + * @return pointer to last element in a list. + */ + +CPLList *CPLListGetLast( CPLList *psList ) +{ + CPLList *psCurrent = psList; + + if ( psList == NULL ) + return NULL; + + while ( psCurrent->psNext ) + psCurrent = psCurrent->psNext; + + return psCurrent; +} + +/************************************************************************/ +/* CPLListGet() */ +/************************************************************************/ + +/** + * Return the pointer to the specified element in a list. + * + * @param psList pointer to list head. + * @param nPosition the index of the element in the list, 0 being the first element + * + * @return pointer to the specified element in a list. + */ + +CPLList *CPLListGet( CPLList *psList, int nPosition ) +{ + int iItem = 0; + CPLList *psCurrent = psList; + + if ( nPosition < 0 ) + return NULL; + + while ( iItem < nPosition && psCurrent ) + { + psCurrent = psCurrent->psNext; + iItem++; + } + + return psCurrent; +} + +/************************************************************************/ +/* CPLListCount() */ +/************************************************************************/ + +/** + * Return the number of elements in a list. + * + * @param psList pointer to list head. + * + * @return number of elements in a list. + */ + +int CPLListCount( CPLList *psList ) +{ + int nItems = 0; + CPLList *psCurrent = psList; + + while ( psCurrent ) + { + nItems++; + psCurrent = psCurrent->psNext; + } + + return nItems; +} + +/************************************************************************/ +/* CPLListRemove() */ +/************************************************************************/ + +/** + * Remove the element from the specified position (zero based) in a list. Data + * object contained in removed element must be freed by the caller first. + * + * @param psList pointer to list head. + * @param nPosition position number to delet an element. + * + * @return pointer to the head of modified list. + */ + +CPLList *CPLListRemove( CPLList *psList, int nPosition ) +{ + CPLList *psCurrent, *psRemoved; + int i; + + if ( psList == NULL) + { + return NULL; + } + else if ( nPosition < 0) + { + return psList; /* Nothing to do!*/ + } + else if ( nPosition == 0 ) + { + psCurrent = psList->psNext; + CPLFree( psList ); + psList = psCurrent; + } + else + { + psCurrent = psList; + for ( i = 0; i < nPosition - 1; i++ ) + { + psCurrent = psCurrent->psNext; + /* psCurrent == NULL if nPosition >= CPLListCount(psList) */ + if (psCurrent == NULL) + return psList; + } + psRemoved = psCurrent->psNext; + /* psRemoved == NULL if nPosition >= CPLListCount(psList) */ + if (psRemoved == NULL) + return psList; + psCurrent->psNext = psRemoved->psNext; + CPLFree( psRemoved ); + } + + return psList; +} + +/************************************************************************/ +/* CPLListDestroy() */ +/************************************************************************/ + +/** + * Destroy a list. Caller responsible for freeing data objects contained in + * list elements. + * + * @param psList pointer to list head. + * + */ + +void CPLListDestroy( CPLList *psList ) +{ + CPLList *psNext; + CPLList *psCurrent = psList; + + while ( psCurrent ) + { + psNext = psCurrent->psNext; + CPLFree( psCurrent ); + psCurrent = psNext; + } +} + +/************************************************************************/ +/* CPLListGetNext() */ +/************************************************************************/ + +/** + * Return the pointer to next element in a list. + * + * @param psElement pointer to list element. + * + * @return pointer to the list element preceded by the given element. + */ + +CPLList *CPLListGetNext( CPLList *psElement ) +{ + if ( psElement == NULL ) + return NULL; + else + return psElement->psNext; +} + +/************************************************************************/ +/* CPLListGetData() */ +/************************************************************************/ + +/** + * Return pointer to the data object contained in given list element. + * + * @param psElement pointer to list element. + * + * @return pointer to the data object contained in given list element. + */ + +void *CPLListGetData( CPLList *psElement ) +{ + if ( psElement == NULL ) + return NULL; + else + return psElement->pData; +} + diff --git a/cpl/cpl_list.h b/cpl/cpl_list.h new file mode 100644 index 0000000..be0f724 --- /dev/null +++ b/cpl/cpl_list.h @@ -0,0 +1,72 @@ +/********************************************************************** + * $Id: cpl_list.h 26927 2014-02-11 15:54:59Z goatbar $ + * + * Name: cpl_list.h + * Project: CPL - Common Portability Library + * Purpose: List functions. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ********************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_LIST_H_INCLUDED +#define _CPL_LIST_H_INCLUDED + +#include "cpl_port.h" + +/** + * \file cpl_list.h + * + * Simplest list implementation. List contains only pointers to stored + * objects, not objects itself. All operations regarding allocation and + * freeing memory for objects should be performed by the caller. + * + */ + +CPL_C_START + +/** List element structure. */ +typedef struct _CPLList +{ + /*! Pointer to the data object. Should be allocated and freed by the + * caller. + * */ + void *pData; + /*! Pointer to the next element in list. NULL, if current element is the + * last one. + */ + struct _CPLList *psNext; +} CPLList; + +CPLList CPL_DLL *CPLListAppend( CPLList *psList, void *pData ); +CPLList CPL_DLL *CPLListInsert( CPLList *psList, void *pData, int nPosition ); +CPLList CPL_DLL *CPLListGetLast( CPLList *psList ); +CPLList CPL_DLL *CPLListGet( CPLList *psList, int nPosition ); +int CPL_DLL CPLListCount( CPLList *psList ); +CPLList CPL_DLL *CPLListRemove( CPLList *psList, int nPosition ); +void CPL_DLL CPLListDestroy( CPLList *psList ); +CPLList CPL_DLL *CPLListGetNext( CPLList *psElement ); +void CPL_DLL *CPLListGetData( CPLList *psElement ); + +CPL_C_END + +#endif /* _CPL_LIST_H_INCLUDED */ diff --git a/cpl/cpl_minixml.cpp b/cpl/cpl_minixml.cpp index 47d1f83..721587b 100644 --- a/cpl/cpl_minixml.cpp +++ b/cpl/cpl_minixml.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_minixml.cpp 17930 2009-10-30 22:58:03Z rouault $ + * $Id: cpl_minixml.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Implementation of MiniXML Parser and handling. @@ -7,6 +7,7 @@ * ********************************************************************** * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -44,7 +45,7 @@ #include "cpl_string.h" #include -CPL_CVSID("$Id: cpl_minixml.cpp 17930 2009-10-30 22:58:03Z rouault $"); +CPL_CVSID("$Id: cpl_minixml.cpp 27044 2014-03-16 23:41:27Z rouault $"); typedef enum { TNone, @@ -83,6 +84,8 @@ typedef struct { CPLXMLNode *psLastNode; } ParseContext; +static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, + const char *pszText ); /************************************************************************/ /* ReadChar() */ @@ -127,28 +130,55 @@ static CPL_INLINE void UnreadChar( ParseContext *psContext, char chToUnread ) } /************************************************************************/ -/* AddToToken() */ +/* ReallocToken() */ /************************************************************************/ -static CPL_INLINE void AddToToken( ParseContext *psContext, char chNewChar ) - +static int ReallocToken( ParseContext *psContext ) { - if( psContext->pszToken == NULL ) + if (psContext->nTokenMaxSize > INT_MAX / 2) { - psContext->nTokenMaxSize = 10; - psContext->pszToken = (char *) CPLMalloc(psContext->nTokenMaxSize); + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d*2 bytes", (int)psContext->nTokenMaxSize); + VSIFree(psContext->pszToken); + psContext->pszToken = NULL; + return FALSE; } - else if( psContext->nTokenSize >= psContext->nTokenMaxSize - 2 ) + + psContext->nTokenMaxSize *= 2; + char* pszToken = (char *) + VSIRealloc(psContext->pszToken,psContext->nTokenMaxSize); + if (pszToken == NULL) { - psContext->nTokenMaxSize *= 2; - psContext->pszToken = (char *) - CPLRealloc(psContext->pszToken,psContext->nTokenMaxSize); + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d bytes", (int)psContext->nTokenMaxSize); + VSIFree(psContext->pszToken); + psContext->pszToken = NULL; + return FALSE; + } + psContext->pszToken = pszToken; + return TRUE; +} + +/************************************************************************/ +/* AddToToken() */ +/************************************************************************/ + +static CPL_INLINE int _AddToToken( ParseContext *psContext, char chNewChar ) + +{ + if( psContext->nTokenSize >= psContext->nTokenMaxSize - 2 ) + { + if (!ReallocToken(psContext)) + return FALSE; } psContext->pszToken[psContext->nTokenSize++] = chNewChar; psContext->pszToken[psContext->nTokenSize] = '\0'; + return TRUE; } +#define AddToToken(psContext, chNewChar) if (!_AddToToken(psContext, chNewChar)) goto fail; + /************************************************************************/ /* ReadToken() */ /************************************************************************/ @@ -223,9 +253,11 @@ static XMLTokenType ReadToken( ParseContext *psContext ) do { chNext = ReadChar( psContext ); + if (chNext == ']') + break; AddToToken( psContext, chNext ); } - while( chNext != ']' && chNext != '\0' + while( chNext != '\0' && !EQUALN(psContext->pszInput+psContext->nInputOffset,"]>", 2) ); if (chNext == '\0') @@ -237,11 +269,14 @@ static XMLTokenType ReadToken( ParseContext *psContext ) break; } - chNext = ReadChar( psContext ); - AddToToken( psContext, chNext ); + if (chNext != ']') + { + chNext = ReadChar( psContext ); + AddToToken( psContext, chNext ); - // Skip ">" character, will be consumed below - chNext = ReadChar( psContext ); + // Skip ">" character, will be consumed below + chNext = ReadChar( psContext ); + } } @@ -444,26 +479,49 @@ static XMLTokenType ReadToken( ParseContext *psContext ) } return psContext->eTokenType; + +fail: + psContext->eTokenType = TNone; + return TNone; } - + /************************************************************************/ /* PushNode() */ /************************************************************************/ -static void PushNode( ParseContext *psContext, CPLXMLNode *psNode ) +static int PushNode( ParseContext *psContext, CPLXMLNode *psNode ) { if( psContext->nStackMaxSize <= psContext->nStackSize ) { psContext->nStackMaxSize += 10; - psContext->papsStack = (StackContext *) - CPLRealloc(psContext->papsStack, - sizeof(StackContext) * psContext->nStackMaxSize); + + if (psContext->nStackMaxSize >= (int)(INT_MAX / sizeof(StackContext))) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d*%d bytes", (int)sizeof(StackContext), psContext->nStackMaxSize); + VSIFree(psContext->papsStack); + psContext->papsStack = NULL; + return FALSE; + } + StackContext* papsStack; + papsStack = (StackContext *)VSIRealloc(psContext->papsStack, + sizeof(StackContext) * psContext->nStackMaxSize); + if (papsStack == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d bytes", (int)(sizeof(StackContext) * psContext->nStackMaxSize)); + VSIFree(psContext->papsStack); + psContext->papsStack = NULL; + return FALSE; + } + psContext->papsStack = papsStack; } psContext->papsStack[psContext->nStackSize].psFirstNode = psNode; psContext->papsStack[psContext->nStackSize].psLastChild = NULL; psContext->nStackSize ++; + return TRUE; } /************************************************************************/ @@ -547,8 +605,10 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) sContext.nInputOffset = 0; sContext.nInputLine = 0; sContext.bInElement = FALSE; - sContext.pszToken = NULL; - sContext.nTokenMaxSize = 0; + sContext.nTokenMaxSize = 10; + sContext.pszToken = (char *) VSIMalloc(sContext.nTokenMaxSize); + if (sContext.pszToken == NULL) + return NULL; sContext.nTokenSize = 0; sContext.eTokenType = TNone; sContext.nStackMaxSize = 0; @@ -557,9 +617,6 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) sContext.psFirstNode = NULL; sContext.psLastNode = NULL; - /* ensure token is initialized */ - AddToToken( &sContext, ' ' ); - /* ==================================================================== */ /* Loop reading tokens. */ /* ==================================================================== */ @@ -582,10 +639,12 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) if( sContext.pszToken[0] != '/' ) { - psElement = CPLCreateXMLNode( NULL, CXT_Element, + psElement = _CPLCreateXMLNode( NULL, CXT_Element, sContext.pszToken ); + if (!psElement) break; AttachNode( &sContext, psElement ); - PushNode( &sContext, psElement ); + if (!PushNode( &sContext, psElement )) + break; } else { @@ -601,6 +660,20 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) } else { + if (strcmp(sContext.pszToken+1, + sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue) != 0) + { + /* TODO: at some point we could just error out like any other */ + /* sane XML parser would do */ + CPLError( CE_Warning, CPLE_AppDefined, + "Line %d: <%.500s> matches <%.500s>, but the case isn't the same. " + "Going on, but this is invalid XML that might be rejected in " + "future versions.", + sContext.nInputLine, + sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue, + sContext.pszToken ); + } + if( ReadToken(&sContext) != TClose ) { CPLError( CE_Failure, CPLE_AppDefined, @@ -623,7 +696,8 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) { CPLXMLNode *psAttr; - psAttr = CPLCreateXMLNode(NULL, CXT_Attribute, sContext.pszToken); + psAttr = _CPLCreateXMLNode(NULL, CXT_Attribute, sContext.pszToken); + if (!psAttr) break; AttachNode( &sContext, psAttr ); if( ReadToken(&sContext) != TEqual ) @@ -634,8 +708,17 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) break; } - if( ReadToken(&sContext) != TString - && sContext.eTokenType != TToken ) + if( ReadToken(&sContext) == TToken ) + { + /* TODO: at some point we could just error out like any other */ + /* sane XML parser would do */ + CPLError( CE_Warning, CPLE_AppDefined, + "Line %d: Attribute value should be single or double quoted. " + "Going on, but this is invalid XML that might be rejected in " + "future versions.", + sContext.nInputLine ); + } + else if( sContext.eTokenType != TString ) { CPLError( CE_Failure, CPLE_AppDefined, "Line %d: Didn't find expected attribute value.", @@ -643,7 +726,7 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) break; } - CPLCreateXMLNode( psAttr, CXT_Text, sContext.pszToken ); + if (!_CPLCreateXMLNode( psAttr, CXT_Text, sContext.pszToken )) break; } /* -------------------------------------------------------------------- */ @@ -710,7 +793,8 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) { CPLXMLNode *psValue; - psValue = CPLCreateXMLNode(NULL, CXT_Comment, sContext.pszToken); + psValue = _CPLCreateXMLNode(NULL, CXT_Comment, sContext.pszToken); + if (!psValue) break; AttachNode( &sContext, psValue ); } @@ -721,7 +805,8 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) { CPLXMLNode *psValue; - psValue = CPLCreateXMLNode(NULL, CXT_Literal, sContext.pszToken); + psValue = _CPLCreateXMLNode(NULL, CXT_Literal, sContext.pszToken); + if (!psValue) break; AttachNode( &sContext, psValue ); } @@ -732,7 +817,8 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) { CPLXMLNode *psValue; - psValue = CPLCreateXMLNode(NULL, CXT_Text, sContext.pszToken); + psValue = _CPLCreateXMLNode(NULL, CXT_Text, sContext.pszToken); + if (!psValue) break; AttachNode( &sContext, psValue ); } /* -------------------------------------------------------------------- */ @@ -750,7 +836,7 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) /* -------------------------------------------------------------------- */ /* Did we pop all the way out of our stack? */ /* -------------------------------------------------------------------- */ - if( CPLGetLastErrorType() == CE_None && sContext.nStackSize != 0 ) + if( CPLGetLastErrorType() != CE_Failure && sContext.nStackSize != 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "Parse error at EOF, not all elements have been closed,\n" @@ -765,7 +851,7 @@ CPLXMLNode *CPLParseXMLString( const char *pszString ) if( sContext.papsStack != NULL ) CPLFree( sContext.papsStack ); - if( CPLGetLastErrorType() != CE_None ) + if( CPLGetLastErrorType() == CE_Failure ) { CPLDestroyXMLNode( sContext.psFirstNode ); sContext.psFirstNode = NULL; @@ -795,7 +881,7 @@ static void _GrowBuffer( size_t nNeeded, /************************************************************************/ static void -CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, +CPLSerializeXMLNode( const CPLXMLNode *psNode, int nIndent, char **ppszText, unsigned int *pnLength, unsigned int *pnMaxLength ) @@ -816,7 +902,7 @@ CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, /* -------------------------------------------------------------------- */ if( psNode->eType == CXT_Text ) { - char *pszEscaped = CPLEscapeString( psNode->pszValue, -1, CPLES_XML ); + char *pszEscaped = CPLEscapeString( psNode->pszValue, -1, CPLES_XML_BUT_QUOTES ); CPLAssert( psNode->psChild == NULL ); @@ -837,8 +923,18 @@ CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, && psNode->psChild->eType == CXT_Text ); sprintf( *ppszText + *pnLength, " %s=\"", psNode->pszValue ); - CPLSerializeXMLNode( psNode->psChild, 0, ppszText, - pnLength, pnMaxLength ); + *pnLength += strlen(*ppszText + *pnLength); + + char *pszEscaped = CPLEscapeString( psNode->psChild->pszValue, -1, CPLES_XML ); + + _GrowBuffer( strlen(pszEscaped) + *pnLength, + ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, pszEscaped ); + + CPLFree( pszEscaped ); + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( 3 + *pnLength, ppszText, pnMaxLength ); strcat( *ppszText + *pnLength, "\"" ); } @@ -903,10 +999,13 @@ CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, if( !bHasNonAttributeChildren ) { + _GrowBuffer( *pnLength + 40, + ppszText, pnMaxLength ); + if( psNode->pszValue[0] == '?' ) strcat( *ppszText + *pnLength, "?>\n" ); else - strcat( *ppszText + *pnLength, "/>\n" ); + strcat( *ppszText + *pnLength, " />\n" ); } else { @@ -961,17 +1060,17 @@ CPLSerializeXMLNode( CPLXMLNode *psNode, int nIndent, * document becomes owned by the caller and should be freed with CPLFree() * when no longer needed. * - * @param psNode + * @param psNode the node to serialize. * * @return the document on success or NULL on failure. */ -char *CPLSerializeXMLTree( CPLXMLNode *psNode ) +char *CPLSerializeXMLTree( const CPLXMLNode *psNode ) { unsigned int nMaxLength = 100, nLength = 0; char *pszText = NULL; - CPLXMLNode *psThis; + const CPLXMLNode *psThis; pszText = (char *) CPLMalloc(nMaxLength); pszText[0] = '\0'; @@ -1035,6 +1134,59 @@ CPLXMLNode *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, return psNode; } +/************************************************************************/ +/* _CPLCreateXMLNode() */ +/************************************************************************/ + +/* Same as CPLCreateXMLNode() but can return NULL in case of out-of-memory */ +/* situation */ + +static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, + const char *pszText ) + +{ + CPLXMLNode *psNode; + +/* -------------------------------------------------------------------- */ +/* Create new node. */ +/* -------------------------------------------------------------------- */ + psNode = (CPLXMLNode *) VSICalloc(sizeof(CPLXMLNode),1); + if (psNode == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate CPLXMLNode"); + return NULL; + } + + psNode->eType = eType; + psNode->pszValue = VSIStrdup( pszText ); + if (psNode->pszValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate psNode->pszValue"); + VSIFree(psNode); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Attach to parent, if provided. */ +/* -------------------------------------------------------------------- */ + if( poParent != NULL ) + { + if( poParent->psChild == NULL ) + poParent->psChild = psNode; + else + { + CPLXMLNode *psLink = poParent->psChild; + + while( psLink->psNext != NULL ) + psLink = psLink->psNext; + + psLink->psNext = psNode; + } + } + + return psNode; +} + /************************************************************************/ /* CPLDestroyXMLNode() */ /************************************************************************/ @@ -1051,17 +1203,32 @@ CPLXMLNode *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, void CPLDestroyXMLNode( CPLXMLNode *psNode ) { - if( psNode == NULL ) - return; + while(psNode != NULL) + { + if( psNode->pszValue != NULL ) + CPLFree( psNode->pszValue ); - if( psNode->psChild != NULL ) - CPLDestroyXMLNode( psNode->psChild ); - - if( psNode->psNext != NULL ) - CPLDestroyXMLNode( psNode->psNext ); + if( psNode->psChild != NULL ) + { + CPLXMLNode* psNext = psNode->psNext; + psNode->psNext = psNode->psChild; + /* Move the child and its siblings as the next */ + /* siblings of the current node */ + if (psNext != NULL) + { + CPLXMLNode* psIter = psNode->psChild; + while(psIter->psNext != NULL) + psIter = psIter->psNext; + psIter->psNext = psNext; + } + } + + CPLXMLNode* psNext = psNode->psNext; + + CPLFree( psNode ); - CPLFree( psNode->pszValue ); - CPLFree( psNode ); + psNode = psNext; + } } /************************************************************************/ @@ -1372,7 +1539,7 @@ void CPLAddXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild ) } /************************************************************************/ -/* CPLAddXMLChild() */ +/* CPLRemoveXMLChild() */ /************************************************************************/ /** @@ -1656,47 +1823,46 @@ void CPLStripXMLNamespace( CPLXMLNode *psRoot, int bRecurse ) { - if( psRoot == NULL ) - return; + size_t nNameSpaceLen = (pszNamespace) ? strlen(pszNamespace) : 0; - if( psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute ) + while( psRoot != NULL ) { - if( pszNamespace != NULL ) + + if( psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute ) { - if( EQUALN(pszNamespace,psRoot->pszValue,strlen(pszNamespace)) - && psRoot->pszValue[strlen(pszNamespace)] == ':' ) + if( pszNamespace != NULL ) { - char *pszNewValue = - CPLStrdup(psRoot->pszValue+strlen(pszNamespace)+1); - - CPLFree( psRoot->pszValue ); - psRoot->pszValue = pszNewValue; + if( EQUALN(pszNamespace,psRoot->pszValue,nNameSpaceLen) + && psRoot->pszValue[nNameSpaceLen] == ':' ) + { + memmove(psRoot->pszValue, psRoot->pszValue+nNameSpaceLen+1, + strlen(psRoot->pszValue+nNameSpaceLen+1) + 1); + } } - } - else - { - const char *pszCheck; - - for( pszCheck = psRoot->pszValue; *pszCheck != '\0'; pszCheck++ ) + else { - if( *pszCheck == ':' ) + const char *pszCheck; + + for( pszCheck = psRoot->pszValue; *pszCheck != '\0'; pszCheck++ ) { - char *pszNewValue = CPLStrdup( pszCheck+1 ); - - CPLFree( psRoot->pszValue ); - psRoot->pszValue = pszNewValue; - break; + if( *pszCheck == ':' ) + { + memmove(psRoot->pszValue, pszCheck + 1, strlen(pszCheck + 1) + 1); + break; + } } } } - } - if( bRecurse ) - { - if( psRoot->psChild != NULL ) - CPLStripXMLNamespace( psRoot->psChild, pszNamespace, 1 ); - if( psRoot->psNext != NULL ) - CPLStripXMLNamespace( psRoot->psNext, pszNamespace, 1 ); + if( bRecurse ) + { + if( psRoot->psChild != NULL ) + CPLStripXMLNamespace( psRoot->psChild, pszNamespace, 1 ); + + psRoot = psRoot->psNext; + } + else + break; } } @@ -1722,46 +1888,16 @@ void CPLStripXMLNamespace( CPLXMLNode *psRoot, CPLXMLNode *CPLParseXMLFile( const char *pszFilename ) { - FILE *fp; - vsi_l_offset nLen; + GByte *pabyOut = NULL; char *pszDoc; CPLXMLNode *psTree; /* -------------------------------------------------------------------- */ -/* Read the file. */ +/* Ingest the file. */ /* -------------------------------------------------------------------- */ - fp = VSIFOpenL( pszFilename, "rb" ); - if( fp == NULL ) - { - CPLError( CE_Failure, CPLE_OpenFailed, - "Failed to open %.500s to read.", pszFilename ); - return NULL; - } - - VSIFSeekL( fp, 0, SEEK_END ); - nLen = VSIFTellL( fp ); - VSIFSeekL( fp, 0, SEEK_SET ); - - pszDoc = (char *) VSIMalloc((size_t)nLen + 1); - if( pszDoc == NULL ) - { - CPLError( CE_Failure, CPLE_OutOfMemory, - "Out of memory allocating space for %d byte buffer in\n" - "CPLParseXMLFile(%.500s).", - (int)nLen+1, pszFilename ); - VSIFCloseL( fp ); + if( !VSIIngestFile( NULL, pszFilename, &pabyOut, NULL, -1 ) ) return NULL; - } - if( VSIFReadL( pszDoc, 1, (size_t)nLen, fp ) < nLen ) - { - CPLError( CE_Failure, CPLE_FileIO, - "VSIFRead() result short of expected %d bytes from %.500s.", - (int)nLen, pszFilename ); - pszDoc[0] = '\0'; - } - VSIFCloseL( fp ); - - pszDoc[nLen] = '\0'; + pszDoc = (char*) pabyOut; /* -------------------------------------------------------------------- */ /* Parse it. */ @@ -1789,11 +1925,11 @@ CPLXMLNode *CPLParseXMLFile( const char *pszFilename ) * @return TRUE on success, FALSE otherwise. */ -int CPLSerializeXMLTreeToFile( CPLXMLNode *psTree, const char *pszFilename ) +int CPLSerializeXMLTreeToFile( const CPLXMLNode *psTree, const char *pszFilename ) { char *pszDoc; - FILE *fp; + VSILFILE *fp; vsi_l_offset nLength; /* -------------------------------------------------------------------- */ diff --git a/cpl/cpl_minixml.h b/cpl/cpl_minixml.h index ed84cfc..ed27453 100644 --- a/cpl/cpl_minixml.h +++ b/cpl/cpl_minixml.h @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_minixml.h 15686 2008-11-06 18:46:11Z rouault $ + * $Id: cpl_minixml.h 25119 2012-10-13 22:38:43Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Declarations for MiniXML Handler. @@ -130,7 +130,7 @@ const char CPL_DLL *CPLGetXMLValue( CPLXMLNode *poRoot, CPLXMLNode CPL_DLL *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, const char *pszText ); -char CPL_DLL *CPLSerializeXMLTree( CPLXMLNode *psNode ); +char CPL_DLL *CPLSerializeXMLTree( const CPLXMLNode *psNode ); void CPL_DLL CPLAddXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild ); int CPL_DLL CPLRemoveXMLChild( CPLXMLNode *psParent, @@ -149,7 +149,7 @@ void CPL_DLL CPLStripXMLNamespace( CPLXMLNode *psRoot, void CPL_DLL CPLCleanXMLElementName( char * ); CPLXMLNode CPL_DLL *CPLParseXMLFile( const char *pszFilename ); -int CPL_DLL CPLSerializeXMLTreeToFile( CPLXMLNode *psTree, +int CPL_DLL CPLSerializeXMLTreeToFile( const CPLXMLNode *psTree, const char *pszFilename ); CPL_C_END diff --git a/cpl/cpl_multiproc.cpp b/cpl/cpl_multiproc.cpp index 227c1f1..36dc060 100644 --- a/cpl/cpl_multiproc.cpp +++ b/cpl/cpl_multiproc.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_multiproc.cpp 18652 2010-01-24 14:44:08Z rouault $ + * $Id: cpl_multiproc.cpp 27438 2014-06-07 20:16:11Z rouault $ * * Project: CPL - Common Portability Library * Purpose: CPL Multi-Threading, and process handling portability functions. @@ -7,6 +7,7 @@ * ********************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -40,12 +41,16 @@ # include #endif -CPL_CVSID("$Id: cpl_multiproc.cpp 18652 2010-01-24 14:44:08Z rouault $"); +CPL_CVSID("$Id: cpl_multiproc.cpp 27438 2014-06-07 20:16:11Z rouault $"); #if defined(CPL_MULTIPROC_STUB) && !defined(DEBUG) # define MUTEX_NONE #endif +/* We don't want it to be publicly used since it solves rather tricky issues */ +/* that are better to remain hidden... */ +void CPLFinalizeTLS(); + /************************************************************************/ /* CPLMutexHolder() */ /************************************************************************/ @@ -60,20 +65,27 @@ CPLMutexHolder::CPLMutexHolder( void **phMutex, double dfWaitInSeconds, nLine = nLineIn; #ifdef DEBUG_MUTEX - CPLDebug( "MH", "Request %p for pid %ld at %d/%s", - *phMutex, (long) CPLGetPID(), nLine, pszFile ); + /* + * XXX: There is no way to use CPLDebug() here because it works with + * mutexes itself so we will fall in infinite recursion. Good old + * fprintf() will do the job right. + */ + fprintf( stderr, + "CPLMutexHolder: Request %p for pid %ld at %d/%s.\n", + *phMutex, (long) CPLGetPID(), nLine, pszFile ); #endif if( !CPLCreateOrAcquireMutex( phMutex, dfWaitInSeconds ) ) { - CPLDebug( "CPLMutexHolder", "failed to acquire mutex!" ); + fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" ); hMutex = NULL; } else { #ifdef DEBUG_MUTEX - CPLDebug( "MH", "Acquired %p for pid %ld at %d/%s", - *phMutex, (long) CPLGetPID(), nLine, pszFile ); + fprintf( stderr, + "CPLMutexHolder: Acquired %p for pid %ld at %d/%s.\n", + *phMutex, (long) CPLGetPID(), nLine, pszFile ); #endif hMutex = *phMutex; @@ -81,6 +93,29 @@ CPLMutexHolder::CPLMutexHolder( void **phMutex, double dfWaitInSeconds, #endif /* ndef MUTEX_NONE */ } +/************************************************************************/ +/* CPLMutexHolder() */ +/************************************************************************/ + +CPLMutexHolder::CPLMutexHolder( void *hMutexIn, double dfWaitInSeconds, + const char *pszFileIn, + int nLineIn ) + +{ +#ifndef MUTEX_NONE + pszFile = pszFileIn; + nLine = nLineIn; + hMutex = hMutexIn; + + if( hMutex != NULL && + !CPLAcquireMutex( hMutex, dfWaitInSeconds ) ) + { + fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" ); + hMutex = NULL; + } +#endif /* ndef MUTEX_NONE */ +} + /************************************************************************/ /* ~CPLMutexHolder() */ /************************************************************************/ @@ -92,8 +127,9 @@ CPLMutexHolder::~CPLMutexHolder() if( hMutex != NULL ) { #ifdef DEBUG_MUTEX - CPLDebug( "MH", "Release %p for pid %ld at %d/%s", - hMutex, (long) CPLGetPID(), nLine, pszFile ); + fprintf( stderr, + "~CPLMutexHolder: Release %p for pid %ld at %d/%s.\n", + hMutex, (long) CPLGetPID(), nLine, pszFile ); #endif CPLReleaseMutex( hMutex ); } @@ -105,13 +141,18 @@ CPLMutexHolder::~CPLMutexHolder() /* CPLCreateOrAcquireMutex() */ /************************************************************************/ +#ifndef CPL_MULTIPROC_PTHREAD + +#ifndef MUTEX_NONE +static void *hCOAMutex = NULL; +#endif + int CPLCreateOrAcquireMutex( void **phMutex, double dfWaitInSeconds ) { int bSuccess = FALSE; #ifndef MUTEX_NONE - static void *hCOAMutex = NULL; /* ** ironically, creation of this initial mutex is not threadsafe @@ -121,6 +162,11 @@ int CPLCreateOrAcquireMutex( void **phMutex, double dfWaitInSeconds ) if( hCOAMutex == NULL ) { hCOAMutex = CPLCreateMutex(); + if (hCOAMutex == NULL) + { + *phMutex = NULL; + return FALSE; + } } else { @@ -130,8 +176,8 @@ int CPLCreateOrAcquireMutex( void **phMutex, double dfWaitInSeconds ) if( *phMutex == NULL ) { *phMutex = CPLCreateMutex(); + bSuccess = *phMutex != NULL; CPLReleaseMutex( hCOAMutex ); - bSuccess = TRUE; } else { @@ -143,6 +189,24 @@ int CPLCreateOrAcquireMutex( void **phMutex, double dfWaitInSeconds ) return bSuccess; } +#endif + +/************************************************************************/ +/* CPLCleanupMasterMutex() */ +/************************************************************************/ + +void CPLCleanupMasterMutex() +{ +#ifndef CPL_MULTIPROC_PTHREAD +#ifndef MUTEX_NONE + if( hCOAMutex != NULL ) + { + CPLDestroyMutex( hCOAMutex ); + hCOAMutex = NULL; + } +#endif +#endif +} /************************************************************************/ /* CPLCleanupTLSList() */ @@ -165,14 +229,16 @@ static void CPLCleanupTLSList( void **papTLSList ) { if( papTLSList[i] != NULL && papTLSList[i+CTLS_MAX] != NULL ) { - CPLFree( papTLSList[i] ); + CPLTLSFreeFunc pfnFree = (CPLTLSFreeFunc) papTLSList[i + CTLS_MAX]; + pfnFree( papTLSList[i] ); + papTLSList[i] = NULL; } } CPLFree( papTLSList ); } -#ifdef CPL_MULTIPROC_STUB +#if defined(CPL_MULTIPROC_STUB) /************************************************************************/ /* ==================================================================== */ /* CPL_MULTIPROC_STUB */ @@ -183,6 +249,15 @@ static void CPLCleanupTLSList( void **papTLSList ) /* ==================================================================== */ /************************************************************************/ +/************************************************************************/ +/* CPLGetNumCPUs() */ +/************************************************************************/ + +int CPLGetNumCPUs() +{ + return 1; +} + /************************************************************************/ /* CPLGetThreadingModel() */ /************************************************************************/ @@ -201,7 +276,7 @@ void *CPLCreateMutex() { #ifndef MUTEX_NONE - unsigned char *pabyMutex = (unsigned char *) CPLMalloc( 4 ); + unsigned char *pabyMutex = (unsigned char *) malloc( 4 ); pabyMutex[0] = 1; pabyMutex[1] = 'r'; @@ -270,10 +345,51 @@ void CPLDestroyMutex( void *hMutex ) CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && pabyMutex[3] == 'd' ); - CPLFree( pabyMutex ); + free( pabyMutex ); #endif } +/************************************************************************/ +/* CPLCreateCond() */ +/************************************************************************/ + +void *CPLCreateCond() +{ + return NULL; +} + +/************************************************************************/ +/* CPLCondWait() */ +/************************************************************************/ + +void CPLCondWait( void *hCond, void* hMutex ) +{ +} + +/************************************************************************/ +/* CPLCondSignal() */ +/************************************************************************/ + +void CPLCondSignal( void *hCond ) +{ +} + +/************************************************************************/ +/* CPLCondBroadcast() */ +/************************************************************************/ + +void CPLCondBroadcast( void *hCond ) +{ +} + +/************************************************************************/ +/* CPLDestroyCond() */ +/************************************************************************/ + +void CPLDestroyCond( void *hCond ) +{ +} + /************************************************************************/ /* CPLLockFile() */ /* */ @@ -373,6 +489,26 @@ int CPLCreateThread( CPLThreadFunc pfnMain, void *pArg ) return -1; } +/************************************************************************/ +/* CPLCreateJoinableThread() */ +/************************************************************************/ + +void* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + CPLDebug( "CPLCreateJoinableThread", "Fails to dummy implementation" ); + + return NULL; +} + +/************************************************************************/ +/* CPLJoinThread() */ +/************************************************************************/ + +void CPLJoinThread(void* hJoinableThread) +{ +} + /************************************************************************/ /* CPLSleep() */ /************************************************************************/ @@ -403,11 +539,24 @@ static void **CPLGetTLSList() { if( papTLSList == NULL ) - papTLSList = (void **) CPLCalloc(sizeof(void*),CTLS_MAX*2); + { + papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2); + if( papTLSList == NULL ) + CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); + } return papTLSList; } +/************************************************************************/ +/* CPLFinalizeTLS() */ +/************************************************************************/ + +void CPLFinalizeTLS() +{ + CPLCleanupTLS(); +} + /************************************************************************/ /* CPLCleanupTLS() */ /************************************************************************/ @@ -419,9 +568,9 @@ void CPLCleanupTLS() papTLSList = NULL; } -#endif /* def CPL_MULTIPROC_STUB */ +/* endif CPL_MULTIPROC_STUB */ -#if defined(CPL_MULTIPROC_WIN32) +#elif defined(CPL_MULTIPROC_WIN32) /************************************************************************/ @@ -432,6 +581,9 @@ void CPLCleanupTLS() /* ==================================================================== */ /************************************************************************/ +/* InitializeCriticalSectionAndSpinCount requires _WIN32_WINNT >= 0x403 */ +#define _WIN32_WINNT 0x0500 + #include /* windows.h header must be included above following lines. */ @@ -440,6 +592,19 @@ void CPLCleanupTLS() # define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) #endif +/************************************************************************/ +/* CPLGetNumCPUs() */ +/************************************************************************/ + +int CPLGetNumCPUs() +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + DWORD dwNum = info.dwNumberOfProcessors; + if( dwNum < 1 ) + return 1; + return (int)dwNum; +} /************************************************************************/ /* CPLGetThreadingModel() */ @@ -458,11 +623,26 @@ const char *CPLGetThreadingModel() void *CPLCreateMutex() { +#ifdef USE_WIN32_MUTEX HANDLE hMutex; hMutex = CreateMutex( NULL, TRUE, NULL ); return (void *) hMutex; +#else + CRITICAL_SECTION *pcs; + + /* Do not use CPLMalloc() since its debugging infrastructure */ + /* can call the CPL*Mutex functions... */ + pcs = (CRITICAL_SECTION *)malloc(sizeof(*pcs)); + if( pcs ) + { + InitializeCriticalSectionAndSpinCount(pcs, 4000); + EnterCriticalSection(pcs); + } + + return (void *) pcs; +#endif } /************************************************************************/ @@ -472,12 +652,25 @@ void *CPLCreateMutex() int CPLAcquireMutex( void *hMutexIn, double dfWaitInSeconds ) { +#ifdef USE_WIN32_MUTEX HANDLE hMutex = (HANDLE) hMutexIn; DWORD hr; hr = WaitForSingleObject( hMutex, (int) (dfWaitInSeconds * 1000) ); return hr != WAIT_TIMEOUT; +#else + CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)hMutexIn; + BOOL ret; + + while( (ret = TryEnterCriticalSection(pcs)) == 0 && dfWaitInSeconds > 0.0 ) + { + CPLSleep( MIN(dfWaitInSeconds,0.125) ); + dfWaitInSeconds -= 0.125; + } + + return ret; +#endif } /************************************************************************/ @@ -487,9 +680,15 @@ int CPLAcquireMutex( void *hMutexIn, double dfWaitInSeconds ) void CPLReleaseMutex( void *hMutexIn ) { +#ifdef USE_WIN32_MUTEX HANDLE hMutex = (HANDLE) hMutexIn; ReleaseMutex( hMutex ); +#else + CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)hMutexIn; + + LeaveCriticalSection(pcs); +#endif } /************************************************************************/ @@ -499,9 +698,157 @@ void CPLReleaseMutex( void *hMutexIn ) void CPLDestroyMutex( void *hMutexIn ) { +#ifdef USE_WIN32_MUTEX HANDLE hMutex = (HANDLE) hMutexIn; CloseHandle( hMutex ); +#else + CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)hMutexIn; + + DeleteCriticalSection( pcs ); + free( pcs ); +#endif +} + +/************************************************************************/ +/* CPLCreateCond() */ +/************************************************************************/ + +struct _WaiterItem +{ + HANDLE hEvent; + struct _WaiterItem* psNext; +}; +typedef struct _WaiterItem WaiterItem; + +typedef struct +{ + void *hInternalMutex; + WaiterItem *psWaiterList; +} Win32Cond; + +void *CPLCreateCond() +{ + Win32Cond* psCond = (Win32Cond*) malloc(sizeof(Win32Cond)); + if (psCond == NULL) + return NULL; + psCond->hInternalMutex = CPLCreateMutex(); + if (psCond->hInternalMutex == NULL) + { + free(psCond); + return NULL; + } + CPLReleaseMutex(psCond->hInternalMutex); + psCond->psWaiterList = NULL; + return psCond; +} + +/************************************************************************/ +/* CPLCondWait() */ +/************************************************************************/ + +static void CPLTLSFreeEvent(void* pData) +{ + CloseHandle((HANDLE)pData); +} + +void CPLCondWait( void *hCond, void* hClientMutex ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + + HANDLE hEvent = (HANDLE) CPLGetTLS(CTLS_WIN32_COND); + if (hEvent == NULL) + { + hEvent = CreateEvent(NULL, /* security attributes */ + 0, /* manual reset = no */ + 0, /* initial state = unsignaled */ + NULL /* no name */); + CPLAssert(hEvent != NULL); + + CPLSetTLSWithFreeFunc(CTLS_WIN32_COND, hEvent, CPLTLSFreeEvent); + } + + /* Insert the waiter into the waiter list of the condition */ + CPLAcquireMutex(psCond->hInternalMutex, 1000.0); + + WaiterItem* psItem = (WaiterItem*)malloc(sizeof(WaiterItem)); + CPLAssert(psItem != NULL); + + psItem->hEvent = hEvent; + psItem->psNext = psCond->psWaiterList; + + psCond->psWaiterList = psItem; + + CPLReleaseMutex(psCond->hInternalMutex); + + /* Release the client mutex before waiting for the event being signaled */ + CPLReleaseMutex(hClientMutex); + + // Ideally we would check that we do not get WAIT_FAILED but it is hard + // to report a failure. + WaitForSingleObject(hEvent, INFINITE); + + /* Reacquire the client mutex */ + CPLAcquireMutex(hClientMutex, 1000.0); +} + +/************************************************************************/ +/* CPLCondSignal() */ +/************************************************************************/ + +void CPLCondSignal( void *hCond ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + + /* Signal the first registered event, and remove it from the list */ + CPLAcquireMutex(psCond->hInternalMutex, 1000.0); + + WaiterItem* psIter = psCond->psWaiterList; + if (psIter != NULL) + { + SetEvent(psIter->hEvent); + psCond->psWaiterList = psIter->psNext; + free(psIter); + } + + CPLReleaseMutex(psCond->hInternalMutex); +} + +/************************************************************************/ +/* CPLCondBroadcast() */ +/************************************************************************/ + +void CPLCondBroadcast( void *hCond ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + + /* Signal all the registered events, and remove them from the list */ + CPLAcquireMutex(psCond->hInternalMutex, 1000.0); + + WaiterItem* psIter = psCond->psWaiterList; + while (psIter != NULL) + { + WaiterItem* psNext = psIter->psNext; + SetEvent(psIter->hEvent); + free(psIter); + psIter = psNext; + } + psCond->psWaiterList = NULL; + + CPLReleaseMutex(psCond->hInternalMutex); +} + +/************************************************************************/ +/* CPLDestroyCond() */ +/************************************************************************/ + +void CPLDestroyCond( void *hCond ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + CPLDestroyMutex(psCond->hInternalMutex); + psCond->hInternalMutex = NULL; + CPLAssert(psCond->psWaiterList == NULL); + free(psCond); } /************************************************************************/ @@ -577,6 +924,7 @@ GIntBig CPLGetPID() typedef struct { void *pAppData; CPLThreadFunc pfnMain; + HANDLE hThread; } CPLStdCallThreadInfo; static DWORD WINAPI CPLStdCallThreadJacket( void *pData ) @@ -586,8 +934,9 @@ static DWORD WINAPI CPLStdCallThreadJacket( void *pData ) psInfo->pfnMain( psInfo->pAppData ); - CPLFree( psInfo ); - + if (psInfo->hThread == NULL) + CPLFree( psInfo ); /* Only for detached threads */ + CPLCleanupTLS(); return 0; @@ -611,6 +960,7 @@ int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); psInfo->pAppData = pThreadArg; psInfo->pfnMain = pfnMain; + psInfo->hThread = NULL; hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo, 0, &nThreadId ); @@ -623,6 +973,44 @@ int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) return nThreadId; } +/************************************************************************/ +/* CPLCreateJoinableThread() */ +/************************************************************************/ + +void* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + HANDLE hThread; + DWORD nThreadId; + CPLStdCallThreadInfo *psInfo; + + psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); + psInfo->pAppData = pThreadArg; + psInfo->pfnMain = pfnMain; + + hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo, + 0, &nThreadId ); + + if( hThread == NULL ) + return NULL; + + psInfo->hThread = hThread; + return psInfo; +} + +/************************************************************************/ +/* CPLJoinThread() */ +/************************************************************************/ + +void CPLJoinThread(void* hJoinableThread) +{ + CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) hJoinableThread; + + WaitForSingleObject(psInfo->hThread, INFINITE); + CloseHandle( psInfo->hThread ); + CPLFree( psInfo ); +} + /************************************************************************/ /* CPLSleep() */ /************************************************************************/ @@ -650,8 +1038,7 @@ static void **CPLGetTLSList() nTLSKey = TlsAlloc(); if( nTLSKey == TLS_OUT_OF_INDEXES ) { - CPLError( CE_Fatal, CPLE_AppDefined, - "TlsAlloc() failed!" ); + CPLEmergencyError( "CPLGetTLSList(): TlsAlloc() failed!" ); } bTLSKeySetup = TRUE; } @@ -659,17 +1046,27 @@ static void **CPLGetTLSList() papTLSList = (void **) TlsGetValue( nTLSKey ); if( papTLSList == NULL ) { - papTLSList = (void **) CPLCalloc(sizeof(void*),CTLS_MAX*2); + papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2); + if( papTLSList == NULL ) + CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); if( TlsSetValue( nTLSKey, papTLSList ) == 0 ) { - CPLError( CE_Fatal, CPLE_AppDefined, - "TlsSetValue() failed!" ); + CPLEmergencyError( "CPLGetTLSList(): TlsSetValue() failed!" ); } } return papTLSList; } +/************************************************************************/ +/* CPLFinalizeTLS() */ +/************************************************************************/ + +void CPLFinalizeTLS() +{ + CPLCleanupTLS(); +} + /************************************************************************/ /* CPLCleanupTLS() */ /************************************************************************/ @@ -691,11 +1088,13 @@ void CPLCleanupTLS() CPLCleanupTLSList( papTLSList ); } -#endif /* def CPL_MULTIPROC_WIN32 */ +/* endif CPL_MULTIPROC_WIN32 */ + +#elif defined(CPL_MULTIPROC_PTHREAD) -#ifdef CPL_MULTIPROC_PTHREAD #include #include +#include /************************************************************************/ /* ==================================================================== */ @@ -705,6 +1104,47 @@ void CPLCleanupTLS() /* ==================================================================== */ /************************************************************************/ +/************************************************************************/ +/* CPLGetNumCPUs() */ +/************************************************************************/ + +int CPLGetNumCPUs() +{ +#ifdef _SC_NPROCESSORS_ONLN + return (int)sysconf(_SC_NPROCESSORS_ONLN); +#else + return 1; +#endif +} + +/************************************************************************/ +/* CPLCreateOrAcquireMutex() */ +/************************************************************************/ + +static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; +static void *CPLCreateMutexInternal(int bAlreadyInGlobalLock); + +int CPLCreateOrAcquireMutex( void **phMutex, double dfWaitInSeconds ) + +{ + int bSuccess = FALSE; + + pthread_mutex_lock(&global_mutex); + if( *phMutex == NULL ) + { + *phMutex = CPLCreateMutexInternal(TRUE); + bSuccess = *phMutex != NULL; + pthread_mutex_unlock(&global_mutex); + } + else + { + pthread_mutex_unlock(&global_mutex); + + bSuccess = CPLAcquireMutex( *phMutex, dfWaitInSeconds ); + } + + return bSuccess; +} /************************************************************************/ /* CPLGetThreadingModel() */ @@ -720,40 +1160,68 @@ const char *CPLGetThreadingModel() /* CPLCreateMutex() */ /************************************************************************/ -void *CPLCreateMutex() - +typedef struct _MutexLinkedElt MutexLinkedElt; +struct _MutexLinkedElt { - pthread_mutex_t *hMutex; - - hMutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t)); + pthread_mutex_t sMutex; + _MutexLinkedElt *psPrev; + _MutexLinkedElt *psNext; +}; +static MutexLinkedElt* psMutexList = NULL; +static void CPLInitMutex(MutexLinkedElt* psItem) +{ #if defined(PTHREAD_MUTEX_RECURSIVE) || defined(HAVE_PTHREAD_MUTEX_RECURSIVE) { pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); - pthread_mutex_init( hMutex, &attr ); + pthread_mutex_init( &(psItem->sMutex), &attr ); } /* BSDs have PTHREAD_MUTEX_RECURSIVE as an enum, not a define. */ -/* But they have #define MUTEX_TYPE_COUNTING_FAST PTHREAD_MUTEX_RECURSIVE */ +/* But they have #define MUTEX_TYPE_COUNTING_FAST PTHREAD_MUTEX_RECURSIVE */ #elif defined(MUTEX_TYPE_COUNTING_FAST) { pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, MUTEX_TYPE_COUNTING_FAST ); - pthread_mutex_init( hMutex, &attr ); + pthread_mutex_init( &(psItem->sMutex), &attr ); } #elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) pthread_mutex_t tmp_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; - *hMutex = tmp_mutex; + psItem->sMutex = tmp_mutex; #else #error "Recursive mutexes apparently unsupported, configure --without-threads" #endif +} + +static void *CPLCreateMutexInternal(int bAlreadyInGlobalLock) +{ + MutexLinkedElt* psItem = (MutexLinkedElt *) malloc(sizeof(MutexLinkedElt)); + if (psItem == NULL) + return NULL; + + if( !bAlreadyInGlobalLock ) + pthread_mutex_lock(&global_mutex); + psItem->psPrev = NULL; + psItem->psNext = psMutexList; + if( psMutexList ) + psMutexList->psPrev = psItem; + psMutexList = psItem; + if( !bAlreadyInGlobalLock ) + pthread_mutex_unlock(&global_mutex); + + CPLInitMutex(psItem); // mutexes are implicitly acquired when created. - CPLAcquireMutex( hMutex, 0.0 ); + CPLAcquireMutex( &(psItem->sMutex), 0.0 ); - return (void *) hMutex; + return psItem; +} + +void *CPLCreateMutex() +{ + return CPLCreateMutexInternal(FALSE); } /************************************************************************/ @@ -766,14 +1234,15 @@ int CPLAcquireMutex( void *hMutexIn, double dfWaitInSeconds ) int err; /* we need to add timeout support */ - err = pthread_mutex_lock( (pthread_mutex_t *) hMutexIn ); + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn; + err = pthread_mutex_lock( &(psItem->sMutex) ); if( err != 0 ) { if( err == EDEADLK ) - CPLDebug( "CPLAcquireMutex", "Error = %d/EDEADLK", err ); + fprintf(stderr, "CPLAcquireMutex: Error = %d/EDEADLK", err ); else - CPLDebug( "CPLAcquireMutex", "Error = %d", err ); + fprintf(stderr, "CPLAcquireMutex: Error = %d", err ); return FALSE; } @@ -788,7 +1257,8 @@ int CPLAcquireMutex( void *hMutexIn, double dfWaitInSeconds ) void CPLReleaseMutex( void *hMutexIn ) { - pthread_mutex_unlock( (pthread_mutex_t *) hMutexIn ); + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn; + pthread_mutex_unlock( &(psItem->sMutex) ); } /************************************************************************/ @@ -798,10 +1268,92 @@ void CPLReleaseMutex( void *hMutexIn ) void CPLDestroyMutex( void *hMutexIn ) { - pthread_mutex_destroy( (pthread_mutex_t *) hMutexIn ); + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn; + pthread_mutex_destroy( &(psItem->sMutex) ); + pthread_mutex_lock(&global_mutex); + if( psItem->psPrev ) + psItem->psPrev->psNext = psItem->psNext; + if( psItem->psNext ) + psItem->psNext->psPrev = psItem->psPrev; + if( psItem == psMutexList ) + psMutexList = psItem->psNext; + pthread_mutex_unlock(&global_mutex); free( hMutexIn ); } +/************************************************************************/ +/* CPLReinitAllMutex() */ +/************************************************************************/ + +/* Used by gdalclientserver.cpp just after forking, to avoid */ +/* deadlocks while mixing threads with fork */ +void CPLReinitAllMutex(); +void CPLReinitAllMutex() +{ + MutexLinkedElt* psItem = psMutexList; + while(psItem != NULL ) + { + CPLInitMutex(psItem); + psItem = psItem->psNext; + } + pthread_mutex_t tmp_global_mutex = PTHREAD_MUTEX_INITIALIZER; + global_mutex = tmp_global_mutex; +} + +/************************************************************************/ +/* CPLCreateCond() */ +/************************************************************************/ + +void *CPLCreateCond() +{ + pthread_cond_t* pCond = (pthread_cond_t* )malloc(sizeof(pthread_cond_t)); + if (pCond) + pthread_cond_init(pCond, NULL); + return pCond; +} + +/************************************************************************/ +/* CPLCondWait() */ +/************************************************************************/ + +void CPLCondWait( void *hCond, void* hMutex ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_mutex_t * pMutex = (pthread_mutex_t *)hMutex; + pthread_cond_wait(pCond, pMutex); +} + +/************************************************************************/ +/* CPLCondSignal() */ +/************************************************************************/ + +void CPLCondSignal( void *hCond ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_cond_signal(pCond); +} + +/************************************************************************/ +/* CPLCondBroadcast() */ +/************************************************************************/ + +void CPLCondBroadcast( void *hCond ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_cond_broadcast(pCond); +} + +/************************************************************************/ +/* CPLDestroyCond() */ +/************************************************************************/ + +void CPLDestroyCond( void *hCond ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_cond_destroy(pCond); + free(hCond); +} + /************************************************************************/ /* CPLLockFile() */ /* */ @@ -890,6 +1442,7 @@ typedef struct { void *pAppData; CPLThreadFunc pfnMain; pthread_t hThread; + int bJoinable; } CPLStdCallThreadInfo; static void *CPLStdCallThreadJacket( void *pData ) @@ -899,8 +1452,9 @@ static void *CPLStdCallThreadJacket( void *pData ) psInfo->pfnMain( psInfo->pAppData ); - CPLFree( psInfo ); - + if (!psInfo->bJoinable) + CPLFree( psInfo ); + return NULL; } @@ -915,13 +1469,14 @@ static void *CPLStdCallThreadJacket( void *pData ) int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) { - + CPLStdCallThreadInfo *psInfo; pthread_attr_t hThreadAttr; psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); psInfo->pAppData = pThreadArg; psInfo->pfnMain = pfnMain; + psInfo->bJoinable = FALSE; pthread_attr_init( &hThreadAttr ); pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); @@ -935,6 +1490,47 @@ int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) return 1; /* can we return the actual thread pid? */ } +/************************************************************************/ +/* CPLCreateJoinableThread() */ +/************************************************************************/ + +void* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + CPLStdCallThreadInfo *psInfo; + pthread_attr_t hThreadAttr; + + psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); + psInfo->pAppData = pThreadArg; + psInfo->pfnMain = pfnMain; + psInfo->bJoinable = TRUE; + + pthread_attr_init( &hThreadAttr ); + pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_JOINABLE ); + if( pthread_create( &(psInfo->hThread), &hThreadAttr, + CPLStdCallThreadJacket, (void *) psInfo ) != 0 ) + { + CPLFree( psInfo ); + return NULL; + } + + return psInfo; +} + +/************************************************************************/ +/* CPLJoinThread() */ +/************************************************************************/ + +void CPLJoinThread(void* hJoinableThread) +{ + CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo*) hJoinableThread; + + void* status; + pthread_join( psInfo->hThread, &status); + + CPLFree(psInfo); +} + /************************************************************************/ /* CPLSleep() */ /************************************************************************/ @@ -949,11 +1545,35 @@ void CPLSleep( double dfWaitInSeconds ) nanosleep( &sRequest, &sRemain ); } -static int bTLSKeySetup = FALSE; -static pthread_key_t oTLSKey; +static pthread_key_t oTLSKey; +static pthread_once_t oTLSKeySetup = PTHREAD_ONCE_INIT; /************************************************************************/ -/* CPLCleanupTLS() */ +/* CPLMake_key() */ +/************************************************************************/ + +static void CPLMake_key() + +{ + if( pthread_key_create( &oTLSKey, (void (*)(void*)) CPLCleanupTLSList ) != 0 ) + { + CPLError( CE_Fatal, CPLE_AppDefined, "pthread_key_create() failed!" ); + } +} + +/************************************************************************/ +/* CPLFinalizeTLS() */ +/************************************************************************/ + +void CPLFinalizeTLS() +{ + CPLCleanupTLS(); + /* See #5509 for the explanation why this may be needed */ + pthread_key_delete(oTLSKey); +} + +/************************************************************************/ +/* CPLCleanupTLS() */ /************************************************************************/ void CPLCleanupTLS() @@ -961,9 +1581,6 @@ void CPLCleanupTLS() { void **papTLSList; - if( !bTLSKeySetup ) - return; - papTLSList = (void **) pthread_getspecific( oTLSKey ); if( papTLSList == NULL ) return; @@ -982,25 +1599,21 @@ static void **CPLGetTLSList() { void **papTLSList; - if( !bTLSKeySetup ) + if ( pthread_once(&oTLSKeySetup, CPLMake_key) != 0 ) { - if( pthread_key_create( &oTLSKey, - (void (*)(void*)) CPLCleanupTLSList ) != 0 ) - { - CPLError( CE_Fatal, CPLE_AppDefined, - "pthread_key_create() failed!" ); - } - bTLSKeySetup = TRUE; + CPLEmergencyError( "CPLGetTLSList(): pthread_once() failed!" ); } papTLSList = (void **) pthread_getspecific( oTLSKey ); if( papTLSList == NULL ) { - papTLSList = (void **) CPLCalloc(sizeof(void*),CTLS_MAX*2); + papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2); + if( papTLSList == NULL ) + CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); if( pthread_setspecific( oTLSKey, papTLSList ) != 0 ) { - CPLError( CE_Fatal, CPLE_AppDefined, - "pthread_setspecific() failed!" ); + CPLEmergencyError( + "CPLGetTLSList(): pthread_setspecific() failed!" ); } } @@ -1029,12 +1642,23 @@ void *CPLGetTLS( int nIndex ) void CPLSetTLS( int nIndex, void *pData, int bFreeOnExit ) +{ + CPLSetTLSWithFreeFunc(nIndex, pData, (bFreeOnExit) ? CPLFree : NULL); +} + +/************************************************************************/ +/* CPLSetTLSWithFreeFunc() */ +/************************************************************************/ + +/* Warning : the CPLTLSFreeFunc must not in any case directly or indirectly */ +/* use or fetch any TLS data, or a terminating thread will hang ! */ +void CPLSetTLSWithFreeFunc( int nIndex, void *pData, CPLTLSFreeFunc pfnFree ) + { void **papTLSList = CPLGetTLSList(); CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); papTLSList[nIndex] = pData; - papTLSList[CTLS_MAX + nIndex] = (void *) (long) bFreeOnExit; + papTLSList[CTLS_MAX + nIndex] = (void*) pfnFree; } - diff --git a/cpl/cpl_multiproc.h b/cpl/cpl_multiproc.h index caef7b8..ac78f99 100644 --- a/cpl/cpl_multiproc.h +++ b/cpl/cpl_multiproc.h @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_multiproc.h 17143 2009-05-28 18:26:05Z rouault $ + * $Id: cpl_multiproc.h 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Purpose: CPL Multi-Threading, and process handling portability functions. @@ -7,6 +7,7 @@ * ********************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -41,6 +42,9 @@ #if defined(WIN32) && !defined(CPL_MULTIPROC_STUB) # define CPL_MULTIPROC_WIN32 +/* MinGW can have pthread support, so disable it to avoid issues */ +/* in cpl_multiproc.cpp */ +# undef CPL_MULTIPROC_PTHREAD #endif #if !defined(CPL_MULTIPROC_WIN32) && !defined(CPL_MULTIPROC_PTHREAD) \ @@ -55,24 +59,40 @@ typedef void (*CPLThreadFunc)(void *); void CPL_DLL *CPLLockFile( const char *pszPath, double dfWaitInSeconds ); void CPL_DLL CPLUnlockFile( void *hLock ); -void CPL_DLL *CPLCreateMutex(); +void CPL_DLL *CPLCreateMutex( void ); int CPL_DLL CPLCreateOrAcquireMutex( void **, double dfWaitInSeconds ); int CPL_DLL CPLAcquireMutex( void *hMutex, double dfWaitInSeconds ); void CPL_DLL CPLReleaseMutex( void *hMutex ); void CPL_DLL CPLDestroyMutex( void *hMutex ); +void CPL_DLL CPLCleanupMasterMutex( void ); -GIntBig CPL_DLL CPLGetPID(); +void CPL_DLL *CPLCreateCond( void ); +void CPL_DLL CPLCondWait( void *hCond, void* hMutex ); +void CPL_DLL CPLCondSignal( void *hCond ); +void CPL_DLL CPLCondBroadcast( void *hCond ); +void CPL_DLL CPLDestroyCond( void *hCond ); + +GIntBig CPL_DLL CPLGetPID( void ); int CPL_DLL CPLCreateThread( CPLThreadFunc pfnMain, void *pArg ); +void CPL_DLL* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pArg ); +void CPL_DLL CPLJoinThread(void* hJoinableThread); void CPL_DLL CPLSleep( double dfWaitInSeconds ); -const char CPL_DLL *CPLGetThreadingModel(); +const char CPL_DLL *CPLGetThreadingModel( void ); + +int CPL_DLL CPLGetNumCPUs( void ); CPL_C_END #ifdef __cplusplus +/* Instanciates the mutex if not already done */ #define CPLMutexHolderD(x) CPLMutexHolder oHolder(x,1000.0,__FILE__,__LINE__); +/* This variant assumes the the mutex has already been created. If not, it will */ +/* be a no-op */ +#define CPLMutexHolderOptionalLockD(x) CPLMutexHolder oHolder(x,1000.0,__FILE__,__LINE__); + class CPL_DLL CPLMutexHolder { private: @@ -82,9 +102,17 @@ class CPL_DLL CPLMutexHolder public: + /* Instanciates the mutex if not already done */ CPLMutexHolder( void **phMutex, double dfWaitInSeconds = 1000.0, const char *pszFile = __FILE__, int nLine = __LINE__ ); + + /* This variant assumes the the mutex has already been created. If not, it will */ + /* be a no-op */ + CPLMutexHolder( void* hMutex, double dfWaitInSeconds = 1000.0, + const char *pszFile = __FILE__, + int nLine = __LINE__ ); + ~CPLMutexHolder(); }; #endif /* def __cplusplus */ @@ -94,14 +122,14 @@ class CPL_DLL CPLMutexHolder /* -------------------------------------------------------------------- */ #define CTLS_RLBUFFERINFO 1 /* cpl_conv.cpp */ -#define CTLS_DECDMSBUFFER 2 /* cpl_conv.cpp */ +#define CTLS_WIN32_COND 2 /* cpl_multiproc.cpp */ #define CTLS_CSVTABLEPTR 3 /* cpl_csv.cpp */ #define CTLS_CSVDEFAULTFILENAME 4 /* cpl_csv.cpp */ #define CTLS_ERRORCONTEXT 5 /* cpl_error.cpp */ -#define CTLS_FINDERINFO 6 /* cpl_finder.cpp */ +#define CTLS_GDALDATASET_REC_PROTECT_MAP 6 /* gdaldataset.cpp */ #define CTLS_PATHBUF 7 /* cpl_path.cpp */ -#define CTLS_SPRINTFBUF 8 /* cpl_string.cpp */ -#define CTLS_SWQ_ERRBUF 9 /* swq.c */ +#define CTLS_UNUSED3 8 +#define CTLS_UNUSED4 9 #define CTLS_CPLSPRINTF 10 /* cpl_string.h */ #define CTLS_RESPONSIBLEPID 11 /* gdaldataset.cpp */ #define CTLS_VERSIONINFO 12 /* gdal_misc.cpp */ @@ -114,7 +142,13 @@ class CPL_DLL CPLMutexHolder CPL_C_START void CPL_DLL * CPLGetTLS( int nIndex ); void CPL_DLL CPLSetTLS( int nIndex, void *pData, int bFreeOnExit ); -void CPL_DLL CPLCleanupTLS(); + +/* Warning : the CPLTLSFreeFunc must not in any case directly or indirectly */ +/* use or fetch any TLS data, or a terminating thread will hang ! */ +typedef void (*CPLTLSFreeFunc)( void* pData ); +void CPL_DLL CPLSetTLSWithFreeFunc( int nIndex, void *pData, CPLTLSFreeFunc pfnFree ); + +void CPL_DLL CPLCleanupTLS( void ); CPL_C_END #endif /* _CPL_MULTIPROC_H_INCLUDED_ */ diff --git a/cpl/cpl_path.cpp b/cpl/cpl_path.cpp index 5f19f07..a27f22b 100644 --- a/cpl/cpl_path.cpp +++ b/cpl/cpl_path.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_path.cpp 17818 2009-10-14 18:06:56Z rouault $ + * $Id: cpl_path.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Portable filename/path parsing, and forming ala "Glob API". @@ -7,6 +7,7 @@ * ********************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,11 +32,12 @@ #include "cpl_string.h" #include "cpl_multiproc.h" -CPL_CVSID("$Id: cpl_path.cpp 17818 2009-10-14 18:06:56Z rouault $"); +CPL_CVSID("$Id: cpl_path.cpp 27044 2014-03-16 23:41:27Z rouault $"); /* should be size of larged possible filename */ #define CPL_PATH_BUF_SIZE 2048 +#define CPL_PATH_BUF_COUNT 10 #if defined(WIN32) || defined(WIN32CE) #define SEP_CHAR '\\' @@ -59,14 +61,24 @@ static const char* CPLStaticBufferTooSmall(char *pszStaticResult) static char *CPLGetStaticResult() { - char *pszStaticResult = (char *) CPLGetTLS( CTLS_PATHBUF ); - if( pszStaticResult == NULL ) + char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_PATHBUF ); + if( pachBufRingInfo == NULL ) { - pszStaticResult = (char *) CPLMalloc(CPL_PATH_BUF_SIZE); - CPLSetTLS( CTLS_PATHBUF, pszStaticResult, TRUE ); + pachBufRingInfo = (char *) CPLCalloc(1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT); + CPLSetTLS( CTLS_PATHBUF, pachBufRingInfo, TRUE ); } - return pszStaticResult; +/* -------------------------------------------------------------------- */ +/* Work out which string in the "ring" we want to use this */ +/* time. */ +/* -------------------------------------------------------------------- */ + int *pnBufIndex = (int *) pachBufRingInfo; + int nOffset = sizeof(int) + *pnBufIndex * CPL_PATH_BUF_SIZE; + char *pachBuffer = pachBufRingInfo + nOffset; + + *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT; + + return pachBuffer; } @@ -435,7 +447,7 @@ const char *CPLResetExtension( const char *pszPath, const char *pszExt ) * not. May be NULL. * * @param pszBasename file basename. May optionally have path and/or - * extension. May not be NULL. + * extension. Must *NOT* be NULL. * * @param pszExtension file extension, optionally including the period. May * be NULL. @@ -457,12 +469,24 @@ const char *CPLFormFilename( const char * pszPath, CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) ); CPLAssert( ! (pszBasename >= pszStaticResult && pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + if( pszBasename[0] == '.' && pszBasename[1] == '/' ) + pszBasename += 2; + if( pszPath == NULL ) pszPath = ""; else if( strlen(pszPath) > 0 && pszPath[strlen(pszPath)-1] != '/' && pszPath[strlen(pszPath)-1] != '\\' ) - pszAddedPathSep = SEP_STRING; + { + /* FIXME? would be better to ask the filesystems what they */ + /* prefer as directory separator */ + if (strncmp(pszPath, "/vsicurl/", 9) == 0) + pszAddedPathSep = "/"; + else if (strncmp(pszPath, "/vsizip/", 8) == 0) + pszAddedPathSep = "/"; + else + pszAddedPathSep = SEP_STRING; + } if( pszExtension == NULL ) pszExtension = ""; @@ -512,9 +536,11 @@ const char *CPLFormCIFilename( const char * pszPath, const char * pszExtension ) { -#if defined(WIN32) || defined(WIN32CE) - return CPLFormFilename( pszPath, pszBasename, pszExtension ); -#else + // On case insensitive filesystems, just default to + // CPLFormFilename() + if( !VSIIsCaseSensitiveFS(pszPath) ) + return CPLFormFilename( pszPath, pszBasename, pszExtension ); + const char *pszAddedExtSep = ""; char *pszFilename; const char *pszFullPath; @@ -536,17 +562,17 @@ const char *CPLFormCIFilename( const char * pszPath, pszBasename, pszAddedExtSep, pszExtension ); pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL ); - nStatRet = VSIStatL( pszFullPath, &sStatBuf ); + nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG ); if( nStatRet != 0 ) { for( i = 0; pszFilename[i] != '\0'; i++ ) { if( islower(pszFilename[i]) ) - pszFilename[i] = toupper(pszFilename[i]); + pszFilename[i] = (char) toupper(pszFilename[i]); } pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL ); - nStatRet = VSIStatL( pszFullPath, &sStatBuf ); + nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG ); } if( nStatRet != 0 ) @@ -554,11 +580,11 @@ const char *CPLFormCIFilename( const char * pszPath, for( i = 0; pszFilename[i] != '\0'; i++ ) { if( isupper(pszFilename[i]) ) - pszFilename[i] = tolower(pszFilename[i]); + pszFilename[i] = (char) tolower(pszFilename[i]); } pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL ); - nStatRet = VSIStatL( pszFullPath, &sStatBuf ); + nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG ); } if( nStatRet != 0 ) @@ -567,7 +593,6 @@ const char *CPLFormCIFilename( const char * pszPath, CPLFree( pszFilename ); return pszFullPath; -#endif } /************************************************************************/ @@ -622,7 +647,14 @@ const char *CPLProjectRelativeFilename( const char *pszProjectDir, if( pszProjectDir[strlen(pszProjectDir)-1] != '/' && pszProjectDir[strlen(pszProjectDir)-1] != '\\' ) { - if (CPLStrlcat( pszStaticResult, SEP_STRING, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) + /* FIXME? would be better to ask the filesystems what they */ + /* prefer as directory separator */ + const char* pszAddedPathSep; + if (strncmp(pszStaticResult, "/vsicurl/", 9) == 0) + pszAddedPathSep = "/"; + else + pszAddedPathSep = SEP_STRING; + if (CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) goto error; } @@ -866,7 +898,17 @@ char **CPLCorrespondingPaths( const char *pszOldFilename, { for( i=0; papszFileList[i] != NULL; i++ ) { - if( osOldBasename != CPLGetBasename( papszFileList[i] ) ) + if( osOldBasename == CPLGetBasename( papszFileList[i] ) ) + continue; + + CPLString osFilePath, osFileName; + + osFilePath = CPLGetPath( papszFileList[i] ); + osFileName = CPLGetFilename( papszFileList[i] ); + + if( !EQUALN(osFileName,osOldBasename,osOldBasename.size()) + || !EQUAL(osFilePath,osOldPath) + || osFileName[osOldBasename.size()] != '.' ) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to rename fileset due irregular basenames."); diff --git a/cpl/cpl_port.h b/cpl/cpl_port.h index edf09ee..00edcac 100644 --- a/cpl/cpl_port.h +++ b/cpl/cpl_port.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_port.h 17734 2009-10-03 09:48:01Z rouault $ + * $Id: cpl_port.h 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Author: Frank Warmerdam, warmerdam@pobox.com @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1998, 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,11 +32,6 @@ #ifndef CPL_BASE_H_INCLUDED #define CPL_BASE_H_INCLUDED -/* Remove annoying warnings Microsoft Visual C++ */ -#if defined(_MSC_VER) -# pragma warning(disable:4251 4275 4786) -#endif - /** * \file cpl_port.h * @@ -80,13 +76,28 @@ # ifndef _CRT_NONSTDC_NO_DEPRECATE # define _CRT_NONSTDC_NO_DEPRECATE # endif -# ifdef MSVC_USE_VLD -# include -# endif #endif #include "cpl_config.h" +/* ==================================================================== */ +/* A few sanity checks, mainly to detect problems that sometimes */ +/* arise with bad configured cross-compilation. */ +/* ==================================================================== */ + +#if !defined(SIZEOF_INT) || SIZEOF_INT != 4 +#error "Unexpected value for SIZEOF_INT" +#endif + +#if !defined(SIZEOF_UNSIGNED_LONG) || (SIZEOF_UNSIGNED_LONG != 4 && SIZEOF_UNSIGNED_LONG != 8) +#error "Unexpected value for SIZEOF_UNSIGNED_LONG" +#endif + +#if !defined(SIZEOF_VOIDP) || (SIZEOF_VOIDP != 4 && SIZEOF_VOIDP != 8) +#error "Unexpected value for SIZEOF_VOIDP" +#endif + + /* ==================================================================== */ /* This will disable most WIN32 stuff in a Cygnus build which */ /* defines unix to 1. */ @@ -101,6 +112,31 @@ # define _LARGEFILE64_SOURCE 1 #endif +/* ==================================================================== */ +/* If iconv() is available use extended recoding module. */ +/* Stub implementation is always compiled in, because it works */ +/* faster than iconv() for encodings it supports. */ +/* ==================================================================== */ + +#if defined(HAVE_ICONV) +# define CPL_RECODE_ICONV +#endif + +#define CPL_RECODE_STUB + +/* ==================================================================== */ +/* MinGW stuff */ +/* ==================================================================== */ + +/* We need __MSVCRT_VERSION__ >= 0x0601 to have "struct __stat64" */ +/* Latest versions of mingw32 define it, but with older ones, */ +/* we need to define it manually */ +#if defined(__MINGW32__) +#ifndef __MSVCRT_VERSION__ +#define __MSVCRT_VERSION__ 0x0601 +#endif +#endif + /* ==================================================================== */ /* Standard include files. */ /* ==================================================================== */ @@ -133,7 +169,7 @@ # include #endif -#ifdef _AIX +#if !(defined(WIN32) || defined(WIN32CE)) # include #endif @@ -166,7 +202,12 @@ typedef unsigned int GUInt32; typedef short GInt16; typedef unsigned short GUInt16; typedef unsigned char GByte; +/* hack for PDF driver and poppler >= 0.15.0 that defines incompatible "typedef bool GBool" */ +/* in include/poppler/goo/gtypes.h */ +#ifndef CPL_GBOOL_DEFINED +#define CPL_GBOOL_DEFINED typedef int GBool; +#endif /* -------------------------------------------------------------------- */ /* 64bit support */ @@ -254,7 +295,11 @@ typedef unsigned long GUIntBig; /* TODO : support for other compilers needed */ #if defined(__GNUC__) || defined(_MSC_VER) +#define HAS_CPL_INLINE 1 #define CPL_INLINE __inline +#elif defined(__SUNPRO_CC) +#define HAS_CPL_INLINE 1 +#define CPL_INLINE inline #else #define CPL_INLINE #endif @@ -280,6 +325,10 @@ typedef unsigned long GUIntBig; # define ABS(x) ((x<0) ? (-1*(x)) : x) #endif +#ifndef M_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + /* -------------------------------------------------------------------- */ /* Macro to test equality of two floating point values. */ /* We use fabs() function instead of ABS() macro to avoid side */ @@ -289,14 +338,19 @@ typedef unsigned long GUIntBig; # define CPLIsEqual(x,y) (fabs((x) - (y)) < 0.0000000000001) #endif +/* -------------------------------------------------------------------- */ +/* Provide macros for case insensitive string comparisons. */ +/* -------------------------------------------------------------------- */ #ifndef EQUAL -#if defined(WIN32) || defined(WIN32CE) -# define EQUALN(a,b,n) (strnicmp(a,b,n)==0) -# define EQUAL(a,b) (stricmp(a,b)==0) -#else -# define EQUALN(a,b,n) (strncasecmp(a,b,n)==0) -# define EQUAL(a,b) (strcasecmp(a,b)==0) -#endif +# if defined(WIN32) || defined(WIN32CE) +# define STRCASECMP(a,b) (stricmp(a,b)) +# define STRNCASECMP(a,b,n) (strnicmp(a,b,n)) +# else +# define STRCASECMP(a,b) (strcasecmp(a,b)) +# define STRNCASECMP(a,b,n) (strncasecmp(a,b,n)) +# endif +# define EQUALN(a,b,n) (STRNCASECMP(a,b,n)==0) +# define EQUAL(a,b) (STRCASECMP(a,b)==0) #endif #ifdef macos_pre10 @@ -305,8 +359,8 @@ int strncasecmp(char * str1, char * str2, int len); char * strdup (char *instr); #endif -#ifndef CPL_THREADLOCAL -# define CPL_THREADLOCAL +#ifndef CPL_THREADLOCAL +# define CPL_THREADLOCAL #endif /* -------------------------------------------------------------------- */ @@ -460,6 +514,18 @@ char * strdup (char *instr); #define CPL_LSBINT32PTR(x) ((*(GByte*)(x)) | ((*(GByte*)((x)+1)) << 8) | \ ((*(GByte*)((x)+2)) << 16) | ((*(GByte*)((x)+3)) << 24)) +/** Return a signed Int16 from the 2 bytes ordered in LSB order at address x */ +#define CPL_LSBSINT16PTR(x) ((GInt16) CPL_LSBINT16PTR(x)) + +/** Return a unsigned Int16 from the 2 bytes ordered in LSB order at address x */ +#define CPL_LSBUINT16PTR(x) ((GUInt16)CPL_LSBINT16PTR(x)) + +/** Return a signed Int32 from the 4 bytes ordered in LSB order at address x */ +#define CPL_LSBSINT32PTR(x) ((GInt32) CPL_LSBINT32PTR(x)) + +/** Return a unsigned Int32 from the 4 bytes ordered in LSB order at address x */ +#define CPL_LSBUINT32PTR(x) ((GUInt32)CPL_LSBINT32PTR(x)) + /* Utility macro to explicitly mark intentionally unreferenced parameters. */ #ifndef UNREFERENCED_PARAM @@ -489,10 +555,44 @@ static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } # define CPL_CVSID(string) #endif +/* Null terminated variadic */ +#if defined(__GNUC__) && __GNUC__ >= 4 && !defined(DOXYGEN_SKIP) +# define CPL_NULL_TERMINATED __attribute__((__sentinel__)) +#else +# define CPL_NULL_TERMINATED +#endif + #if defined(__GNUC__) && __GNUC__ >= 3 && !defined(DOXYGEN_SKIP) #define CPL_PRINT_FUNC_FORMAT( format_idx, arg_idx ) __attribute__((__format__ (__printf__, format_idx, arg_idx))) #else #define CPL_PRINT_FUNC_FORMAT( format_idx, arg_idx ) #endif +#if defined(__GNUC__) && __GNUC__ >= 4 && !defined(DOXYGEN_SKIP) +#define CPL_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define CPL_WARN_UNUSED_RESULT +#endif + +#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(DOXYGEN_SKIP) +#define CPL_NO_RETURN __attribute__((noreturn)) +#else +#define CPL_NO_RETURN +#endif + +#if !defined(DOXYGEN_SKIP) +#if defined(__has_extension) + #if __has_extension(attribute_deprecated_with_message) + /* Clang extension */ + #define CPL_WARN_DEPRECATED(x) __attribute__ ((deprecated(x))) + #else + #define CPL_WARN_DEPRECATED(x) + #endif +#elif defined(__GNUC__) + #define CPL_WARN_DEPRECATED(x) __attribute__ ((deprecated)) +#else + #define CPL_WARN_DEPRECATED(x) +#endif +#endif + #endif /* ndef CPL_BASE_H_INCLUDED */ diff --git a/cpl/cpl_progress.cpp b/cpl/cpl_progress.cpp new file mode 100644 index 0000000..c9ec491 --- /dev/null +++ b/cpl/cpl_progress.cpp @@ -0,0 +1,236 @@ +/****************************************************************************** + * $Id$ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Progress function implementations. + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_progress.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: gdal_misc.cpp 25494 2013-01-13 12:55:17Z etourigny $"); + +/************************************************************************/ +/* GDALDummyProgress() */ +/************************************************************************/ + +/** + * \brief Stub progress function. + * + * This is a stub (does nothing) implementation of the GDALProgressFunc() + * semantics. It is primarily useful for passing to functions that take + * a GDALProgressFunc() argument but for which the application does not want + * to use one of the other progress functions that actually do something. + */ + +int CPL_STDCALL GDALDummyProgress( double dfComplete, const char *pszMessage, + void *pData ) + +{ + return TRUE; +} + +/************************************************************************/ +/* GDALScaledProgress() */ +/************************************************************************/ +typedef struct { + GDALProgressFunc pfnProgress; + void *pData; + double dfMin; + double dfMax; +} GDALScaledProgressInfo; + +/** + * \brief Scaled progress transformer. + * + * This is the progress function that should be passed along with the + * callback data returned by GDALCreateScaledProgress(). + */ + +int CPL_STDCALL GDALScaledProgress( double dfComplete, const char *pszMessage, + void *pData ) + +{ + GDALScaledProgressInfo *psInfo = (GDALScaledProgressInfo *) pData; + + return psInfo->pfnProgress( dfComplete * (psInfo->dfMax - psInfo->dfMin) + + psInfo->dfMin, + pszMessage, psInfo->pData ); +} + +/************************************************************************/ +/* GDALCreateScaledProgress() */ +/************************************************************************/ + +/** + * \brief Create scaled progress transformer. + * + * Sometimes when an operations wants to report progress it actually + * invokes several subprocesses which also take GDALProgressFunc()s, + * and it is desirable to map the progress of each sub operation into + * a portion of 0.0 to 1.0 progress of the overall process. The scaled + * progress function can be used for this. + * + * For each subsection a scaled progress function is created and + * instead of passing the overall progress func down to the sub functions, + * the GDALScaledProgress() function is passed instead. + * + * @param dfMin the value to which 0.0 in the sub operation is mapped. + * @param dfMax the value to which 1.0 is the sub operation is mapped. + * @param pfnProgress the overall progress function. + * @param pData the overall progress function callback data. + * + * @return pointer to pass as pProgressArg to sub functions. Should be freed + * with GDALDestroyScaledProgress(). + * + * Example: + * + * \code + * int MyOperation( ..., GDALProgressFunc pfnProgress, void *pProgressData ); + * + * { + * void *pScaledProgress; + * + * pScaledProgress = GDALCreateScaledProgress( 0.0, 0.5, pfnProgress, + * pProgressData ); + * GDALDoLongSlowOperation( ..., GDALScaledProgress, pScaledProgress ); + * GDALDestroyScaledProgress( pScaledProgress ); + * + * pScaledProgress = GDALCreateScaledProgress( 0.5, 1.0, pfnProgress, + * pProgressData ); + * GDALDoAnotherOperation( ..., GDALScaledProgress, pScaledProgress ); + * GDALDestroyScaledProgress( pScaledProgress ); + * + * return ...; + * } + * \endcode + */ + +void * CPL_STDCALL GDALCreateScaledProgress( double dfMin, double dfMax, + GDALProgressFunc pfnProgress, + void * pData ) + +{ + GDALScaledProgressInfo *psInfo; + + psInfo = (GDALScaledProgressInfo *) + CPLCalloc(sizeof(GDALScaledProgressInfo),1); + + if( ABS(dfMin-dfMax) < 0.0000001 ) + dfMax = dfMin + 0.01; + + psInfo->pData = pData; + psInfo->pfnProgress = pfnProgress; + psInfo->dfMin = dfMin; + psInfo->dfMax = dfMax; + + return (void *) psInfo; +} + +/************************************************************************/ +/* GDALDestroyScaledProgress() */ +/************************************************************************/ + +/** + * \brief Cleanup scaled progress handle. + * + * This function cleans up the data associated with a scaled progress function + * as returned by GADLCreateScaledProgress(). + * + * @param pData scaled progress handle returned by GDALCreateScaledProgress(). + */ + +void CPL_STDCALL GDALDestroyScaledProgress( void * pData ) + +{ + CPLFree( pData ); +} + +/************************************************************************/ +/* GDALTermProgress() */ +/************************************************************************/ + +/** + * \brief Simple progress report to terminal. + * + * This progress reporter prints simple progress report to the + * terminal window. The progress report generally looks something like + * this: + +\verbatim +0...10...20...30...40...50...60...70...80...90...100 - done. +\endverbatim + + * Every 2.5% of progress another number or period is emitted. Note that + * GDALTermProgress() uses internal static data to keep track of the last + * percentage reported and will get confused if two terminal based progress + * reportings are active at the same time. + * + * The GDALTermProgress() function maintains an internal memory of the + * last percentage complete reported in a static variable, and this makes + * it unsuitable to have multiple GDALTermProgress()'s active eithin a + * single thread or across multiple threads. + * + * @param dfComplete completion ratio from 0.0 to 1.0. + * @param pszMessage optional message. + * @param pProgressArg ignored callback data argument. + * + * @return Always returns TRUE indicating the process should continue. + */ + +int CPL_STDCALL GDALTermProgress( double dfComplete, const char *pszMessage, + void * pProgressArg ) + +{ + static int nLastTick = -1; + int nThisTick = (int) (dfComplete * 40.0); + + (void) pProgressArg; + + nThisTick = MIN(40,MAX(0,nThisTick)); + + // Have we started a new progress run? + if( nThisTick < nLastTick && nLastTick >= 39 ) + nLastTick = -1; + + if( nThisTick <= nLastTick ) + return TRUE; + + while( nThisTick > nLastTick ) + { + nLastTick++; + if( nLastTick % 4 == 0 ) + fprintf( stdout, "%d", (nLastTick / 4) * 10 ); + else + fprintf( stdout, "." ); + } + + if( nThisTick == 40 ) + fprintf( stdout, " - done.\n" ); + else + fflush( stdout ); + + return TRUE; +} diff --git a/cpl/cpl_progress.h b/cpl/cpl_progress.h new file mode 100644 index 0000000..aa5fe52 --- /dev/null +++ b/cpl/cpl_progress.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id$ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Prototypes and definitions for progress functions. + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_PROGRESS_H_INCLUDED +#define CPL_PROGRESS_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START + +typedef int (CPL_STDCALL *GDALProgressFunc)(double dfComplete, const char *pszMessage, void *pProgressArg); + +int CPL_DLL CPL_STDCALL GDALDummyProgress( double, const char *, void *); +int CPL_DLL CPL_STDCALL GDALTermProgress( double, const char *, void *); +int CPL_DLL CPL_STDCALL GDALScaledProgress( double, const char *, void *); +void CPL_DLL * CPL_STDCALL GDALCreateScaledProgress( double, double, + GDALProgressFunc, void * ); +void CPL_DLL CPL_STDCALL GDALDestroyScaledProgress( void * ); +CPL_C_END + +#endif /* ndef CPL_PROGRESS_H_INCLUDED */ diff --git a/cpl/cpl_recode.cpp b/cpl/cpl_recode.cpp new file mode 100644 index 0000000..2a8e6aa --- /dev/null +++ b/cpl/cpl_recode.cpp @@ -0,0 +1,363 @@ +/********************************************************************** + * $Id: cpl_recode.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_recode.cpp + * Project: CPL - Common Portability Library + * Purpose: Character set recoding and char/wchar_t conversions. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ********************************************************************** + * Copyright (c) 2011, Andrey Kiselev + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2011-2014, Even Rouault + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + **********************************************************************/ + +#include "cpl_string.h" + +CPL_CVSID("$Id: cpl_recode.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#ifdef CPL_RECODE_ICONV +extern void CPLClearRecodeIconvWarningFlags(); +extern char *CPLRecodeIconv( const char *, const char *, const char * ); +extern char *CPLRecodeFromWCharIconv( const wchar_t *, + const char *, const char * ); +extern wchar_t *CPLRecodeToWCharIconv( const char *, + const char *, const char * ); +#endif /* CPL_RECODE_ICONV */ + +extern void CPLClearRecodeStubWarningFlags(); +extern char *CPLRecodeStub( const char *, const char *, const char * ); +extern char *CPLRecodeFromWCharStub( const wchar_t *, + const char *, const char * ); +extern wchar_t *CPLRecodeToWCharStub( const char *, + const char *, const char * ); +extern int CPLIsUTF8Stub( const char *, int ); + +/************************************************************************/ +/* CPLRecode() */ +/************************************************************************/ + +/** + * Convert a string from a source encoding to a destination encoding. + * + * The only guaranteed supported encodings are CPL_ENC_UTF8, CPL_ENC_ASCII + * and CPL_ENC_ISO8859_1. Currently, the following conversions are supported : + *
      + *
    • CPL_ENC_ASCII -> CPL_ENC_UTF8 or CPL_ENC_ISO8859_1 (no conversion in fact)
    • + *
    • CPL_ENC_ISO8859_1 -> CPL_ENC_UTF8
    • + *
    • CPL_ENC_UTF8 -> CPL_ENC_ISO8859_1
    • + *
    + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource a NULL terminated string. + * @param pszSrcEncoding the source encoding. + * @param pszDstEncoding the destination encoding. + * + * @return a NULL terminated string which should be freed with CPLFree(). + * + * @since GDAL 1.6.0 + */ + +char CPL_DLL *CPLRecode( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +/* -------------------------------------------------------------------- */ +/* Handle a few common short cuts. */ +/* -------------------------------------------------------------------- */ + if ( EQUAL(pszSrcEncoding, pszDstEncoding) ) + return CPLStrdup(pszSource); + + if ( EQUAL(pszSrcEncoding, CPL_ENC_ASCII) + && ( EQUAL(pszDstEncoding, CPL_ENC_UTF8) + || EQUAL(pszDstEncoding, CPL_ENC_ISO8859_1) ) ) + return CPLStrdup(pszSource); + +#ifdef CPL_RECODE_ICONV +/* -------------------------------------------------------------------- */ +/* CPL_ENC_ISO8859_1 -> CPL_ENC_UTF8 */ +/* and CPL_ENC_UTF8 -> CPL_ENC_ISO8859_1 conversions are hadled */ +/* very well by the stub implementation which is faster than the */ +/* iconv() route. Use a stub for these two ones and iconv() */ +/* everything else. */ +/* -------------------------------------------------------------------- */ + if ( ( EQUAL(pszSrcEncoding, CPL_ENC_ISO8859_1) + && EQUAL(pszDstEncoding, CPL_ENC_UTF8) ) + || ( EQUAL(pszSrcEncoding, CPL_ENC_UTF8) + && EQUAL(pszDstEncoding, CPL_ENC_ISO8859_1) ) ) + { + return CPLRecodeStub( pszSource, pszSrcEncoding, pszDstEncoding ); + } + else + { + return CPLRecodeIconv( pszSource, pszSrcEncoding, pszDstEncoding ); + } +#else /* CPL_RECODE_STUB */ + return CPLRecodeStub( pszSource, pszSrcEncoding, pszDstEncoding ); +#endif /* CPL_RECODE_ICONV */ +} + +/************************************************************************/ +/* CPLRecodeFromWChar() */ +/************************************************************************/ + +/** + * Convert wchar_t string to UTF-8. + * + * Convert a wchar_t string into a multibyte utf-8 string. The only + * guaranteed supported source encoding is CPL_ENC_UCS2, and the only + * guaranteed supported destination encodings are CPL_ENC_UTF8, CPL_ENC_ASCII + * and CPL_ENC_ISO8859_1. In some cases (ie. using iconv()) other encodings + * may also be supported. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pwszSource the source wchar_t string, terminated with a 0 wchar_t. + * @param pszSrcEncoding the source encoding, typically CPL_ENC_UCS2. + * @param pszDstEncoding the destination encoding, typically CPL_ENC_UTF8. + * + * @return a zero terminated multi-byte string which should be freed with + * CPLFree(), or NULL if an error occurs. + * + * @since GDAL 1.6.0 + */ + +char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +#ifdef CPL_RECODE_ICONV +/* -------------------------------------------------------------------- */ +/* Conversions from CPL_ENC_UCS2 */ +/* to CPL_ENC_UTF8, CPL_ENC_ISO8859_1 and CPL_ENC_ASCII are well */ +/* handled by the stub implementation. */ +/* -------------------------------------------------------------------- */ + if ( (EQUAL(pszSrcEncoding, CPL_ENC_UCS2) || EQUAL(pszSrcEncoding, "WCHAR_T")) + && ( EQUAL(pszDstEncoding, CPL_ENC_UTF8) + || EQUAL(pszDstEncoding, CPL_ENC_ASCII) + || EQUAL(pszDstEncoding, CPL_ENC_ISO8859_1) ) ) + { + return CPLRecodeFromWCharStub( pwszSource, + pszSrcEncoding, pszDstEncoding ); + } + else + { + return CPLRecodeFromWCharIconv( pwszSource, + pszSrcEncoding, pszDstEncoding ); + } +#else /* CPL_RECODE_STUB */ + return CPLRecodeFromWCharStub( pwszSource, + pszSrcEncoding, pszDstEncoding ); +#endif /* CPL_RECODE_ICONV */ +} + +/************************************************************************/ +/* CPLRecodeToWChar() */ +/************************************************************************/ + +/** + * Convert UTF-8 string to a wchar_t string. + * + * Convert a 8bit, multi-byte per character input string into a wide + * character (wchar_t) string. The only guaranteed supported source encodings + * are CPL_ENC_UTF8, CPL_ENC_ASCII and CPL_ENC_ISO8869_1 (LATIN1). The only + * guaranteed supported destination encoding is CPL_ENC_UCS2. Other source + * and destination encodings may be supported depending on the underlying + * implementation. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource input multi-byte character string. + * @param pszSrcEncoding source encoding, typically CPL_ENC_UTF8. + * @param pszDstEncoding destination encoding, typically CPL_ENC_UCS2. + * + * @return the zero terminated wchar_t string (to be freed with CPLFree()) or + * NULL on error. + * + * @since GDAL 1.6.0 + */ + +wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +#ifdef CPL_RECODE_ICONV +/* -------------------------------------------------------------------- */ +/* Conversions to CPL_ENC_UCS2 */ +/* from CPL_ENC_UTF8, CPL_ENC_ISO8859_1 and CPL_ENC_ASCII are well */ +/* handled by the stub implementation. */ +/* -------------------------------------------------------------------- */ + if ( (EQUAL(pszDstEncoding, CPL_ENC_UCS2) || EQUAL(pszDstEncoding, "WCHAR_T")) + && ( EQUAL(pszSrcEncoding, CPL_ENC_UTF8) + || EQUAL(pszSrcEncoding, CPL_ENC_ASCII) + || EQUAL(pszSrcEncoding, CPL_ENC_ISO8859_1) ) ) + { + return CPLRecodeToWCharStub( pszSource, + pszSrcEncoding, pszDstEncoding ); + } + else + { + return CPLRecodeToWCharIconv( pszSource, + pszSrcEncoding, pszDstEncoding ); + } +#else /* CPL_RECODE_STUB */ + return CPLRecodeToWCharStub( pszSource, pszSrcEncoding, pszDstEncoding ); +#endif /* CPL_RECODE_ICONV */ +} + +/************************************************************************/ +/* CPLIsUTF8() */ +/************************************************************************/ + +/** + * Test if a string is encoded as UTF-8. + * + * @param pabyData input string to test + * @param nLen length of the input string, or -1 if the function must compute + * the string length. In which case it must be null terminated. + * @return TRUE if the string is encoded as UTF-8. FALSE otherwise + * + * @since GDAL 1.7.0 + */ +int CPLIsUTF8(const char* pabyData, int nLen) +{ + return CPLIsUTF8Stub( pabyData, nLen ); +} + +/************************************************************************/ +/* CPLForceToASCII() */ +/************************************************************************/ + +/** + * Return a new string that is made only of ASCII characters. If non-ASCII + * characters are found in the input string, they will be replaced by the + * provided replacement character. + * + * @param pabyData input string to test + * @param nLen length of the input string, or -1 if the function must compute + * the string length. In which case it must be null terminated. + * @param chReplacementChar character which will be used when the input stream + * contains a non ASCII character. Must be valid ASCII ! + * + * @return a new string that must be freed with CPLFree(). + * + * @since GDAL 1.7.0 + */ +char CPL_DLL *CPLForceToASCII(const char* pabyData, int nLen, char chReplacementChar) +{ + if (nLen < 0) + nLen = strlen(pabyData); + char* pszOutputString = (char*)CPLMalloc(nLen + 1); + int i; + for(i=0;i 127) + pszOutputString[i] = chReplacementChar; + else + pszOutputString[i] = pabyData[i]; + } + pszOutputString[i] = '\0'; + return pszOutputString; +} + +/************************************************************************/ +/* CPLEncodingCharSize() */ +/************************************************************************/ + +/** + * Return bytes per character for encoding. + * + * This function returns the size in bytes of the smallest character + * in this encoding. For fixed width encodings (ASCII, UCS-2, UCS-4) this + * is straight forward. For encodings like UTF8 and UTF16 which represent + * some characters as a sequence of atomic character sizes the function + * still returns the atomic character size (1 for UTF8, 2 for UTF16). + * + * This function will return the correct value for well known encodings + * with corresponding CPL_ENC_ values. It may not return the correct value + * for other encodings even if they are supported by the underlying iconv + * or windows transliteration services. Hopefully it will improve over time. + * + * @param pszEncoding the name of the encoding. + * + * @return the size of a minimal character in bytes or -1 if the size is + * unknown. + */ + +int CPLEncodingCharSize( const char *pszEncoding ) + +{ + if( EQUAL(pszEncoding,CPL_ENC_UTF8) ) + return 1; + else if( EQUAL(pszEncoding,CPL_ENC_UTF16) ) + return 2; + else if( EQUAL(pszEncoding,CPL_ENC_UCS2) ) + return 2; + else if( EQUAL(pszEncoding,CPL_ENC_UCS4) ) + return 4; + else if( EQUAL(pszEncoding,CPL_ENC_ASCII) ) + return 1; + else if( EQUALN(pszEncoding,"ISO-8859-",9) ) + return 1; + else + return -1; +} + +/************************************************************************/ +/* CPLClearRecodeWarningFlags() */ +/************************************************************************/ + +void CPLClearRecodeWarningFlags() +{ +#ifdef CPL_RECODE_ICONV + CPLClearRecodeIconvWarningFlags(); +#endif + CPLClearRecodeStubWarningFlags(); +} + + +/************************************************************************/ +/* CPLStrlenUTF8() */ +/************************************************************************/ + +/** + * Return the number of UTF-8 characters of a nul-terminated string. + * + * This is different from strlen() which returns the number of bytes. + * + * @param pszUTF8Str a nul-terminated UTF-8 string + * + * @return the number of UTF-8 characters. + */ + +int CPLStrlenUTF8(const char *pszUTF8Str) { + int i = 0, j = 0; + while (pszUTF8Str[i]) { + if ((pszUTF8Str[i] & 0xc0) != 0x80) j++; + i++; + } + return j; +} + diff --git a/cpl/cpl_recode_stub.cpp b/cpl/cpl_recode_stub.cpp index 2625740..97bd705 100644 --- a/cpl/cpl_recode_stub.cpp +++ b/cpl/cpl_recode_stub.cpp @@ -1,9 +1,11 @@ /********************************************************************** - * $Id: cpl_recode_stub.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $ + * $Id: cpl_recode_stub.cpp 27044 2014-03-16 23:41:27Z rouault $ * - * Name: cpl_recode.cpp + * Name: cpl_recode_stub.cpp * Project: CPL - Common Portability Library - * Purpose: Character set recoding and char/wchar_t conversions. + * Purpose: Character set recoding and char/wchar_t conversions, stub + * implementation to be used if iconv() functionality is not + * available. * Author: Frank Warmerdam, warmerdam@pobox.com * * The bulk of this code is derived from the utf.c module from FLTK. It @@ -13,6 +15,7 @@ ********************************************************************** * Copyright (c) 2008, Frank Warmerdam * Copyright 2006 by Bill Spitzak and others. + * Copyright (c) 2009-2014, Even Rouault * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -29,9 +32,7 @@ #include "cpl_string.h" -CPL_CVSID("$Id: cpl_recode_stub.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $"); - -#define CPL_RECODE_STUB +CPL_CVSID("$Id: cpl_recode_stub.cpp 27044 2014-03-16 23:41:27Z rouault $"); #ifdef CPL_RECODE_STUB @@ -46,6 +47,15 @@ static unsigned utf8froma(char* dst, unsigned dstlen, const char* src, unsigned srclen); static int utf8test(const char* src, unsigned srclen); +#ifdef _WIN32 + +#include +#include + +static char* CPLWin32Recode( const char* src, + unsigned src_code_page, unsigned dst_code_page ); +#endif + #ifdef FUTURE_NEEDS static const char* utf8fwd(const char* p, const char* start, const char* end); static const char* utf8back(const char* p, const char* start, const char*end); @@ -59,8 +69,29 @@ static int utf8bytes(unsigned ucs); /* ==================================================================== */ /************************************************************************/ +static int bHaveWarned1 = FALSE; +static int bHaveWarned2 = FALSE; +static int bHaveWarned3 = FALSE; +static int bHaveWarned4 = FALSE; +static int bHaveWarned5 = FALSE; +static int bHaveWarned6 = FALSE; + +/************************************************************************/ +/* CPLClearRecodeStubWarningFlags() */ +/************************************************************************/ + +void CPLClearRecodeStubWarningFlags() +{ + bHaveWarned1 = FALSE; + bHaveWarned2 = FALSE; + bHaveWarned3 = FALSE; + bHaveWarned4 = FALSE; + bHaveWarned5 = FALSE; + bHaveWarned6 = FALSE; +} + /************************************************************************/ -/* CPLRecode() */ +/* CPLRecodeStub() */ /************************************************************************/ /** @@ -76,31 +107,18 @@ static int utf8bytes(unsigned ucs); * * If an error occurs an error may, or may not be posted with CPLError(). * - * @param pszSource a NUL terminated string. + * @param pszSource a NULL terminated string. * @param pszSrcEncoding the source encoding. * @param pszDstEncoding the destination encoding. * - * @return a NUL terminated string which should be freed with CPLFree(). - * - * @since GDAL 1.6.0 + * @return a NULL terminated string which should be freed with CPLFree(). */ -char CPL_DLL *CPLRecode( const char *pszSource, - const char *pszSrcEncoding, - const char *pszDstEncoding ) +char *CPLRecodeStub( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) { -/* -------------------------------------------------------------------- */ -/* Handle a few common short cuts. */ -/* -------------------------------------------------------------------- */ - if( strcmp(pszSrcEncoding,pszDstEncoding) == 0 ) - return CPLStrdup(pszSource); - - if( strcmp(pszSrcEncoding,CPL_ENC_ASCII) == 0 - && (strcmp(pszDstEncoding,CPL_ENC_UTF8) == 0 - || strcmp(pszDstEncoding,CPL_ENC_ISO8859_1) == 0) ) - return CPLStrdup(pszSource); - /* -------------------------------------------------------------------- */ /* If the source or destination is current locale(), we change */ /* it to ISO8859-1 since our stub implementation does not */ @@ -141,6 +159,34 @@ char CPL_DLL *CPLRecode( const char *pszSource, return pszResult; } +#ifdef _WIN32 +/* ---------------------------------------------------------------------*/ +/* CPXXX to UTF8 */ +/* ---------------------------------------------------------------------*/ + if( strncmp(pszSrcEncoding,"CP",2) == 0 + && strcmp(pszDstEncoding,CPL_ENC_UTF8) == 0 ) + { + int nCode = atoi( pszSrcEncoding + 2 ); + if( nCode > 0 ) { + return CPLWin32Recode( pszSource, nCode, CP_UTF8 ); + } + else if( EQUAL(pszSrcEncoding, "CP_OEMCP") ) + return CPLWin32Recode( pszSource, CP_OEMCP, CP_UTF8 ); + } + +/* ---------------------------------------------------------------------*/ +/* UTF8 to CPXXX */ +/* ---------------------------------------------------------------------*/ + if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) == 0 + && strncmp(pszDstEncoding,"CP",2) == 0 ) + { + int nCode = atoi( pszDstEncoding + 2 ); + if( nCode > 0 ) { + return CPLWin32Recode( pszSource, CP_UTF8, nCode ); + } + } +#endif + /* -------------------------------------------------------------------- */ /* Anything else to UTF-8 is treated as ISO8859-1 to UTF-8 with */ /* a one-time warning. */ @@ -149,11 +195,10 @@ char CPL_DLL *CPLRecode( const char *pszSource, { int nCharCount = strlen(pszSource); char *pszResult = (char *) CPLCalloc(1,nCharCount*2+1); - static int bHaveWarned = FALSE; - if( !bHaveWarned ) + if( !bHaveWarned1 ) { - bHaveWarned = 1; + bHaveWarned1 = 1; CPLError( CE_Warning, CPLE_AppDefined, "Recode from %s to UTF-8 not supported, treated as ISO8859-1 to UTF-8.", pszSrcEncoding ); @@ -173,11 +218,10 @@ char CPL_DLL *CPLRecode( const char *pszSource, { int nCharCount = strlen(pszSource); char *pszResult = (char *) CPLCalloc(1,nCharCount+1); - static int bHaveWarned = FALSE; - if( !bHaveWarned ) + if( !bHaveWarned2 ) { - bHaveWarned = 1; + bHaveWarned2 = 1; CPLError( CE_Warning, CPLE_AppDefined, "Recode from UTF-8 to %s not supported, treated as UTF-8 to ISO8859-1.", pszDstEncoding ); @@ -192,11 +236,9 @@ char CPL_DLL *CPLRecode( const char *pszSource, /* Everything else is treated as a no-op with a warning. */ /* -------------------------------------------------------------------- */ { - static int bHaveWarned = FALSE; - - if( !bHaveWarned ) + if( !bHaveWarned3 ) { - bHaveWarned = 1; + bHaveWarned3 = 1; CPLError( CE_Warning, CPLE_AppDefined, "Recode from %s to %s not supported, no change applied.", pszSrcEncoding, pszDstEncoding ); @@ -207,7 +249,7 @@ char CPL_DLL *CPLRecode( const char *pszSource, } /************************************************************************/ -/* CPLRecodeFromWChar() */ +/* CPLRecodeFromWCharStub() */ /************************************************************************/ /** @@ -230,27 +272,26 @@ char CPL_DLL *CPLRecode( const char *pszSource, * * @return a zero terminated multi-byte string which should be freed with * CPLFree(), or NULL if an error occurs. - * - * @since GDAL 1.6.0 */ -char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, - const char *pszSrcEncoding, - const char *pszDstEncoding ) +char *CPLRecodeFromWCharStub( const wchar_t *pwszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) { /* -------------------------------------------------------------------- */ /* We try to avoid changes of character set. We are just */ /* providing for unicode to unicode. */ /* -------------------------------------------------------------------- */ - if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) != 0 + if( strcmp(pszSrcEncoding,"WCHAR_T") != 0 && + strcmp(pszSrcEncoding,CPL_ENC_UTF8) != 0 && strcmp(pszSrcEncoding,CPL_ENC_UTF16) != 0 && strcmp(pszSrcEncoding,CPL_ENC_UCS2) != 0 && strcmp(pszSrcEncoding,CPL_ENC_UCS4) != 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "Stub recoding implementation does not support\n" - "CPLRecodeFromWChar(...,%s,%s)", + "CPLRecodeFromWCharStub(...,%s,%s)", pszSrcEncoding, pszDstEncoding ); return NULL; } @@ -272,6 +313,12 @@ char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, nDstBufSize = nSrcLen * 4 + 1; pszResult = (char *) CPLMalloc(nDstBufSize); // nearly worst case. + if (nSrcLen == 0) + { + pszResult[0] = '\0'; + return pszResult; + } + /* -------------------------------------------------------------------- */ /* Convert, and confirm we had enough space. */ /* -------------------------------------------------------------------- */ @@ -289,7 +336,7 @@ char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, return pszResult; char *pszFinalResult = - CPLRecode( pszResult, CPL_ENC_UTF8, pszDstEncoding ); + CPLRecodeStub( pszResult, CPL_ENC_UTF8, pszDstEncoding ); CPLFree( pszResult ); @@ -297,7 +344,7 @@ char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, } /************************************************************************/ -/* CPLRecodeToWChar() */ +/* CPLRecodeToWCharStub() */ /************************************************************************/ /** @@ -325,9 +372,9 @@ char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, * @since GDAL 1.6.0 */ -wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, - const char *pszSrcEncoding, - const char *pszDstEncoding ) +wchar_t *CPLRecodeToWCharStub( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) { char *pszUTF8Source = (char *) pszSource; @@ -335,7 +382,7 @@ wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) != 0 && strcmp(pszSrcEncoding,CPL_ENC_ASCII) != 0 ) { - pszUTF8Source = CPLRecode( pszSource, pszSrcEncoding, CPL_ENC_UTF8 ); + pszUTF8Source = CPLRecodeStub( pszSource, pszSrcEncoding, CPL_ENC_UTF8 ); if( pszUTF8Source == NULL ) return NULL; } @@ -344,13 +391,14 @@ wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, /* We try to avoid changes of character set. We are just */ /* providing for unicode to unicode. */ /* -------------------------------------------------------------------- */ - if( strcmp(pszDstEncoding,CPL_ENC_UCS2) != 0 + if( strcmp(pszDstEncoding,"WCHAR_T") != 0 + && strcmp(pszDstEncoding,CPL_ENC_UCS2) != 0 && strcmp(pszDstEncoding,CPL_ENC_UCS4) != 0 && strcmp(pszDstEncoding,CPL_ENC_UTF16) != 0 ) { CPLError( CE_Failure, CPLE_AppDefined, "Stub recoding implementation does not support\n" - "CPLRecodeToWChar(...,%s,%s)", + "CPLRecodeToWCharStub(...,%s,%s)", pszSrcEncoding, pszDstEncoding ); return NULL; } @@ -384,50 +432,13 @@ wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, * * @since GDAL 1.7.0 */ -int CPLIsUTF8(const char* pabyData, int nLen) +int CPLIsUTF8Stub(const char* pabyData, int nLen) { if (nLen < 0) nLen = strlen(pabyData); return utf8test(pabyData, (unsigned)nLen) != 0; } -/************************************************************************/ -/* CPLForceToASCII() */ -/************************************************************************/ - -/** - * Return a new string that is made only of ASCII characters. If non-ASCII - * characters are found in the input string, they will be replaced by the - * provided replacement character. - * - * @param pabyData input string to test - * @param nLen length of the input string, or -1 if the function must compute - * the string length. In which case it must be null terminated. - * @param chReplacementChar character which will be used when the input stream - * contains a non ASCII character. Must be valid ASCII ! - * - * @return a new string that must be freed with CPLFree(). - * - * @since GDAL 1.7.0 - */ -char CPL_DLL *CPLForceToASCII(const char* pabyData, int nLen, char chReplacementChar) -{ - if (nLen < 0) - nLen = strlen(pabyData); - char* pszOutputString = (char*)CPLMalloc(nLen + 1); - int i; - for(i=0;i 127) - pszOutputString[i] = chReplacementChar; - else - pszOutputString[i] = pabyData[i]; - } - pszOutputString[i] = '\0'; - return pszOutputString; -} - - /************************************************************************/ /* ==================================================================== */ /* UTF.C code from FLTK with some modifications. */ @@ -856,7 +867,17 @@ static unsigned utf8toa(const char* src, unsigned srclen, int len; unsigned ucs = utf8decode(p,e,&len); p += len; if (ucs < 0x100) dst[count] = (char)ucs; - else dst[count] = '?'; + else + { + if (!bHaveWarned4) + { + bHaveWarned4 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters couldn't be converted correctly from UTF-8 to ISO-8859-1.\n" + "This warning will not be emitted anymore."); + } + dst[count] = '?'; + } } if (++count >= dstlen) {dst[count-1] = 0; break;} } @@ -1026,6 +1047,123 @@ static unsigned utf8froma(char* dst, unsigned dstlen, return count; } +#ifdef _WIN32 + +/************************************************************************/ +/* CPLWin32Recode() */ +/************************************************************************/ + +/* Convert an CODEPAGE (ie normal c-string) byte stream + to another CODEPAGE (ie normal c-string) byte stream. + + \a src is target c-string byte stream (including a null terminator). + \a src_code_page is target c-string byte code page. + \a dst_code_page is destination c-string byte code page. + + UTF7 65000 + UTF8 65001 + OEM-US 437 + OEM-ALABIC 720 + OEM-GREEK 737 + OEM-BALTIC 775 + OEM-MLATIN1 850 + OEM-LATIN2 852 + OEM-CYRILLIC 855 + OEM-TURKISH 857 + OEM-MLATIN1P 858 + OEM-HEBREW 862 + OEM-RUSSIAN 866 + + THAI 874 + SJIS 932 + GBK 936 + KOREA 949 + BIG5 950 + + EUROPE 1250 + CYRILLIC 1251 + LATIN1 1252 + GREEK 1253 + TURKISH 1254 + HEBREW 1255 + ARABIC 1256 + BALTIC 1257 + VIETNAM 1258 + + ISO-LATIN1 28591 + ISO-LATIN2 28592 + ISO-LATIN3 28593 + ISO-BALTIC 28594 + ISO-CYRILLIC 28595 + ISO-ARABIC 28596 + ISO-HEBREW 28598 + ISO-TURKISH 28599 + ISO-LATIN9 28605 + + ISO-2022-JP 50220 + +*/ + +char* CPLWin32Recode( const char* src, unsigned src_code_page, unsigned dst_code_page ) +{ + /* Convert from source code page to Unicode */ + + /* Compute the length in wide characters */ + int wlen = MultiByteToWideChar( src_code_page, MB_ERR_INVALID_CHARS, src, -1, 0, 0 ); + if (wlen == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) + { + if (!bHaveWarned5) + { + bHaveWarned5 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters could not be translated from CP%d. " + "This warning will not be emitted anymore.", src_code_page); + } + + /* Retry now without MB_ERR_INVALID_CHARS flag */ + wlen = MultiByteToWideChar( src_code_page, 0, src, -1, 0, 0 ); + } + + /* Do the actual conversion */ + wchar_t* tbuf = (wchar_t*)CPLCalloc(sizeof(wchar_t),wlen+1); + tbuf[wlen] = 0; + MultiByteToWideChar( src_code_page, 0, src, -1, tbuf, wlen+1 ); + + /* Convert from Unicode to destination code page */ + + /* Compute the length in chars */ + BOOL bUsedDefaultChar = FALSE; + int len; + if ( dst_code_page == CP_UTF7 || dst_code_page == CP_UTF8 ) + len = WideCharToMultiByte( dst_code_page, 0, tbuf, -1, 0, 0, 0, NULL ); + else + len = WideCharToMultiByte( dst_code_page, 0, tbuf, -1, 0, 0, 0, &bUsedDefaultChar ); + if (bUsedDefaultChar) + { + if (!bHaveWarned6) + { + bHaveWarned6 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters could not be translated to CP%d. " + "This warning will not be emitted anymore.", dst_code_page); + } + } + + /* Do the actual conversion */ + char* pszResult = (char*)CPLCalloc(sizeof(char),len+1); + WideCharToMultiByte( dst_code_page, 0, tbuf, -1, pszResult, len+1, 0, NULL ); + pszResult[len] = 0; + + /* Cleanup */ + CPLFree(tbuf); + + return pszResult; +} + +#endif + + + /* ** For now we disable the rest which is locale() related. We may need ** parts of it later. diff --git a/cpl/cpl_spawn.cpp b/cpl/cpl_spawn.cpp new file mode 100644 index 0000000..0705218 --- /dev/null +++ b/cpl/cpl_spawn.cpp @@ -0,0 +1,1014 @@ +/********************************************************************** + * $Id: cpl_spawn.cpp 27328 2014-05-14 16:23:44Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement CPLSystem(). + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_spawn.h" + +#include "cpl_error.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#define PIPE_BUFFER_SIZE 4096 + +#define IN_FOR_PARENT 0 +#define OUT_FOR_PARENT 1 + +CPL_CVSID("$Id: cpl_spawn.cpp 27328 2014-05-14 16:23:44Z rouault $"); + +static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout); + +//int CPL_DLL CPLSystem( const char* pszApplicationName, const char* pszCommandLine ); + +/************************************************************************/ +/* FillPipeFromFile() */ +/************************************************************************/ + +static void FillPipeFromFile(VSILFILE* fin, CPL_FILE_HANDLE pipe_fd) +{ + char buf[PIPE_BUFFER_SIZE]; + while(TRUE) + { + int nRead = (int)VSIFReadL(buf, 1, PIPE_BUFFER_SIZE, fin); + if( nRead <= 0 ) + break; + if (!CPLPipeWrite(pipe_fd, buf, nRead)) + break; + } +} + +/************************************************************************/ +/* CPLSpawn() */ +/************************************************************************/ + +/** + * Runs an executable in another process. + * + * This function runs an executable, wait for it to finish and returns + * its exit code. + * + * It is implemented as CreateProcess() on Windows platforms, and fork()/exec() + * on other platforms. + * + * @param papszArgv argument list of the executable to run. papszArgv[0] is the + * name of the executable + * @param fin File handle for input data to feed to the standard input of the + * sub-process. May be NULL. + * @param fout File handle for output data to extract from the standard output of the + * sub-process. May be NULL. + * @param bDisplayErr Set to TRUE to emit the content of the standard error stream of + * the sub-process with CPLError(). + * + * @return the exit code of the spawned process, or -1 in case of error. + * + * @since GDAL 1.10.0 + */ + +int CPLSpawn(const char * const papszArgv[], VSILFILE* fin, VSILFILE* fout, + int bDisplayErr) +{ + CPLSpawnedProcess* sp = CPLSpawnAsync(NULL, papszArgv, TRUE, TRUE, TRUE, NULL); + if( sp == NULL ) + return -1; + + CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp); + if (fin != NULL) + FillPipeFromFile(fin, in_child); + CPLSpawnAsyncCloseOutputFileHandle(sp); + + CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp); + if (fout != NULL) + FillFileFromPipe(out_child, fout); + CPLSpawnAsyncCloseInputFileHandle(sp); + + CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp); + CPLString osName; + osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID()); + VSILFILE* ferr = VSIFOpenL(osName.c_str(), "w"); + + FillFileFromPipe(err_child, ferr); + CPLSpawnAsyncCloseErrorFileHandle(sp); + + VSIFCloseL(ferr); + vsi_l_offset nDataLength = 0; + GByte* pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE); + if( nDataLength > 0 ) + pData[nDataLength-1] = '\0'; + if( pData && strstr((const char*)pData, "An error occured while forking process") != NULL ) + bDisplayErr = TRUE; + if( pData && bDisplayErr ) + CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0], pData); + CPLFree(pData); + + return CPLSpawnAsyncFinish(sp, TRUE, FALSE); +} + +#if defined(WIN32) + +#include + +#if 0 +/************************************************************************/ +/* CPLSystem() */ +/************************************************************************/ + +int CPLSystem( const char* pszApplicationName, const char* pszCommandLine ) +{ + int nRet = -1; + PROCESS_INFORMATION processInfo; + STARTUPINFO startupInfo; + ZeroMemory( &processInfo, sizeof(PROCESS_INFORMATION) ); + ZeroMemory( &startupInfo, sizeof(STARTUPINFO) ); + startupInfo.cb = sizeof(STARTUPINFO); + + char* pszDupedCommandLine = (pszCommandLine) ? CPLStrdup(pszCommandLine) : NULL; + + if( !CreateProcess( pszApplicationName, + pszDupedCommandLine, + NULL, + NULL, + FALSE, + CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &startupInfo, + &processInfo) ) + { + DWORD err = GetLastError(); + CPLDebug("CPL", "'%s' failed : err = %d", pszCommandLine, (int)err); + nRet = -1; + } + else + { + WaitForSingleObject( processInfo.hProcess, INFINITE ); + + DWORD exitCode; + + // Get the exit code. + int err = GetExitCodeProcess(processInfo.hProcess, &exitCode); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + + if( !err ) + { + CPLDebug("CPL", "GetExitCodeProcess() failed : err = %d", err); + } + else + nRet = exitCode; + } + + CPLFree(pszDupedCommandLine); + + return nRet; +} +#endif + +/************************************************************************/ +/* CPLPipeRead() */ +/************************************************************************/ + +int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length) +{ + GByte* pabyData = (GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + DWORD nRead = 0; + if (!ReadFile( fin, pabyData, nRemain, &nRead, NULL)) + return FALSE; + pabyData += nRead; + nRemain -= nRead; + } + return TRUE; +} + +/************************************************************************/ +/* CPLPipeWrite() */ +/************************************************************************/ + +int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length) +{ + const GByte* pabyData = (const GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + DWORD nWritten = 0; + if (!WriteFile(fout, pabyData, nRemain, &nWritten, NULL)) + return FALSE; + pabyData += nWritten; + nRemain -= nWritten; + } + return TRUE; +} + +/************************************************************************/ +/* FillFileFromPipe() */ +/************************************************************************/ + +static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout) +{ + char buf[PIPE_BUFFER_SIZE]; + while(TRUE) + { + DWORD nRead; + if (!ReadFile( pipe_fd, buf, PIPE_BUFFER_SIZE, &nRead, NULL)) + break; + if (nRead <= 0) + break; + int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout); + if (nWritten < (int)nRead) + break; + } +} + +struct _CPLSpawnedProcess +{ + HANDLE hProcess; + DWORD nProcessId; + HANDLE hThread; + CPL_FILE_HANDLE fin; + CPL_FILE_HANDLE fout; + CPL_FILE_HANDLE ferr; +}; + +/************************************************************************/ +/* CPLSpawnAsync() */ +/************************************************************************/ + +CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE), + const char * const papszArgv[], + int bCreateInputPipe, + int bCreateOutputPipe, + int bCreateErrorPipe, + char** papszOptions) +{ + HANDLE pipe_in[2] = {NULL, NULL}; + HANDLE pipe_out[2] = {NULL, NULL}; + HANDLE pipe_err[2] = {NULL, NULL}; + SECURITY_ATTRIBUTES saAttr; + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + CPLString osCommandLine; + int i; + CPLSpawnedProcess* p = NULL; + + if( papszArgv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "On Windows, papszArgv argument must not be NULL"); + return NULL; + } + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if( bCreateInputPipe ) + { + if (!CreatePipe(&pipe_in[IN_FOR_PARENT],&pipe_in[OUT_FOR_PARENT],&saAttr, 0)) + goto err_pipe; + /* The child must not inherit from the write side of the pipe_in */ + if (!SetHandleInformation(pipe_in[OUT_FOR_PARENT],HANDLE_FLAG_INHERIT,0)) + goto err_pipe; + } + + if( bCreateOutputPipe ) + { + if (!CreatePipe(&pipe_out[IN_FOR_PARENT],&pipe_out[OUT_FOR_PARENT],&saAttr, 0)) + goto err_pipe; + /* The child must not inherit from the read side of the pipe_out */ + if (!SetHandleInformation(pipe_out[IN_FOR_PARENT],HANDLE_FLAG_INHERIT,0)) + goto err_pipe; + } + + if( bCreateErrorPipe ) + { + if (!CreatePipe(&pipe_err[IN_FOR_PARENT],&pipe_err[OUT_FOR_PARENT],&saAttr, 0)) + goto err_pipe; + /* The child must not inherit from the read side of the pipe_err */ + if (!SetHandleInformation(pipe_err[IN_FOR_PARENT],HANDLE_FLAG_INHERIT,0)) + goto err_pipe; + } + + memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION)); + memset(&siStartInfo, 0, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdInput = (bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : GetStdHandle(STD_INPUT_HANDLE); + siStartInfo.hStdOutput = (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : GetStdHandle(STD_OUTPUT_HANDLE); + siStartInfo.hStdError = (bCreateErrorPipe) ? pipe_err[OUT_FOR_PARENT] : GetStdHandle(STD_ERROR_HANDLE); + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + for(i=0;papszArgv[i] != NULL;i++) + { + if (i > 0) + osCommandLine += " "; + /* We need to quote arguments with spaces in them (if not already done) */ + if( strchr(papszArgv[i], ' ') != NULL && + papszArgv[i][0] != '"' ) + { + osCommandLine += "\""; + osCommandLine += papszArgv[i]; + osCommandLine += "\""; + } + else + osCommandLine += papszArgv[i]; + } + + if (!CreateProcess(NULL, + (CHAR*)osCommandLine.c_str(), + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, + &piProcInfo)) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s", + osCommandLine.c_str()); + goto err; + } + + /* Close unused end of pipe */ + if( bCreateInputPipe ) + CloseHandle(pipe_in[IN_FOR_PARENT]); + if( bCreateOutputPipe ) + CloseHandle(pipe_out[OUT_FOR_PARENT]); + if( bCreateErrorPipe ) + CloseHandle(pipe_err[OUT_FOR_PARENT]); + + p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess)); + p->hProcess = piProcInfo.hProcess; + p->nProcessId = piProcInfo.dwProcessId; + p->hThread = piProcInfo.hThread; + p->fin = pipe_out[IN_FOR_PARENT]; + p->fout = pipe_in[OUT_FOR_PARENT]; + p->ferr = pipe_err[IN_FOR_PARENT]; + return p; + +err_pipe: + CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe"); +err: + for(i=0;i<2;i++) + { + if (pipe_in[i] != NULL) + CloseHandle(pipe_in[i]); + if (pipe_out[i] != NULL) + CloseHandle(pipe_out[i]); + if (pipe_err[i] != NULL) + CloseHandle(pipe_err[i]); + } + + return NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetChildProcessId() */ +/************************************************************************/ + +CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p) +{ + return p->nProcessId; +} + +/************************************************************************/ +/* CPLSpawnAsyncFinish() */ +/************************************************************************/ + +int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill) +{ + // Get the exit code. + DWORD exitCode = -1; + + if( bWait ) + { + WaitForSingleObject( p->hProcess, INFINITE ); + GetExitCodeProcess(p->hProcess, &exitCode); + } + else + exitCode = 0; + + CloseHandle(p->hProcess); + CloseHandle(p->hThread); + + CPLSpawnAsyncCloseInputFileHandle(p); + CPLSpawnAsyncCloseOutputFileHandle(p); + CPLSpawnAsyncCloseErrorFileHandle(p); + CPLFree(p); + + return (int)exitCode; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseInputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fin != NULL ) + CloseHandle(p->fin); + p->fin = NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseOutputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fout != NULL ) + CloseHandle(p->fout); + p->fout = NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseErrorFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p) +{ + if( p->ferr != NULL ) + CloseHandle(p->ferr); + p->ferr = NULL; +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_POSIX_SPAWNP + #include + #ifdef __APPLE__ + #include + #endif + #if defined(__APPLE__) && (!defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE==0) + #include + #define environ (*_NSGetEnviron()) + #else + extern char** environ; + #endif +#endif + +#if 0 +/************************************************************************/ +/* CPLSystem() */ +/************************************************************************/ + +/** + * Runs an executable in another process. + * + * This function runs an executable, wait for it to finish and returns + * its exit code. + * + * It is implemented as CreateProcess() on Windows platforms, and system() + * on other platforms. + * + * @param pszApplicationName the lpApplicationName for Windows (might be NULL), + * or ignored on other platforms. + * @param pszCommandLine the command line, starting with the executable name + * + * @return the exit code of the spawned process, or -1 in case of error. + * + * @since GDAL 1.10.0 + */ + +int CPLSystem( const char* pszApplicationName, const char* pszCommandLine ) +{ + return system(pszCommandLine); +} +#endif + +/************************************************************************/ +/* CPLPipeRead() */ +/************************************************************************/ + +/** + * Read data from the standard output of a forked process. + * + * @param p handle returned by CPLSpawnAsyncGetInputFileHandle(). + * @param data buffer in which to write. + * @param length number of bytes to read. + * + * @return TRUE in case of success. + * + * @since GDAL 1.10.0 + */ +int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length) +{ + GByte* pabyData = (GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + while(TRUE) + { + int n = read(fin, pabyData, nRemain); + if( n < 0 ) + { + if( errno == EINTR ) + continue; + else + return FALSE; + } + else if( n == 0 ) + return FALSE; + pabyData += n; + nRemain -= n; + break; + } + } + return TRUE; +} + +/************************************************************************/ +/* CPLPipeWrite() */ +/************************************************************************/ + +/** + * Write data to the standard input of a forked process. + * + * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle(). + * @param data buffer from which to read. + * @param length number of bytes to write. + * + * @return TRUE in case of success. + * + * @since GDAL 1.10.0 + */ +int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length) +{ + const GByte* pabyData = (const GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + while(TRUE) + { + int n = write(fout, pabyData, nRemain); + if( n < 0 ) + { + if( errno == EINTR ) + continue; + else + return FALSE; + } + pabyData += n; + nRemain -= n; + break; + } + } + return TRUE; +} + +/************************************************************************/ +/* FillFileFromPipe() */ +/************************************************************************/ + +static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout) +{ + char buf[PIPE_BUFFER_SIZE]; + while(TRUE) + { + int nRead = read(pipe_fd, buf, PIPE_BUFFER_SIZE); + if (nRead <= 0) + break; + int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout); + if (nWritten < nRead) + break; + } +} + +/************************************************************************/ +/* CPLSpawnAsync() */ +/************************************************************************/ + +struct _CPLSpawnedProcess +{ + pid_t pid; + CPL_FILE_HANDLE fin; + CPL_FILE_HANDLE fout; + CPL_FILE_HANDLE ferr; +#ifdef HAVE_POSIX_SPAWNP + int bFreeActions; + posix_spawn_file_actions_t actions; +#endif +}; + +/** + * Runs an executable in another process (or fork the current process) + * and return immediately. + * + * This function launches an executable and returns immediately, while letting + * the sub-process to run asynchronously. + * + * It is implemented as CreateProcess() on Windows platforms, and fork()/exec() + * on other platforms. + * + * On Unix, a pointer of function can be provided to run in the child process, + * without exec()'ing a new executable. + * + * @param pfnMain the function to run in the child process (Unix only). + * @param papszArgv argument list of the executable to run. papszArgv[0] is the + * name of the executable. + * @param bCreateInputPipe set to TRUE to create a pipe for the child input stream. + * @param bCreateOutputPipe set to TRUE to create a pipe for the child output stream. + * @param bCreateErrorPipe set to TRUE to create a pipe for the child error stream. + * + * @return a handle, that must be freed with CPLSpawnAsyncFinish() + * + * @since GDAL 1.10.0 + */ +CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE), + const char * const papszArgv[], + int bCreateInputPipe, + int bCreateOutputPipe, + int bCreateErrorPipe, + char** papszOptions) +{ + pid_t pid; + int pipe_in[2] = { -1, -1 }; + int pipe_out[2] = { -1, -1 }; + int pipe_err[2] = { -1, -1 }; + int i; + char** papszArgvDup = CSLDuplicate((char**)papszArgv); + int bDup2In = bCreateInputPipe, + bDup2Out = bCreateOutputPipe, + bDup2Err = bCreateErrorPipe; + + if ((bCreateInputPipe && pipe(pipe_in)) || + (bCreateOutputPipe && pipe(pipe_out)) || + (bCreateErrorPipe && pipe(pipe_err))) + goto err_pipe; + + /* If we don't do any file actions, posix_spawnp() might be implemented */ + /* efficiently as a vfork()/exec() pair (or if it is not available, we */ + /* can use vfork()/exec()), so if the child is cooperative */ + /* we pass the pipe handles as commandline arguments */ + if( papszArgv != NULL ) + { + for(i=0; papszArgvDup[i] != NULL; i++) + { + if( bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0 ) + { + CPLFree(papszArgvDup[i]); + papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d", + pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT])); + bDup2In = FALSE; + } + else if( bCreateOutputPipe && strcmp(papszArgvDup[i], "{pipe_out}") == 0 ) + { + CPLFree(papszArgvDup[i]); + papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d", + pipe_out[OUT_FOR_PARENT], pipe_out[IN_FOR_PARENT])); + bDup2Out = FALSE; + } + else if( bCreateErrorPipe && strcmp(papszArgvDup[i], "{pipe_err}") == 0 ) + { + CPLFree(papszArgvDup[i]); + papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d", + pipe_err[OUT_FOR_PARENT], pipe_err[IN_FOR_PARENT])); + bDup2Err = FALSE; + } + } + } + +#ifdef HAVE_POSIX_SPAWNP + if( papszArgv != NULL ) + { + int bHasActions = FALSE; + posix_spawn_file_actions_t actions; + + if( bDup2In ) + { + if( !bHasActions ) posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT], fileno(stdin)); + posix_spawn_file_actions_addclose(&actions, pipe_in[OUT_FOR_PARENT]); + bHasActions = TRUE; + } + + if( bDup2Out ) + { + if( !bHasActions ) posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT], fileno(stdout)); + posix_spawn_file_actions_addclose(&actions, pipe_out[IN_FOR_PARENT]); + bHasActions = TRUE; + } + + if( bDup2Err ) + { + if( !bHasActions ) posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT], fileno(stderr)); + posix_spawn_file_actions_addclose(&actions, pipe_err[IN_FOR_PARENT]); + bHasActions = TRUE; + } + + if( posix_spawnp(&pid, papszArgvDup[0], + bHasActions ? &actions : NULL, + NULL, + (char* const*) papszArgvDup, + environ) != 0 ) + { + if( bHasActions ) + posix_spawn_file_actions_destroy(&actions); + CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed"); + goto err; + } + + CSLDestroy(papszArgvDup); + + /* Close unused end of pipe */ + if( bCreateInputPipe ) + close(pipe_in[IN_FOR_PARENT]); + if( bCreateOutputPipe ) + close(pipe_out[OUT_FOR_PARENT]); + if( bCreateErrorPipe ) + close(pipe_err[OUT_FOR_PARENT]); + + /* Ignore SIGPIPE */ + #ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); + #endif + CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess)); + if( bHasActions ) + memcpy(&p->actions, &actions, sizeof(actions)); + p->bFreeActions = bHasActions; + p->pid = pid; + p->fin = pipe_out[IN_FOR_PARENT]; + p->fout = pipe_in[OUT_FOR_PARENT]; + p->ferr = pipe_err[IN_FOR_PARENT]; + return p; + } +#endif // #ifdef HAVE_POSIX_SPAWNP + +#ifdef HAVE_VFORK + if( papszArgv != NULL && !bDup2In && !bDup2Out && !bDup2Err ) + pid = vfork(); + else +#endif + pid = fork(); + if (pid == 0) + { + /* Close unused end of pipe */ + if( bDup2In ) + close(pipe_in[OUT_FOR_PARENT]); + if( bDup2Out ) + close(pipe_out[IN_FOR_PARENT]); + if( bDup2Err ) + close(pipe_err[IN_FOR_PARENT]); + +#ifndef HAVE_POSIX_SPAWNP + if( papszArgv != NULL ) + { + if( bDup2In ) + dup2(pipe_in[IN_FOR_PARENT], fileno(stdin)); + if( bDup2Out ) + dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout)); + if( bDup2Err ) + dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr)); + + execvp(papszArgvDup[0], (char* const*) papszArgvDup); + + _exit(1); + } + else +#endif // HAVE_POSIX_SPAWNP + { + if( bCreateErrorPipe ) + close(pipe_err[OUT_FOR_PARENT]); + + int nRet = 0; + if (pfnMain != NULL) + nRet = pfnMain((bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : fileno(stdin), + (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : fileno(stdout)); + _exit(nRet); + } + } + else if( pid > 0 ) + { + CSLDestroy(papszArgvDup); + + /* Close unused end of pipe */ + if( bCreateInputPipe ) + close(pipe_in[IN_FOR_PARENT]); + if( bCreateOutputPipe ) + close(pipe_out[OUT_FOR_PARENT]); + if( bCreateErrorPipe ) + close(pipe_err[OUT_FOR_PARENT]); + + /* Ignore SIGPIPE */ +#ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); +#endif + CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess)); +#ifdef HAVE_POSIX_SPAWNP + p->bFreeActions = FALSE; +#endif + p->pid = pid; + p->fin = pipe_out[IN_FOR_PARENT]; + p->fout = pipe_in[OUT_FOR_PARENT]; + p->ferr = pipe_err[IN_FOR_PARENT]; + return p; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Fork failed"); + goto err; + } + +err_pipe: + CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe"); +err: + CSLDestroy(papszArgvDup); + for(i=0;i<2;i++) + { + if (pipe_in[i] >= 0) + close(pipe_in[i]); + if (pipe_out[i] >= 0) + close(pipe_out[i]); + if (pipe_err[i] >= 0) + close(pipe_err[i]); + } + + return NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetChildProcessId() */ +/************************************************************************/ + +CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p) +{ + return p->pid; +} + +/************************************************************************/ +/* CPLSpawnAsyncFinish() */ +/************************************************************************/ + +/** + * Wait for the forked process to finish. + * + * @param p handle returned by CPLSpawnAsync() + * @param bWait set to TRUE to wait for the child to terminate. Otherwise the associated + * handles are just cleaned. + * @param bKill set to TRUE to force child termination (unimplemented right now). + * + * @return the return code of the forked process if bWait == TRUE, 0 otherwise + * + * @since GDAL 1.10.0 + */ +int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill) +{ + int status = 0; + + if( bWait ) + { + while(1) + { + status = -1; + int ret = waitpid (p->pid, &status, 0); + if (ret < 0) + { + if (errno != EINTR) + { + break; + } + } + else + break; + } + } + else + bWait = FALSE; + CPLSpawnAsyncCloseInputFileHandle(p); + CPLSpawnAsyncCloseOutputFileHandle(p); + CPLSpawnAsyncCloseErrorFileHandle(p); +#ifdef HAVE_POSIX_SPAWNP + if( p->bFreeActions ) + posix_spawn_file_actions_destroy(&p->actions); +#endif + CPLFree(p); + return status; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseInputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fin >= 0 ) + close(p->fin); + p->fin = -1; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseOutputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fout >= 0 ) + close(p->fout); + p->fout = -1; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseErrorFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p) +{ + if( p->ferr >= 0 ) + close(p->ferr); + p->ferr = -1; +} + +#endif + +/************************************************************************/ +/* CPLSpawnAsyncGetInputFileHandle() */ +/************************************************************************/ + +/** + * Return the file handle of the standard output of the forked process + * from which to read. + * + * @param p handle returned by CPLSpawnAsync(). + * + * @return the file handle. + * + * @since GDAL 1.10.0 + */ +CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess* p) +{ + return p->fin; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetOutputFileHandle() */ +/************************************************************************/ + +/** + * Return the file handle of the standard input of the forked process + * into which to write + * + * @param p handle returned by CPLSpawnAsync(). + * + * @return the file handle. + * + * @since GDAL 1.10.0 + */ +CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess* p) +{ + return p->fout; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetErrorFileHandle() */ +/************************************************************************/ + +/** + * Return the file handle of the standard error of the forked process + * from which to read. + * + * @param p handle returned by CPLSpawnAsync(). + * + * @return the file handle + * + * @since GDAL 1.10.0 + */ +CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess* p) +{ + return p->ferr; +} diff --git a/cpl/cpl_spawn.h b/cpl/cpl_spawn.h new file mode 100644 index 0000000..37036ba --- /dev/null +++ b/cpl/cpl_spawn.h @@ -0,0 +1,79 @@ +/********************************************************************** + * $Id: cpl_spawn.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement CPLSystem(). + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_SPAWN_H_INCLUDED +#define CPL_SPAWN_H_INCLUDED + +#include "cpl_vsi.h" + +CPL_C_START + +/* -------------------------------------------------------------------- */ +/* Spawn a process. */ +/* -------------------------------------------------------------------- */ + +int CPL_DLL CPLSpawn( const char * const papszArgv[], VSILFILE* fin, VSILFILE* fout, + int bDisplayErr ); + +#ifdef WIN32 +#include +typedef HANDLE CPL_FILE_HANDLE; +#define CPL_FILE_INVALID_HANDLE NULL +typedef DWORD CPL_PID; +#else +#include +typedef int CPL_FILE_HANDLE; +#define CPL_FILE_INVALID_HANDLE -1 +typedef pid_t CPL_PID; +#endif + +typedef struct _CPLSpawnedProcess CPLSpawnedProcess; + +CPLSpawnedProcess CPL_DLL* CPLSpawnAsync( int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE), + const char * const papszArgv[], + int bCreateInputPipe, + int bCreateOutputPipe, + int bCreateErrorPipe, + char** papszOptions ); +CPL_PID CPL_DLL CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p); +int CPL_DLL CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill); +CPL_FILE_HANDLE CPL_DLL CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess* p); +CPL_FILE_HANDLE CPL_DLL CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess* p); +CPL_FILE_HANDLE CPL_DLL CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess* p); +void CPL_DLL CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p); +void CPL_DLL CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p); +void CPL_DLL CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p); + +int CPL_DLL CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length); +int CPL_DLL CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length); + +CPL_C_END + +#endif // CPL_SPAWN_H_INCLUDED + diff --git a/cpl/cpl_string.cpp b/cpl/cpl_string.cpp index 21b1e43..f0d9b2c 100644 --- a/cpl/cpl_string.cpp +++ b/cpl/cpl_string.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_string.cpp 18179 2009-12-05 00:40:04Z warmerdam $ + * $Id: cpl_string.cpp 27434 2014-06-04 19:38:05Z rouault $ * * Name: cpl_string.cpp * Project: CPL - Common Portability Library @@ -8,6 +8,7 @@ * ********************************************************************** * Copyright (c) 1998, Daniel Morissette + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -54,7 +55,7 @@ # include #endif -CPL_CVSID("$Id: cpl_string.cpp 18179 2009-12-05 00:40:04Z warmerdam $"); +CPL_CVSID("$Id: cpl_string.cpp 27434 2014-06-04 19:38:05Z rouault $"); /*===================================================================== StringList manipulation functions. @@ -292,7 +293,7 @@ char **CSLMerge( char **papszOrig, char **papszOverride ) char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions) { - FILE *fp; + VSILFILE *fp; const char *pszLine; char **papszStrList=NULL; int nLines = 0; @@ -328,10 +329,8 @@ char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszO papszStrList[nLines + 1] = NULL; nLines ++; } - else if (CPLGetLastErrorType() != 0) - { + else break; - } } VSIFCloseL(fp); @@ -340,10 +339,13 @@ char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszO } else { - /* Unable to open file */ - CPLError( CE_Failure, CPLE_OpenFailed, - "CSLLoad2(\"%s\") failed: unable to open output file.", - pszFname ); + if (CSLFetchBoolean(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", TRUE)) + { + /* Unable to open file */ + CPLError( CE_Failure, CPLE_OpenFailed, + "CSLLoad2(\"%s\") failed: unable to open file.", + pszFname ); + } } return papszStrList; @@ -383,7 +385,7 @@ char **CSLLoad(const char *pszFname) **********************************************************************/ int CSLSave(char **papszStrList, const char *pszFname) { - FILE *fp; + VSILFILE *fp; int nLines = 0; if (papszStrList) @@ -634,7 +636,7 @@ char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete, /************************************************************************/ /** - * Find a string within a string list. + * Find a string within a string list (case insensitive). * * Returns the index of the entry in the string list that contains the * target string. The string in the string list must be a full match for @@ -663,6 +665,42 @@ int CSLFindString( char ** papszList, const char * pszTarget ) return -1; } +/************************************************************************/ +/* CSLFindStringCaseSensitive() */ +/************************************************************************/ + +/** + * Find a string within a string list(case sensitive) + * + * Returns the index of the entry in the string list that contains the + * target string. The string in the string list must be a full match for + * the target. + * + * @param papszList the string list to be searched. + * @param pszTarget the string to be searched for. + * + * @return the index of the string within the list or -1 on failure. + * + * @since GDAL 2.0 + */ + +int CSLFindStringCaseSensitive( char ** papszList, const char * pszTarget ) + +{ + int i; + + if( papszList == NULL ) + return -1; + + for( i = 0; papszList[i] != NULL; i++ ) + { + if( strcmp(papszList[i],pszTarget) == 0 ) + return i; + } + + return -1; +} + /************************************************************************/ /* CSLPartialFindString() */ /************************************************************************/ @@ -788,8 +826,9 @@ char ** CSLTokenizeString2( const char * pszString, int nCSLTFlags ) { - char **papszRetList = NULL; - int nRetMax = 0, nRetLen = 0; + if( pszString == NULL ) + return (char **) CPLCalloc(sizeof(char *),1); + CPLStringList oRetList; char *pszToken; int nTokenMax, nTokenLen; int bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS); @@ -897,43 +936,29 @@ char ** CSLTokenizeString2( const char * pszString, * Add the token. */ if( pszToken[0] != '\0' || bAllowEmptyTokens ) - { - if( nRetLen >= nRetMax - 1 ) - { - nRetMax = nRetMax * 2 + 10; - papszRetList = (char **) - CPLRealloc(papszRetList, sizeof(char*) * nRetMax ); - } - - papszRetList[nRetLen++] = CPLStrdup( pszToken ); - papszRetList[nRetLen] = NULL; - } + oRetList.AddString( pszToken ); } /* * If the last token was empty, then we need to capture * it now, as the loop would skip it. */ - if( *pszString == '\0' && bAllowEmptyTokens && nRetLen > 0 + if( *pszString == '\0' && bAllowEmptyTokens && oRetList.Count() > 0 && strchr(pszDelimiters,*(pszString-1)) != NULL ) { - if( nRetLen >= nRetMax - 1 ) - { - nRetMax = nRetMax * 2 + 10; - papszRetList = (char **) - CPLRealloc(papszRetList, sizeof(char*) * nRetMax ); - } - - papszRetList[nRetLen++] = CPLStrdup(""); - papszRetList[nRetLen] = NULL; + oRetList.AddString( "" ); } - if( papszRetList == NULL ) - papszRetList = (char **) CPLCalloc(sizeof(char *),1); - CPLFree( pszToken ); - return papszRetList; + if( oRetList.List() == NULL ) + { + // we prefer to return empty lists as a pointer to + // a null pointer since some client code might depend on this. + oRetList.Assign( (char**) CPLCalloc(sizeof(char*),1) ); + } + + return oRetList.StealList(); } /********************************************************************** @@ -983,7 +1008,11 @@ const char *CPLSPrintf(const char *fmt, ...) va_start(args, fmt); #if defined(HAVE_VSNPRINTF) - vsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE-1, fmt, args); + int ret = vsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE-1, fmt, args); + if( ret < 0 || ret >= CPLSPrintf_BUF_SIZE-1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "CPLSPrintf() called with too big string. Output will be truncated !"); + } #else vsprintf(pachBuffer, fmt, args); #endif @@ -1027,7 +1056,7 @@ int CPLVASPrintf( char **buf, const char *fmt, va_list ap ) osWork.vPrintf( fmt, ap ); if( buf ) - *buf = strdup(osWork.c_str()); + *buf = CPLStrdup(osWork.c_str()); return strlen(osWork); } @@ -1458,8 +1487,8 @@ void CSLSetNameValueSeparator( char ** papszList, const char *pszSeparator ) * The backslash, quote, '\\0' and newline characters are all escaped in * the usual C style. * - * CPLES_XML(1): This scheme converts the '<', '<' and '&' characters into - * their XML/HTML equivelent (>, < and &) making a string safe + * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into + * their XML/HTML equivelent (<, >, " and &) making a string safe * to embed as CDATA within an XML element. The '\\0' is not escaped and * should not be included in the input. * @@ -1539,19 +1568,19 @@ char *CPLEscapeString( const char *pszInput, int nLength, if( (pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') || (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') || (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') - || pszInput[iIn] == '_' ) + || pszInput[iIn] == '_' || pszInput[iIn] == '.' ) { pszOutput[iOut++] = pszInput[iIn]; } else { - sprintf( pszOutput+iOut, "%%%02X", pszInput[iIn] ); + sprintf( pszOutput+iOut, "%%%02X", ((unsigned char*)pszInput)[iIn] ); iOut += 3; } } pszOutput[iOut] = '\0'; } - else if( nScheme == CPLES_XML ) + else if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES ) { int iOut = 0, iIn; @@ -1579,7 +1608,7 @@ char *CPLEscapeString( const char *pszInput, int nLength, pszOutput[iOut++] = 'p'; pszOutput[iOut++] = ';'; } - else if( pszInput[iIn] == '"' ) + else if( pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES ) { pszOutput[iOut++] = '&'; pszOutput[iOut++] = 'q'; @@ -1588,6 +1617,14 @@ char *CPLEscapeString( const char *pszInput, int nLength, pszOutput[iOut++] = 't'; pszOutput[iOut++] = ';'; } + else if( ((GByte*)pszInput)[iIn] < 0x20 + && pszInput[iIn] != 0x9 + && pszInput[iIn] != 0xA + && pszInput[iIn] != 0xD ) + { + // These control characters are unrepresentable in XML format, + // so we just drop them. #4117 + } else pszOutput[iOut++] = pszInput[iIn]; } @@ -1633,8 +1670,6 @@ char *CPLEscapeString( const char *pszInput, int nLength, pszOutput[iOut++] = '\"'; pszOutput[iOut++] = '\"'; } - else if( pszInput[iIn] == 13 ) - /* drop DOS LF's in strings. */; else pszOutput[iOut++] = pszInput[iIn]; } @@ -1683,14 +1718,19 @@ char *CPLUnescapeString( const char *pszInput, int *pnLength, int nScheme ) char *pszOutput; int iOut=0, iIn; - pszOutput = (char *) CPLMalloc(strlen(pszInput)+1); + pszOutput = (char *) CPLMalloc(4 * strlen(pszInput)+1); pszOutput[0] = '\0'; - if( nScheme == CPLES_XML ) + if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES ) { - for( iIn = 0; pszInput[iIn] != '\0'; iIn++ ) + char ch; + for( iIn = 0; (ch = pszInput[iIn]) != '\0'; iIn++ ) { - if( EQUALN(pszInput+iIn,"<",4) ) + if( ch != '&' ) + { + pszOutput[iOut++] = ch; + } + else if( EQUALN(pszInput+iIn,"<",4) ) { pszOutput[iOut++] = '<'; iIn += 3; @@ -1705,14 +1745,73 @@ char *CPLUnescapeString( const char *pszInput, int *pnLength, int nScheme ) pszOutput[iOut++] = '&'; iIn += 4; } + else if( EQUALN(pszInput+iIn,"'",6) ) + { + pszOutput[iOut++] = '\''; + iIn += 5; + } else if( EQUALN(pszInput+iIn,""",6) ) { pszOutput[iOut++] = '"'; iIn += 5; } + else if( EQUALN(pszInput+iIn,"&#x",3) ) + { + wchar_t anVal[2] = {0 , 0}; + iIn += 3; + + while(TRUE) + { + ch = pszInput[iIn ++]; + if (ch >= 'a' && ch <= 'f') + anVal[0] = anVal[0] * 16 + ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'A') + anVal[0] = anVal[0] * 16 + ch - 'A' + 10; + else if (ch >= '0' && ch <= '9') + anVal[0] = anVal[0] * 16 + ch - '0'; + else + break; + } + if (ch != ';') + break; + iIn --; + + char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8); + int nLen = strlen(pszUTF8); + memcpy(pszOutput + iOut, pszUTF8, nLen); + CPLFree(pszUTF8); + iOut += nLen; + } + else if( EQUALN(pszInput+iIn,"&#",2) ) + { + char ch; + wchar_t anVal[2] = {0 , 0}; + iIn += 2; + + while(TRUE) + { + ch = pszInput[iIn ++]; + if (ch >= '0' && ch <= '9') + anVal[0] = anVal[0] * 10 + ch - '0'; + else + break; + } + if (ch != ';') + break; + iIn --; + + char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8); + int nLen = strlen(pszUTF8); + memcpy(pszOutput + iOut, pszUTF8, nLen); + CPLFree(pszUTF8); + iOut += nLen; + } else { - pszOutput[iOut++] = pszInput[iIn]; + /* illegal escape sequence */ + CPLDebug( "CPL", + "Error unescaping CPLES_XML text, '&' character followed by unhandled escape sequence." ); + break; } } } @@ -1908,6 +2007,10 @@ GByte *CPLHexToBinary( const char *pszHex, int *pnBytes ) * a real, an integer or a string * Leading and trailing spaces are skipped in the analysis. * + * Note: in the context of this function, integer must be understood in a + * broad sense. It does not mean that the value can fit into a 32 bit integer + * for example. It might be larger. + * * @param pszValue the string to analyze * * @return returns the type of the value contained in the string. @@ -1917,7 +2020,7 @@ CPLValueType CPLGetValueType(const char* pszValue) { /* doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 " - not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3" + not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d" */ int bFoundDot = FALSE; @@ -1928,14 +2031,17 @@ CPLValueType CPLGetValueType(const char* pszValue) if (pszValue == NULL) return CPL_VALUE_STRING; - /* Skip leading + or - */ - if (*pszValue == '+' || *pszValue == '-') - pszValue ++; - /* Skip leading spaces */ while( isspace( (unsigned char)*pszValue ) ) pszValue ++; + if (*pszValue == '\0') + return CPL_VALUE_STRING; + + /* Skip leading + or - */ + if (*pszValue == '+' || *pszValue == '-') + pszValue ++; + for(; *pszValue != '\0'; pszValue++ ) { if( isdigit( *pszValue)) @@ -1975,6 +2081,10 @@ CPLValueType CPLGetValueType(const char* pszValue) else if (*pszValue == 'D' || *pszValue == 'd' || *pszValue == 'E' || *pszValue == 'e' ) { + if (!(pszValue[1] == '+' || pszValue[1] == '-' || + isdigit(pszValue[1]))) + return CPL_VALUE_STRING; + bIsReal = TRUE; if (!bFoundExponent) bFoundExponent = TRUE; diff --git a/cpl/cpl_string.h b/cpl/cpl_string.h index 33b3300..16a12ad 100644 --- a/cpl/cpl_string.h +++ b/cpl/cpl_string.h @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_string.h 18103 2009-11-25 21:03:23Z rouault $ + * $Id: cpl_string.h 27384 2014-05-24 12:28:12Z rouault $ * * Name: cpl_string.h * Project: CPL - Common Portability Library @@ -8,6 +8,7 @@ * ********************************************************************** * Copyright (c) 1998, Daniel Morissette + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -49,26 +50,30 @@ * name/value pairs. The actual data is formatted with each string having * the format ":" (though "=" is also an acceptable separator). * A number of the functions in the file operate on name/value style - * string lists (such as CSLSetNameValue(), and CSLFetchNameValue()). + * string lists (such as CSLSetNameValue(), and CSLFetchNameValue()). + * + * To some extent the CPLStringList C++ class can be used to abstract + * managing string lists a bit but still be able to return them from C + * functions. * */ CPL_C_START -char CPL_DLL **CSLAddString(char **papszStrList, const char *pszNewString); +char CPL_DLL **CSLAddString(char **papszStrList, const char *pszNewString) CPL_WARN_UNUSED_RESULT; int CPL_DLL CSLCount(char **papszStrList); const char CPL_DLL *CSLGetField( char **, int ); void CPL_DLL CPL_STDCALL CSLDestroy(char **papszStrList); -char CPL_DLL **CSLDuplicate(char **papszStrList); -char CPL_DLL **CSLMerge( char **papszOrig, char **papszOverride ); +char CPL_DLL **CSLDuplicate(char **papszStrList) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLMerge( char **papszOrig, char **papszOverride ) CPL_WARN_UNUSED_RESULT; -char CPL_DLL **CSLTokenizeString(const char *pszString ); +char CPL_DLL **CSLTokenizeString(const char *pszString ) CPL_WARN_UNUSED_RESULT; char CPL_DLL **CSLTokenizeStringComplex(const char *pszString, const char *pszDelimiter, - int bHonourStrings, int bAllowEmptyTokens ); + int bHonourStrings, int bAllowEmptyTokens ) CPL_WARN_UNUSED_RESULT; char CPL_DLL **CSLTokenizeString2( const char *pszString, const char *pszDelimeter, - int nCSLTFlags ); + int nCSLTFlags ) CPL_WARN_UNUSED_RESULT; #define CSLT_HONOURSTRINGS 0x0001 #define CSLT_ALLOWEMPTYTOKENS 0x0002 @@ -78,17 +83,18 @@ char CPL_DLL **CSLTokenizeString2( const char *pszString, #define CSLT_STRIPENDSPACES 0x0020 int CPL_DLL CSLPrint(char **papszStrList, FILE *fpOut); -char CPL_DLL **CSLLoad(const char *pszFname); -char CPL_DLL **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions); +char CPL_DLL **CSLLoad(const char *pszFname) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions) CPL_WARN_UNUSED_RESULT; int CPL_DLL CSLSave(char **papszStrList, const char *pszFname); char CPL_DLL **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo, - char **papszNewLines); + char **papszNewLines) CPL_WARN_UNUSED_RESULT; char CPL_DLL **CSLInsertString(char **papszStrList, int nInsertAtLineNo, - const char *pszNewLine); + const char *pszNewLine) CPL_WARN_UNUSED_RESULT; char CPL_DLL **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete, - int nNumToRemove, char ***ppapszRetStrings); + int nNumToRemove, char ***ppapszRetStrings) CPL_WARN_UNUSED_RESULT; int CPL_DLL CSLFindString( char **, const char * ); +int CPL_DLL CSLFindStringCaseSensitive( char **, const char * ); int CPL_DLL CSLPartialFindString( char **papszHaystack, const char * pszNeedle ); int CPL_DLL CSLFindName(char **papszStrList, const char *pszName); @@ -97,7 +103,7 @@ int CPL_DLL CSLFetchBoolean( char **papszStrList, const char *pszKey, int bDefault ); const char CPL_DLL *CPLSPrintf(const char *fmt, ...) CPL_PRINT_FUNC_FORMAT(1, 2); -char CPL_DLL **CSLAppendPrintf(char **papszStrList, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT(2, 3); +char CPL_DLL **CSLAppendPrintf(char **papszStrList, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT(2, 3) CPL_WARN_UNUSED_RESULT; int CPL_DLL CPLVASPrintf(char **buf, const char *fmt, va_list args ); const char CPL_DLL * @@ -111,26 +117,30 @@ char CPL_DLL ** CSLFetchNameValueMultiple(char **papszStrList, const char *pszName); char CPL_DLL ** CSLAddNameValue(char **papszStrList, - const char *pszName, const char *pszValue); + const char *pszName, const char *pszValue) CPL_WARN_UNUSED_RESULT; char CPL_DLL ** CSLSetNameValue(char **papszStrList, - const char *pszName, const char *pszValue); + const char *pszName, const char *pszValue) CPL_WARN_UNUSED_RESULT; void CPL_DLL CSLSetNameValueSeparator( char ** papszStrList, const char *pszSeparator ); #define CPLES_BackslashQuotable 0 #define CPLES_XML 1 -#define CPLES_URL 2 /* unescape only for now */ +#define CPLES_URL 2 #define CPLES_SQL 3 #define CPLES_CSV 4 +#define CPLES_XML_BUT_QUOTES 5 char CPL_DLL *CPLEscapeString( const char *pszString, int nLength, - int nScheme ); + int nScheme ) CPL_WARN_UNUSED_RESULT; char CPL_DLL *CPLUnescapeString( const char *pszString, int *pnLength, - int nScheme ); + int nScheme ) CPL_WARN_UNUSED_RESULT; + +char CPL_DLL *CPLBinaryToHex( int nBytes, const GByte *pabyData ) CPL_WARN_UNUSED_RESULT; +GByte CPL_DLL *CPLHexToBinary( const char *pszHex, int *pnBytes ) CPL_WARN_UNUSED_RESULT; -char CPL_DLL *CPLBinaryToHex( int nBytes, const GByte *pabyData ); -GByte CPL_DLL *CPLHexToBinary( const char *pszHex, int *pnBytes ); +char CPL_DLL *CPLBase64Encode( int nBytes, const GByte *pabyData ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPLBase64DecodeInPlace(GByte* pszBase64); typedef enum { @@ -156,17 +166,20 @@ size_t CPL_DLL CPLStrnlen (const char *pszStr, size_t nMaxLen); #define CPL_ENC_ASCII "ASCII" #define CPL_ENC_ISO8859_1 "ISO-8859-1" +int CPL_DLL CPLEncodingCharSize( const char *pszEncoding ); +void CPL_DLL CPLClearRecodeWarningFlags( void ); char CPL_DLL *CPLRecode( const char *pszSource, const char *pszSrcEncoding, - const char *pszDstEncoding ); + const char *pszDstEncoding ) CPL_WARN_UNUSED_RESULT; char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, const char *pszSrcEncoding, - const char *pszDstEncoding ); + const char *pszDstEncoding ) CPL_WARN_UNUSED_RESULT; wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, const char *pszSrcEncoding, - const char *pszDstEncoding ); + const char *pszDstEncoding ) CPL_WARN_UNUSED_RESULT; int CPL_DLL CPLIsUTF8(const char* pabyData, int nLen); -char CPL_DLL *CPLForceToASCII(const char* pabyData, int nLen, char chReplacementChar); +char CPL_DLL *CPLForceToASCII(const char* pabyData, int nLen, char chReplacementChar) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPLStrlenUTF8(const char *pszUTF8Str); CPL_C_END @@ -200,9 +213,9 @@ CPL_C_END /* Avoid C2614 errors */ #ifdef MSVC_OLD_STUPID_BEHAVIOUR using std::string; -# define std_string string +# define gdal_std_string string #else -# define std_string std::string +# define gdal_std_string std::string #endif /* Remove annoying warnings in Microsoft eVC++ and Microsoft Visual C++ */ @@ -210,47 +223,143 @@ CPL_C_END # pragma warning(disable:4251 4275 4786) #endif - - - -class CPL_DLL CPLString : public std_string +//! Convenient string class based on std::string. +class CPL_DLL CPLString : public gdal_std_string { public: CPLString(void) {} - CPLString( const std::string &oStr ) : std_string( oStr ) {} - CPLString( const char *pszStr ) : std_string( pszStr ) {} + CPLString( const std::string &oStr ) : gdal_std_string( oStr ) {} + CPLString( const char *pszStr ) : gdal_std_string( pszStr ) {} operator const char* (void) const { return c_str(); } char& operator[](std::string::size_type i) { - return std_string::operator[](i); + return gdal_std_string::operator[](i); } const char& operator[](std::string::size_type i) const { - return std_string::operator[](i); + return gdal_std_string::operator[](i); } char& operator[](int i) { - return std_string::operator[](static_cast(i)); + return gdal_std_string::operator[](static_cast(i)); } const char& operator[](int i) const { - return std_string::operator[](static_cast(i)); + return gdal_std_string::operator[](static_cast(i)); } void Clear() { resize(0); } + // NULL safe assign and free. + void Seize(char *pszValue) + { + if (pszValue == NULL ) + Clear(); + else + { + *this = pszValue; + CPLFree(pszValue); + } + } + /* There seems to be a bug in the way the compiler count indices... Should be CPL_PRINT_FUNC_FORMAT (1, 2) */ CPLString &Printf( const char *pszFormat, ... ) CPL_PRINT_FUNC_FORMAT (2, 3); CPLString &vPrintf( const char *pszFormat, va_list args ); CPLString &FormatC( double dfValue, const char *pszFormat = NULL ); CPLString &Trim(); + CPLString &Recode( const char *pszSrcEncoding, const char *pszDstEncoding ); + + /* case insensitive find alternates */ + size_t ifind( const std::string & str, size_t pos = 0 ) const; + size_t ifind( const char * s, size_t pos = 0 ) const; + CPLString &toupper( void ); + CPLString &tolower( void ); +}; + +CPLString CPLOPrintf(const char *pszFormat, ... ) CPL_PRINT_FUNC_FORMAT (1, 2); +CPLString CPLOvPrintf(const char *pszFormat, va_list args); + +/* -------------------------------------------------------------------- */ +/* URL processing functions, here since they depend on CPLString. */ +/* -------------------------------------------------------------------- */ +CPLString CPL_DLL CPLURLGetValue(const char* pszURL, const char* pszKey); +CPLString CPL_DLL CPLURLAddKVP(const char* pszURL, const char* pszKey, + const char* pszValue); + +/************************************************************************/ +/* CPLStringList */ +/************************************************************************/ + +//! String list class designed around our use of C "char**" string lists. +class CPL_DLL CPLStringList +{ + char **papszList; + mutable int nCount; + mutable int nAllocation; + int bOwnList; + int bIsSorted; + + void Initialize(); + void MakeOurOwnCopy(); + void EnsureAllocation( int nMaxLength ); + int FindSortedInsertionPoint( const char *pszLine ); + + public: + CPLStringList(); + CPLStringList( char **papszList, int bTakeOwnership=TRUE ); + CPLStringList( const CPLStringList& oOther ); + ~CPLStringList(); + + CPLStringList &Clear(); + + int size() const { return Count(); } + int Count() const; + + CPLStringList &AddString( const char *pszNewString ); + CPLStringList &AddStringDirectly( char *pszNewString ); + + CPLStringList &InsertString( int nInsertAtLineNo, const char *pszNewLine ) + { return InsertStringDirectly( nInsertAtLineNo, CPLStrdup(pszNewLine) ); } + CPLStringList &InsertStringDirectly( int nInsertAtLineNo, char *pszNewLine); + +// CPLStringList &InsertStrings( int nInsertAtLineNo, char **papszNewLines ); +// CPLStringList &RemoveStrings( int nFirstLineToDelete, int nNumToRemove=1 ); + + int FindString( const char *pszTarget ) const + { return CSLFindString( papszList, pszTarget ); } + int PartialFindString( const char *pszNeedle ) const + { return CSLPartialFindString( papszList, pszNeedle ); } + + int FindName( const char *pszName ) const; + int FetchBoolean( const char *pszKey, int bDefault ) const; + const char *FetchNameValue( const char *pszKey ) const; + const char *FetchNameValueDef( const char *pszKey, const char *pszDefault ) const; + CPLStringList &AddNameValue( const char *pszKey, const char *pszValue ); + CPLStringList &SetNameValue( const char *pszKey, const char *pszValue ); + + CPLStringList &Assign( char **papszList, int bTakeOwnership=TRUE ); + CPLStringList &operator=(char **papszListIn) { return Assign( papszListIn, TRUE ); } + CPLStringList &operator=(const CPLStringList& oOther); + + char * operator[](int i); + char * operator[](size_t i) { return (*this)[(int)i]; } + const char * operator[](int i) const; + const char * operator[](size_t i) const { return (*this)[(int)i]; } + + char **List() { return papszList; } + char **StealList(); + + CPLStringList &Sort(); + int IsSorted() const { return bIsSorted; } + + operator char**(void) { return List(); } }; #endif /* def __cplusplus && !CPL_SUPRESS_CPLUSPLUS */ diff --git a/cpl/cpl_strtod.cpp b/cpl/cpl_strtod.cpp index 9363bb5..c1d296d 100644 --- a/cpl/cpl_strtod.cpp +++ b/cpl/cpl_strtod.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_strtod.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $ + * $Id: cpl_strtod.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Functions to convert ASCII string to floating point number. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2006, Andrey Kiselev + * Copyright (c) 2008-2012, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,7 +34,7 @@ #include "cpl_conv.h" -CPL_CVSID("$Id: cpl_strtod.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $"); +CPL_CVSID("$Id: cpl_strtod.cpp 27044 2014-03-16 23:41:27Z rouault $"); // XXX: with GCC 2.95 strtof() function is only available when in c99 mode. // Fix it here not touching the compiler options. @@ -43,6 +44,32 @@ extern float strtof(const char *nptr, char **endptr); } #endif +#ifndef NAN +# ifdef HUGE_VAL +# define NAN (HUGE_VAL * 0.0) +# else + +static float CPLNaN(void) +{ + float fNan; + int nNan = 0x7FC00000; + memcpy(&fNan, &nNan, 4); + return fNan; +} + +# define NAN CPLNan() +# endif +#endif + +#ifndef INFINITY + static CPL_INLINE double CPLInfinity(void) + { + static double ZERO = 0; + return 1.0 / ZERO; /* MSVC doesn't like 1.0 / 0.0 */ + } + #define INFINITY CPLInfinity() +#endif + /************************************************************************/ /* CPLAtofDelim() */ /************************************************************************/ @@ -150,12 +177,12 @@ double CPLAtofM( const char *nptr ) } /************************************************************************/ -/* CPLStrtodDelim() */ +/* CPLReplacePointByLocalePoint() */ /************************************************************************/ -static void CPLReplacePointByLocalePoint(char* pszNumber, char point) +static char* CPLReplacePointByLocalePoint(const char* pszNumber, char point) { -#if defined(WIN32CE) +#if defined(WIN32CE) || defined(__ANDROID__) static char byPoint = 0; if (byPoint == 0) { @@ -165,16 +192,12 @@ static void CPLReplacePointByLocalePoint(char* pszNumber, char point) } if (point != byPoint) { - int i = 0; - - while ( pszNumber[i] ) + const char* pszPoint = strchr(pszNumber, point); + if (pszPoint) { - if ( pszNumber[i] == point ) - { - pszNumber[i] = byPoint; - break; - } - i++; + char* pszNew = CPLStrdup(pszNumber); + pszNew[pszPoint - pszNumber] = byPoint; + return pszNew; } } #else @@ -183,25 +206,26 @@ static void CPLReplacePointByLocalePoint(char* pszNumber, char point) && poLconv->decimal_point && strlen(poLconv->decimal_point) > 0 ) { - int i = 0; char byPoint = poLconv->decimal_point[0]; if (point != byPoint) { - while ( pszNumber[i] ) + const char* pszPoint = strchr(pszNumber, point); + if (pszPoint) { - if ( pszNumber[i] == point ) - { - pszNumber[i] = byPoint; - break; - } - i++; + char* pszNew = CPLStrdup(pszNumber); + pszNew[pszPoint - pszNumber] = byPoint; + return pszNew; } } } #endif + return (char*) pszNumber; } +/************************************************************************/ +/* CPLStrtodDelim() */ +/************************************************************************/ /** * Converts ASCII string to floating point number using specified delimiter. @@ -222,17 +246,41 @@ static void CPLReplacePointByLocalePoint(char* pszNumber, char point) */ double CPLStrtodDelim(const char *nptr, char **endptr, char point) { + while( *nptr == ' ' ) + nptr ++; + + if (nptr[0] == '-') + { + if (strcmp(nptr, "-1.#QNAN") == 0 || + strcmp(nptr, "-1.#IND") == 0) + return NAN; + + if (strcmp(nptr,"-inf") == 0 || + strcmp(nptr,"-1.#INF") == 0) + return -INFINITY; + } + else if (nptr[0] == '1') + { + if (strcmp(nptr, "1.#QNAN") == 0) + return NAN; + if (strcmp (nptr,"1.#INF") == 0) + return INFINITY; + } + else if (nptr[0] == 'i' && strcmp(nptr,"inf") == 0) + return INFINITY; + else if (nptr[0] == 'n' && strcmp(nptr,"nan") == 0) + return NAN; + /* -------------------------------------------------------------------- */ /* We are implementing a simple method here: copy the input string */ /* into the temporary buffer, replace the specified decimal delimiter */ /* with the one, taken from locale settings and use standard strtod() */ /* on that buffer. */ /* -------------------------------------------------------------------- */ - char *pszNumber = CPLStrdup( nptr ); double dfValue; int nError; - CPLReplacePointByLocalePoint(pszNumber, point); + char* pszNumber = CPLReplacePointByLocalePoint(nptr, point); dfValue = strtod( pszNumber, endptr ); nError = errno; @@ -240,7 +288,8 @@ double CPLStrtodDelim(const char *nptr, char **endptr, char point) if ( endptr ) *endptr = (char *)nptr + (*endptr - pszNumber); - CPLFree( pszNumber ); + if (pszNumber != (char*) nptr) + CPLFree( pszNumber ); errno = nError; return dfValue; @@ -303,11 +352,10 @@ float CPLStrtofDelim(const char *nptr, char **endptr, char point) /* on that buffer. */ /* -------------------------------------------------------------------- */ - char *pszNumber = CPLStrdup( nptr ); double dfValue; int nError; - CPLReplacePointByLocalePoint(pszNumber, point); + char* pszNumber = CPLReplacePointByLocalePoint(nptr, point); dfValue = strtof( pszNumber, endptr ); nError = errno; @@ -315,7 +363,8 @@ float CPLStrtofDelim(const char *nptr, char **endptr, char point) if ( endptr ) *endptr = (char *)nptr + (*endptr - pszNumber); - CPLFree( pszNumber ); + if (pszNumber != (char*) nptr) + CPLFree( pszNumber ); errno = nError; return dfValue; @@ -352,4 +401,5 @@ float CPLStrtof(const char *nptr, char **endptr) { return CPLStrtofDelim(nptr, endptr, '.'); } + /* END OF FILE */ diff --git a/cpl/cpl_time.cpp b/cpl/cpl_time.cpp new file mode 100644 index 0000000..713cff9 --- /dev/null +++ b/cpl/cpl_time.cpp @@ -0,0 +1,153 @@ +/********************************************************************** + * $Id: cpl_time.cpp 18063 2009-11-21 21:11:49Z warmerdam $ + * + * Name: cpl_time.cpp + * Project: CPL - Common Portability Library + * Purpose: Time functions. + * Author: Even Rouault, + * + ********************************************************************** + * + * CPLUnixTimeToYMDHMS() is derived from timesub() in localtime.c from openbsd/freebsd/netbsd. + * CPLYMDHMSToUnixTime() has been implemented by Even Rouault and is in the public domain + * + * Cf http://svn.freebsd.org/viewvc/base/stable/7/lib/libc/stdtime/localtime.c?revision=178142&view=markup + * localtime.c comes with the following header : + * + * This file is in the public domain, so clarified as of + * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). + */ + +#include "cpl_time.h" + +#define SECSPERMIN 60L +#define MINSPERHOUR 60L +#define HOURSPERDAY 24L +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) +#define DAYSPERWEEK 7 +#define MONSPERYEAR 12 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY 4 +#define TM_YEAR_BASE 1900 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + +static const int mon_lengths[2][MONSPERYEAR] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +} ; + + +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +/************************************************************************/ +/* CPLUnixTimeToYMDHMS() */ +/************************************************************************/ + +/** Converts a time value since the Epoch (aka "unix" time) to a broken-down UTC time. + * + * This function is similar to gmtime_r(). + * This function will always set tm_isdst to 0. + * + * @param unixTime number of seconds since the Epoch. + * @param pRet address of the return structure. + * + * @return the structure pointed by pRet filled with a broken-down UTC time. + */ + +struct tm * CPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet) +{ + GIntBig days = unixTime / SECSPERDAY; + GIntBig rem = unixTime % SECSPERDAY; + + while (rem < 0) { + rem += SECSPERDAY; + --days; + } + + pRet->tm_hour = (int) (rem / SECSPERHOUR); + rem = rem % SECSPERHOUR; + pRet->tm_min = (int) (rem / SECSPERMIN); + /* + ** A positive leap second requires a special + ** representation. This uses "... ??:59:60" et seq. + */ + pRet->tm_sec = (int) (rem % SECSPERMIN); + pRet->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); + if (pRet->tm_wday < 0) + pRet->tm_wday += DAYSPERWEEK; + GIntBig y = EPOCH_YEAR; + int yleap; + while (days < 0 || days >= (GIntBig) year_lengths[yleap = isleap(y)]) + { + GIntBig newy; + + newy = y + days / DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(newy - 1) - + LEAPS_THRU_END_OF(y - 1); + y = newy; + } + pRet->tm_year = (int) (y - TM_YEAR_BASE); + pRet->tm_yday = (int) days; + const int* ip = mon_lengths[yleap]; + for (pRet->tm_mon = 0; days >= (GIntBig) ip[pRet->tm_mon]; ++(pRet->tm_mon)) + days = days - (GIntBig) ip[pRet->tm_mon]; + pRet->tm_mday = (int) (days + 1); + pRet->tm_isdst = 0; + + return pRet; +} + +/************************************************************************/ +/* CPLYMDHMSToUnixTime() */ +/************************************************************************/ + +/** Converts a broken-down UTC time into time since the Epoch (aka "unix" time). + * + * This function is similar to mktime(), but the passed structure is not modified. + * This function ignores the tm_wday, tm_yday and tm_isdst fields of the passed value. + * No timezone shift will be applied. This function returns 0 for the 1/1/1970 00:00:00 + * + * @param brokendowntime broken-downtime UTC time. + * + * @return a number of seconds since the Epoch encoded as a value of type GIntBig, + * or -1 if the time cannot be represented. + */ + +GIntBig CPLYMDHMSToUnixTime(const struct tm *brokendowntime) +{ + GIntBig days; + int mon; + + if (brokendowntime->tm_mon < 0 || brokendowntime->tm_mon >= 12) + return -1; + + /* Number of days of the current month */ + days = brokendowntime->tm_mday - 1; + + /* Add the number of days of the current year */ + const int* ip = mon_lengths[isleap(TM_YEAR_BASE + brokendowntime->tm_year)]; + for(mon=0;montm_mon;mon++) + days += ip [mon]; + + /* Add the number of days of the other years */ + days += (TM_YEAR_BASE + (GIntBig)brokendowntime->tm_year - EPOCH_YEAR) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(TM_YEAR_BASE + (GIntBig)brokendowntime->tm_year - 1) - + LEAPS_THRU_END_OF(EPOCH_YEAR - 1); + + /* Now add the secondes, minutes and hours to the number of days since EPOCH */ + return brokendowntime->tm_sec + + brokendowntime->tm_min * SECSPERMIN + + brokendowntime->tm_hour * SECSPERHOUR + + days * SECSPERDAY; +} diff --git a/cpl/cpl_time.h b/cpl/cpl_time.h new file mode 100644 index 0000000..b131b5c --- /dev/null +++ b/cpl/cpl_time.h @@ -0,0 +1,41 @@ +/********************************************************************** + * $Id: cpl_time.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_time.h + * Project: CPL - Common Portability Library + * Purpose: Time functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_TIME_H_INCLUDED +#define _CPL_TIME_H_INCLUDED + +#include + +#include "cpl_port.h" + +struct tm CPL_DLL * CPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet); +GIntBig CPL_DLL CPLYMDHMSToUnixTime(const struct tm *brokendowntime); + +#endif // _CPL_TIME_H_INCLUDED diff --git a/cpl/cpl_virtualmem.cpp b/cpl/cpl_virtualmem.cpp new file mode 100644 index 0000000..b76df79 --- /dev/null +++ b/cpl/cpl_virtualmem.cpp @@ -0,0 +1,2057 @@ +/********************************************************************** + * $Id: cpl_virtualmem.cpp 27315 2014-05-11 18:16:54Z rouault $ + * + * Name: cpl_virtualmem.cpp + * Project: CPL - Common Portability Library + * Purpose: Virtual memory + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "cpl_virtualmem.h" +#include "cpl_error.h" +#include "cpl_multiproc.h" +#include "cpl_atomic_ops.h" +#include "cpl_conv.h" + +#if defined(__linux) && defined(CPL_MULTIPROC_PTHREAD) + +#include /* mmap, munmap, mremap */ +#include /* select */ +#include /* open() */ +#include /* open() */ +#include +#include +#include /* open() */ +#include /* sigaction */ +#include +#include +#include +#include /* read, write, close, pipe */ +#include + +#ifndef HAVE_5ARGS_MREMAP +#include "cpl_atomic_ops.h" +#endif + +/* Linux specific (i.e. non POSIX compliant) features used : + - returning from a SIGSEGV handler is clearly a POSIX violation, but in + practice most POSIX systems should be happy. + - mremap() with 5 args is Linux specific. It is used when the user callback is invited + to fill a page, we currently mmap() a writable page, let it filled it, + and afterwards mremap() that temporary page onto the location where the + fault occured. + If we have no mremap(), the workaround is to pause other threads that + consume the current view while we are updating the faulted page, otherwise + a non-paused thread could access a page that is in the middle of being + filled... The way we pause those threads is quite original : we send them + a SIGUSR1 and wait that they are stuck in the temporary SIGUSR1 handler... + - MAP_ANONYMOUS isn't documented in Posix, but very commonly found (sometimes called MAP_ANON) + - dealing with the limitation of number of memory mapping regions, and the 65536 limit. + - other things I've not identified +*/ + + +#define ALIGN_DOWN(p,pagesize) (void*)(((size_t)(p)) / (pagesize) * (pagesize)) +#define ALIGN_UP(p,pagesize) (void*)(((size_t)(p) + (pagesize) - 1) / (pagesize) * (pagesize)) + +#define DEFAULT_PAGE_SIZE (256*256) +#define MAXIMUM_PAGE_SIZE (32*1024*1024) + +/* Linux Kernel limit */ +#define MAXIMUM_COUNT_OF_MAPPINGS 65536 + +#define BYEBYE_ADDR ((void*)(~(size_t)0)) + +#define MAPPING_FOUND "yeah" +#define MAPPING_NOT_FOUND "doh!" + +#define SET_BIT(ar,bitnumber) ar[(bitnumber)/8] |= 1 << ((bitnumber) % 8) +#define UNSET_BIT(ar,bitnumber) ar[(bitnumber)/8] &= ~(1 << ((bitnumber) % 8)) +#define TEST_BIT(ar,bitnumber) (ar[(bitnumber)/8] & (1 << ((bitnumber) % 8))) + +typedef enum +{ + OP_LOAD, + OP_STORE, + OP_MOVS_RSI_RDI, + OP_UNKNOWN +} OpType; + +struct CPLVirtualMem +{ + struct CPLVirtualMem *pVMemBase; + int nRefCount; + + int bFileMemoryMapped; /* if TRUE, only eAccessMode, pData, pDataToFree, nSize and nPageSize are valid */ + CPLVirtualMemAccessMode eAccessMode; + + size_t nPageSize; + void *pData; /* aligned on nPageSize */ + void *pDataToFree; /* returned by mmap(), potentially lower than pData */ + size_t nSize; /* requested size (unrounded) */ + + GByte *pabitMappedPages; + GByte *pabitRWMappedPages; + + int nCacheMaxSizeInPages; /* maximum size of page array */ + int *panLRUPageIndices; /* array with indices of cached pages */ + int iLRUStart; /* index in array where to write next page index */ + int nLRUSize; /* current isze of the array */ + + int iLastPage; /* last page accessed */ + int nRetry; /* number of consecutive retries to that last page */ + + int bSingleThreadUsage; + + CPLVirtualMemCachePageCbk pfnCachePage; /* called when a page is mapped */ + CPLVirtualMemUnCachePageCbk pfnUnCachePage; /* called when a (writable) page is unmapped */ + void *pCbkUserData; + CPLVirtualMemFreeUserData pfnFreeUserData; +#ifndef HAVE_5ARGS_MREMAP + void *hMutexThreadArray; + int nThreads; + pthread_t *pahThreads; +#endif +}; + +typedef struct +{ + /* hVirtualMemManagerMutex protects the 2 following variables */ + CPLVirtualMem **pasVirtualMem; + int nVirtualMemCount; + + int pipefd_to_thread[2]; + int pipefd_from_thread[2]; + int pipefd_wait_thread[2]; + void *hHelperThread; + + struct sigaction oldact; +} CPLVirtualMemManager; + +typedef struct +{ + void *pFaultAddr; + OpType opType; + pthread_t hRequesterThread; +} CPLVirtualMemMsgToWorkerThread; + +static CPLVirtualMemManager* pVirtualMemManager = NULL; +static void* hVirtualMemManagerMutex = NULL; + +static void CPLVirtualMemManagerInit(); + +#ifdef DEBUG_VIRTUALMEM + +/************************************************************************/ +/* fprintfstderr() */ +/************************************************************************/ + +static void fprintfstderr(const char* fmt, ...) +{ + char buffer[80]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + int offset = 0; + while(TRUE) + { + int ret = write(2, buffer + offset, strlen(buffer + offset)); + if( ret < 0 && errno == EINTR ) + ; + else + { + if( ret == (int)strlen(buffer + offset) ) + break; + offset += ret; + } + } +} + +#endif + +/************************************************************************/ +/* CPLGetPageSize() */ +/************************************************************************/ + +size_t CPLGetPageSize(void) +{ + return (size_t) sysconf(_SC_PAGESIZE); +} + +/************************************************************************/ +/* CPLVirtualMemManagerRegisterVirtualMem() */ +/************************************************************************/ + +static void CPLVirtualMemManagerRegisterVirtualMem(CPLVirtualMem* ctxt) +{ + CPLVirtualMemManagerInit(); + + assert(ctxt); + CPLAcquireMutex(hVirtualMemManagerMutex, 1000.0); + pVirtualMemManager->pasVirtualMem = (CPLVirtualMem**) CPLRealloc( + pVirtualMemManager->pasVirtualMem, sizeof(CPLVirtualMem*) * (pVirtualMemManager->nVirtualMemCount + 1) ); + pVirtualMemManager->pasVirtualMem[pVirtualMemManager->nVirtualMemCount] = ctxt; + pVirtualMemManager->nVirtualMemCount ++; + CPLReleaseMutex(hVirtualMemManagerMutex); +} + +/************************************************************************/ +/* CPLVirtualMemManagerUnregisterVirtualMem() */ +/************************************************************************/ + +static void CPLVirtualMemManagerUnregisterVirtualMem(CPLVirtualMem* ctxt) +{ + int i; + CPLAcquireMutex(hVirtualMemManagerMutex, 1000.0); + for(i=0;inVirtualMemCount;i++) + { + if( pVirtualMemManager->pasVirtualMem[i] == ctxt ) + { + if( i < pVirtualMemManager->nVirtualMemCount - 1 ) + { + memmove( pVirtualMemManager->pasVirtualMem + i, + pVirtualMemManager->pasVirtualMem + i + 1, + sizeof(CPLVirtualMem*) * (pVirtualMemManager->nVirtualMemCount - i - 1) ); + } + pVirtualMemManager->nVirtualMemCount --; + break; + } + } + CPLReleaseMutex(hVirtualMemManagerMutex); +} + +/************************************************************************/ +/* CPLVirtualMemNew() */ +/************************************************************************/ + +CPLVirtualMem* CPLVirtualMemNew(size_t nSize, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemCachePageCbk pfnCachePage, + CPLVirtualMemUnCachePageCbk pfnUnCachePage, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + CPLVirtualMem* ctxt; + void* pData; + size_t nMinPageSize = CPLGetPageSize(); + size_t nPageSize = DEFAULT_PAGE_SIZE; + size_t nCacheMaxSizeInPages; + size_t nRoundedMappingSize; + FILE* f; + int nMappings = 0; + + assert(nSize > 0); + assert(pfnCachePage != NULL); + + if( nPageSizeHint >= nMinPageSize && nPageSizeHint <= MAXIMUM_PAGE_SIZE ) + { + if( (nPageSizeHint % nMinPageSize) == 0 ) + nPageSize = nPageSizeHint; + else + { + int nbits = 0; + nPageSize = (size_t)nPageSizeHint; + while(nPageSize > 0) + { + nPageSize >>= 1; + nbits ++; + } + nPageSize = (size_t)1 << (nbits - 1); + if( nPageSize < (size_t)nPageSizeHint ) + nPageSize <<= 1; + } + } + + if( (nPageSize % nMinPageSize) != 0 ) + nPageSize = nMinPageSize; + + if( nCacheSize > nSize ) + nCacheSize = nSize; + else if( nCacheSize == 0 ) + nCacheSize = 1; + + /* Linux specific */ + /* Count the number of existing memory mappings */ + f = fopen("/proc/self/maps", "rb"); + if( f != NULL ) + { + char buffer[80]; + while( fgets(buffer, sizeof(buffer), f) != NULL ) + nMappings ++; + fclose(f); + } + + while(TRUE) + { + /* /proc/self/maps must not have more than 65K lines */ + nCacheMaxSizeInPages = (nCacheSize + 2 * nPageSize - 1) / nPageSize; + if( nCacheMaxSizeInPages > + (size_t)((MAXIMUM_COUNT_OF_MAPPINGS * 9 / 10) - nMappings) ) + nPageSize <<= 1; + else + break; + } + nRoundedMappingSize = ((nSize + 2 * nPageSize - 1) / nPageSize) * nPageSize; + pData = mmap(NULL, nRoundedMappingSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if( pData == MAP_FAILED ) + { + perror("mmap"); + return NULL; + } + ctxt = (CPLVirtualMem* )CPLCalloc(1, sizeof(CPLVirtualMem)); + ctxt->nRefCount = 1; + ctxt->bFileMemoryMapped = FALSE; + ctxt->eAccessMode = eAccessMode; + ctxt->pDataToFree = pData; + ctxt->pData = ALIGN_UP(pData, nPageSize); + ctxt->nPageSize = nPageSize; + ctxt->pabitMappedPages = (GByte*)CPLCalloc(1, (nRoundedMappingSize / nPageSize + 7) / 8); + assert(ctxt->pabitMappedPages); + ctxt->pabitRWMappedPages = (GByte*)CPLCalloc(1, (nRoundedMappingSize / nPageSize + 7) / 8); + assert(ctxt->pabitRWMappedPages); + /* we need at least 2 pages in case for a rep movs instruction */ + /* that operate in the view */ + ctxt->nCacheMaxSizeInPages = nCacheMaxSizeInPages; + ctxt->panLRUPageIndices = (int*)CPLMalloc(ctxt->nCacheMaxSizeInPages * sizeof(int)); + assert(ctxt->panLRUPageIndices); + ctxt->nSize = nSize; + ctxt->iLRUStart = 0; + ctxt->nLRUSize = 0; + ctxt->iLastPage = -1; + ctxt->nRetry = 0; + ctxt->bSingleThreadUsage = bSingleThreadUsage; + ctxt->pfnCachePage = pfnCachePage; + ctxt->pfnUnCachePage = pfnUnCachePage; + ctxt->pfnFreeUserData = pfnFreeUserData; + ctxt->pCbkUserData = pCbkUserData; +#ifndef HAVE_5ARGS_MREMAP + if( !ctxt->bSingleThreadUsage ) + { + ctxt->hMutexThreadArray = CPLCreateMutex(); + assert(ctxt->hMutexThreadArray != NULL); + CPLReleaseMutex(ctxt->hMutexThreadArray); + ctxt->nThreads = 0; + ctxt->pahThreads = NULL; + } +#endif + + CPLVirtualMemManagerRegisterVirtualMem(ctxt); + + return ctxt; +} + +/************************************************************************/ +/* CPLIsVirtualMemFileMapAvailable() */ +/************************************************************************/ + +int CPLIsVirtualMemFileMapAvailable(void) +{ + return TRUE; +} + +/************************************************************************/ +/* CPLVirtualMemFileMapNew() */ +/************************************************************************/ + +CPLVirtualMem *CPLVirtualMemFileMapNew( VSILFILE* fp, + vsi_l_offset nOffset, + vsi_l_offset nLength, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData ) +{ +#if SIZEOF_VOIDP == 4 + if( nLength != (size_t)nLength ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "nLength = " CPL_FRMT_GUIB " incompatible with 32 bit architecture", + nLength); + return NULL; + } +#endif + + int fd = (int) (size_t) VSIFGetNativeFileDescriptorL(fp); + if( fd == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot operate on a virtual file"); + return NULL; + } + + off_t nAlignedOffset = (nOffset / CPLGetPageSize()) * CPLGetPageSize(); + size_t nAligment = nOffset - nAlignedOffset; + size_t nMappingSize = nLength + nAligment; + + /* We need to ensure that the requested extent fits into the file size */ + /* otherwise SIGBUS errors will occur when using the mapping */ + vsi_l_offset nCurPos = VSIFTellL(fp); + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nFileSize = VSIFTellL(fp); + if( nFileSize < nOffset + nLength ) + { + if( eAccessMode != VIRTUALMEM_READWRITE ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Trying to map an extent outside of the file"); + VSIFSeekL(fp, nCurPos, SEEK_SET); + return NULL; + } + else + { + char ch = 0; + if( VSIFSeekL(fp, nOffset + nLength - 1, SEEK_SET) != 0 || + VSIFWriteL(&ch, 1, 1, fp) != 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot extend file to mapping size"); + VSIFSeekL(fp, nCurPos, SEEK_SET); + return NULL; + } + } + } + VSIFSeekL(fp, nCurPos, SEEK_SET); + + void* addr = mmap(NULL, nMappingSize, + (eAccessMode == VIRTUALMEM_READWRITE) ? PROT_READ | PROT_WRITE : PROT_READ, + MAP_SHARED, fd, nAlignedOffset); + if( addr == MAP_FAILED ) + { + int myerrno = errno; + CPLError(CE_Failure, CPLE_AppDefined, + "mmap() failed : %s", strerror(myerrno)); + return NULL; + } + + CPLVirtualMem* ctxt = (CPLVirtualMem* )CPLCalloc(1, sizeof(CPLVirtualMem)); + ctxt->nRefCount = 1; + ctxt->eAccessMode = eAccessMode; + ctxt->bFileMemoryMapped = TRUE; + ctxt->pData = (GByte*) addr + nAligment; + ctxt->pDataToFree = addr; + ctxt->nSize = nLength; + ctxt->nPageSize = CPLGetPageSize(); + ctxt->bSingleThreadUsage = FALSE; + ctxt->pfnFreeUserData = pfnFreeUserData; + ctxt->pCbkUserData = pCbkUserData; + + return ctxt; +} + +/************************************************************************/ +/* CPLVirtualMemDerivedNew() */ +/************************************************************************/ + +CPLVirtualMem *CPLVirtualMemDerivedNew(CPLVirtualMem* pVMemBase, + vsi_l_offset nOffset, + vsi_l_offset nSize, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + if( nOffset + nSize > pVMemBase->nSize ) + return NULL; + + CPLVirtualMem* ctxt = (CPLVirtualMem* )CPLCalloc(1, sizeof(CPLVirtualMem)); + ctxt->nRefCount = 1; + ctxt->pVMemBase = pVMemBase; + pVMemBase->nRefCount ++; + ctxt->eAccessMode = pVMemBase->eAccessMode; + ctxt->bFileMemoryMapped = pVMemBase->bFileMemoryMapped; + ctxt->pData = (GByte*) pVMemBase->pData + nOffset; + ctxt->pDataToFree = NULL; + ctxt->nSize = nSize; + ctxt->nPageSize = pVMemBase->nPageSize; + ctxt->bSingleThreadUsage = pVMemBase->bSingleThreadUsage; + ctxt->pfnFreeUserData = pfnFreeUserData; + ctxt->pCbkUserData = pCbkUserData; + + return ctxt; +} + +/************************************************************************/ +/* CPLVirtualMemFree() */ +/************************************************************************/ + +void CPLVirtualMemFree(CPLVirtualMem* ctxt) +{ + size_t nRoundedMappingSize; + + if( ctxt == NULL || --(ctxt->nRefCount) > 0 ) + return; + + if( ctxt->pVMemBase != NULL ) + { + CPLVirtualMemFree(ctxt->pVMemBase); + if( ctxt->pfnFreeUserData != NULL ) + ctxt->pfnFreeUserData(ctxt->pCbkUserData); + CPLFree(ctxt); + return; + } + + if( ctxt->bFileMemoryMapped ) + { + size_t nMappingSize = ctxt->nSize + (GByte*)ctxt->pData - (GByte*)ctxt->pDataToFree; + assert(munmap(ctxt->pDataToFree, nMappingSize) == 0); + if( ctxt->pfnFreeUserData != NULL ) + ctxt->pfnFreeUserData(ctxt->pCbkUserData); + CPLFree(ctxt); + return; + } + + CPLVirtualMemManagerUnregisterVirtualMem(ctxt); + + nRoundedMappingSize = ((ctxt->nSize + 2 * ctxt->nPageSize - 1) / + ctxt->nPageSize) * ctxt->nPageSize; + if( ctxt->eAccessMode == VIRTUALMEM_READWRITE && + ctxt->pfnUnCachePage != NULL ) + { + size_t i; + for(i = 0; i < nRoundedMappingSize / ctxt->nPageSize; i++) + { + if( TEST_BIT(ctxt->pabitRWMappedPages, i) ) + { + void* addr = (char*)ctxt->pData + i * ctxt->nPageSize; + ctxt->pfnUnCachePage(ctxt, + i * ctxt->nPageSize, + addr, + ctxt->nPageSize, + ctxt->pCbkUserData); + } + } + } + assert(munmap(ctxt->pDataToFree, nRoundedMappingSize) == 0); + CPLFree(ctxt->pabitMappedPages); + CPLFree(ctxt->pabitRWMappedPages); + CPLFree(ctxt->panLRUPageIndices); +#ifndef HAVE_5ARGS_MREMAP + if( !ctxt->bSingleThreadUsage ) + { + CPLFree(ctxt->pahThreads); + CPLDestroyMutex(ctxt->hMutexThreadArray); + } +#endif + if( ctxt->pfnFreeUserData != NULL ) + ctxt->pfnFreeUserData(ctxt->pCbkUserData); + + CPLFree(ctxt); +} + +/************************************************************************/ +/* CPLVirtualMemGetAddr() */ +/************************************************************************/ + +void* CPLVirtualMemGetAddr(CPLVirtualMem* ctxt) +{ + return ctxt->pData; +} + +/************************************************************************/ +/* CPLVirtualMemIsFileMapping() */ +/************************************************************************/ + +int CPLVirtualMemIsFileMapping(CPLVirtualMem* ctxt) +{ + return ctxt->bFileMemoryMapped; +} + +/************************************************************************/ +/* CPLVirtualMemGetAccessMode() */ +/************************************************************************/ + +CPLVirtualMemAccessMode CPLVirtualMemGetAccessMode(CPLVirtualMem* ctxt) +{ + return ctxt->eAccessMode; +} + +/************************************************************************/ +/* CPLVirtualMemGetPageSize() */ +/************************************************************************/ + +size_t CPLVirtualMemGetPageSize(CPLVirtualMem* ctxt) +{ + return ctxt->nPageSize; +} + +/************************************************************************/ +/* CPLVirtualMemGetSize() */ +/************************************************************************/ + +size_t CPLVirtualMemGetSize(CPLVirtualMem* ctxt) +{ + return ctxt->nSize; +} + +#ifndef HAVE_5ARGS_MREMAP + +static volatile int nCountThreadsInSigUSR1 = 0; +static volatile int nWaitHelperThread = 0; + +/************************************************************************/ +/* CPLVirtualMemSIGUSR1Handler() */ +/************************************************************************/ + +static void CPLVirtualMemSIGUSR1Handler(int signum_unused, + siginfo_t* the_info_unused, + void* the_ctxt_unused) +{ + /* fprintfstderr("entering CPLVirtualMemSIGUSR1Handler %X\n", pthread_self()); */ + (void)signum_unused; + (void)the_info_unused; + (void)the_ctxt_unused; + /* I guess this is only POSIX correct if it is implemented by an intrinsic */ + CPLAtomicInc(&nCountThreadsInSigUSR1); + while( nWaitHelperThread ) + usleep(1); /* not explicitely indicated as signal-async-safe, but hopefully ok */ + CPLAtomicDec(&nCountThreadsInSigUSR1); + /* fprintfstderr("leaving CPLVirtualMemSIGUSR1Handler %X\n", pthread_self()); */ +} +#endif + +/************************************************************************/ +/* CPLVirtualMemIsAccessThreadSafe() */ +/************************************************************************/ + +int CPLVirtualMemIsAccessThreadSafe(CPLVirtualMem* ctxt) +{ + return !ctxt->bSingleThreadUsage; +} + +/************************************************************************/ +/* CPLVirtualMemDeclareThread() */ +/************************************************************************/ + +void CPLVirtualMemDeclareThread(CPLVirtualMem* ctxt) +{ + if( ctxt->bFileMemoryMapped ) + return; +#ifndef HAVE_5ARGS_MREMAP + assert( !ctxt->bSingleThreadUsage ); + CPLAcquireMutex(ctxt->hMutexThreadArray, 1000.0); + ctxt->pahThreads = (pthread_t*) CPLRealloc(ctxt->pahThreads, + (ctxt->nThreads + 1) * sizeof(pthread_t)); + ctxt->pahThreads[ctxt->nThreads] = pthread_self(); + ctxt->nThreads ++; + + CPLReleaseMutex(ctxt->hMutexThreadArray); +#endif +} + +/************************************************************************/ +/* CPLVirtualMemUnDeclareThread() */ +/************************************************************************/ + +void CPLVirtualMemUnDeclareThread(CPLVirtualMem* ctxt) +{ + if( ctxt->bFileMemoryMapped ) + return; +#ifndef HAVE_5ARGS_MREMAP + int i; + pthread_t self = pthread_self(); + assert( !ctxt->bSingleThreadUsage ); + CPLAcquireMutex(ctxt->hMutexThreadArray, 1000.0); + for(i = 0; i < ctxt->nThreads; i++) + { + if( ctxt->pahThreads[i] == self ) + { + if( i < ctxt->nThreads - 1 ) + memmove(ctxt->pahThreads + i + 1, + ctxt->pahThreads + i, + (ctxt->nThreads - 1 - i) * sizeof(pthread_t)); + ctxt->nThreads --; + break; + } + } + + CPLReleaseMutex(ctxt->hMutexThreadArray); +#endif +} + + +/************************************************************************/ +/* CPLVirtualMemGetPageToFill() */ +/************************************************************************/ + +/* Must be paired with CPLVirtualMemAddPage */ +static +void* CPLVirtualMemGetPageToFill(CPLVirtualMem* ctxt, void* start_page_addr) +{ + void* pPageToFill; + + if( ctxt->bSingleThreadUsage ) + { + pPageToFill = start_page_addr; + assert(mprotect(pPageToFill, ctxt->nPageSize, PROT_READ | PROT_WRITE) == 0); + } + else + { +#ifndef HAVE_5ARGS_MREMAP + CPLAcquireMutex(ctxt->hMutexThreadArray, 1000.0); + if( ctxt->nThreads == 1 ) + { + pPageToFill = start_page_addr; + assert(mprotect(pPageToFill, ctxt->nPageSize, PROT_READ | PROT_WRITE) == 0); + } + else +#endif + { + /* Allocate a temporary writable page that the user */ + /* callback can fill */ + pPageToFill = mmap(NULL, ctxt->nPageSize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(pPageToFill != MAP_FAILED); + } + } + return pPageToFill; +} + +/************************************************************************/ +/* CPLVirtualMemAddPage() */ +/************************************************************************/ + +static +void CPLVirtualMemAddPage(CPLVirtualMem* ctxt, void* target_addr, void* pPageToFill, + OpType opType, pthread_t hRequesterThread) +{ + size_t iPage = (int)(((char*)target_addr - (char*)ctxt->pData) / ctxt->nPageSize); + if( ctxt->nLRUSize == ctxt->nCacheMaxSizeInPages ) + { + /* fprintfstderr("uncaching page %d\n", iPage); */ + int nOldPage = ctxt->panLRUPageIndices[ctxt->iLRUStart]; + void* addr = (char*)ctxt->pData + nOldPage * ctxt->nPageSize; + if( ctxt->eAccessMode == VIRTUALMEM_READWRITE && + ctxt->pfnUnCachePage != NULL && + TEST_BIT(ctxt->pabitRWMappedPages, nOldPage) ) + { + size_t nToBeEvicted = ctxt->nPageSize; + if( (char*)addr + nToBeEvicted >= (char*) ctxt->pData + ctxt->nSize ) + nToBeEvicted = (char*) ctxt->pData + ctxt->nSize - (char*)addr; + + ctxt->pfnUnCachePage(ctxt, + nOldPage * ctxt->nPageSize, + addr, + nToBeEvicted, + ctxt->pCbkUserData); + } + /* "Free" the least recently used page */ + UNSET_BIT(ctxt->pabitMappedPages, nOldPage); + UNSET_BIT(ctxt->pabitRWMappedPages, nOldPage); + /* Free the old page */ + /* Not sure how portable it is to do that that way... */ + assert(mmap(addr, ctxt->nPageSize, PROT_NONE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == addr); + } + ctxt->panLRUPageIndices[ctxt->iLRUStart] = iPage; + ctxt->iLRUStart = (ctxt->iLRUStart + 1) % ctxt->nCacheMaxSizeInPages; + if( ctxt->nLRUSize < ctxt->nCacheMaxSizeInPages ) + { + ctxt->nLRUSize ++; + } + SET_BIT(ctxt->pabitMappedPages, iPage); + + if( ctxt->bSingleThreadUsage ) + { + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else if( ctxt->eAccessMode != VIRTUALMEM_READONLY ) + { + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ) == 0); + } + } + else + { +#ifdef HAVE_5ARGS_MREMAP + (void)hRequesterThread; + + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else if( ctxt->eAccessMode != VIRTUALMEM_READONLY ) + { + /* Turn the temporary page read-only before remapping it. We will only turn it */ + /* writtable when a new fault occurs (and that the mapping is writable) */ + assert(mprotect(pPageToFill, ctxt->nPageSize, PROT_READ) == 0); + } + /* Can now remap the pPageToFill onto the target page */ + assert(mremap(pPageToFill, ctxt->nPageSize, ctxt->nPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, target_addr) == target_addr); + +#else + if (ctxt->nThreads > 1 ) + { + int i; + + /* Pause threads that share this mem view */ + CPLAtomicInc(&nWaitHelperThread); + + /* Install temporary SIGUSR1 signal handler */ + struct sigaction act, oldact; + act.sa_sigaction = CPLVirtualMemSIGUSR1Handler; + sigemptyset (&act.sa_mask); + /* We don't want the sigsegv handler to be called when we are */ + /* running the sigusr1 handler */ + assert(sigaddset(&act.sa_mask, SIGSEGV) == 0); + act.sa_flags = 0; + assert(sigaction(SIGUSR1, &act, &oldact) == 0); + + for(i = 0; i < ctxt->nThreads; i++) + { + if( ctxt->pahThreads[i] != hRequesterThread ) + { + /* fprintfstderr("stopping thread %X\n", ctxt->pahThreads[i]); */ + assert(pthread_kill( ctxt->pahThreads[i], SIGUSR1 ) == 0); + } + } + + /* Wait that they are all paused */ + while( nCountThreadsInSigUSR1 != ctxt->nThreads-1 ) + usleep(1); + + /* Restore old SIGUSR1 signal handler */ + assert(sigaction(SIGUSR1, &oldact, NULL) == 0); + + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ | PROT_WRITE) == 0); + /*fprintfstderr("memcpying page %d\n", iPage);*/ + memcpy(target_addr, pPageToFill, ctxt->nPageSize); + + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else + { + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ) == 0); + } + + /* Wake up sleeping threads */ + CPLAtomicDec(&nWaitHelperThread); + while( nCountThreadsInSigUSR1 != 0 ) + usleep(1); + + assert(munmap(pPageToFill, ctxt->nPageSize) == 0); + } + else + { + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else if( ctxt->eAccessMode != VIRTUALMEM_READONLY ) + { + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ) == 0); + } + } + + CPLReleaseMutex(ctxt->hMutexThreadArray); +#endif + } +} + +/************************************************************************/ +/* CPLVirtualMemGetOpTypeImm() */ +/************************************************************************/ + +#if defined(__x86_64__) || defined(__i386__) +static OpType CPLVirtualMemGetOpTypeImm(GByte val_rip) +{ + OpType opType = OP_UNKNOWN; + if( (/*val_rip >= 0x00 &&*/ val_rip <= 0x07) || + (val_rip >= 0x40 && val_rip <= 0x47) ) /* add $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x08 && val_rip <= 0x0f) || + (val_rip >= 0x48 && val_rip <= 0x4f) ) /* or $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x20 && val_rip <= 0x27) || + (val_rip >= 0x60 && val_rip <= 0x67) ) /* and $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x28 && val_rip <= 0x2f) || + (val_rip >= 0x68 && val_rip <= 0x6f) ) /* sub $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x30 && val_rip <= 0x37) || + (val_rip >= 0x70 && val_rip <= 0x77) ) /* xor $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x38 && val_rip <= 0x3f) || + (val_rip >= 0x78 && val_rip <= 0x7f) ) /* cmp $,(X) */ + opType = OP_LOAD; + return opType; +} +#endif + +/************************************************************************/ +/* CPLVirtualMemGetOpType() */ +/************************************************************************/ + +/* We don't need exhaustivity. It is just a hint for an optimization : */ +/* if the fault occurs on a store operation, then we can directly put */ +/* the page in writable mode if the mapping allows it */ + +static OpType CPLVirtualMemGetOpType(const GByte* rip) +{ + OpType opType = OP_UNKNOWN; + +#if defined(__x86_64__) || defined(__i386__) + switch(rip[0]) + { + case 0x00: /* add %al,(%rax) */ + case 0x01: /* add %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x02: /* add (%rax),%al */ + case 0x03: /* add (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x08: /* or %al,(%rax) */ + case 0x09: /* or %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x0a: /* or (%rax),%al */ + case 0x0b: /* or (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x0f: + { + switch(rip[1]) + { + case 0xb6: /* movzbl (%rax),%eax */ + case 0xb7: /* movzwl (%rax),%eax */ + case 0xbe: /* movsbl (%rax),%eax */ + case 0xbf: /* movswl (%rax),%eax */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + case 0xc6: /* movb $,(%rax) */ + case 0xc7: /* movl $,(%rax) */ + opType = OP_STORE; + break; + + case 0x20: /* and %al,(%rax) */ + case 0x21: /* and %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x22: /* and (%rax),%al */ + case 0x23: /* and (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x28: /* sub %al,(%rax) */ + case 0x29: /* sub %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x2a: /* sub (%rax),%al */ + case 0x2b: /* sub (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x30: /* xor %al,(%rax) */ + case 0x31: /* xor %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x32: /* xor (%rax),%al */ + case 0x33: /* xor (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x38: /* cmp %al,(%rax) */ + case 0x39: /* cmp %eax,(%rax) */ + opType = OP_LOAD; + break; + case 0x40: + { + switch(rip[1]) + { + case 0x00: /* add %spl,(%rax) */ + opType = OP_STORE; + break; + case 0x02: /* add (%rax),%spl */ + opType = OP_LOAD; + break; + case 0x28: /* sub %spl,(%rax) */ + opType = OP_STORE; + break; + case 0x2a: /* sub (%rax),%spl */ + opType = OP_LOAD; + break; + case 0x3a: /* cmp (%rax),%spl */ + opType = OP_LOAD; + break; + case 0x8a: /* mov (%rax),%spl */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } +#if defined(__x86_64__) + case 0x41: /* reg=%al/%eax, X=%r8 */ + case 0x42: /* reg=%al/%eax, X=%rax,%r8,1 */ + case 0x43: /* reg=%al/%eax, X=%r8,%r8,1 */ + case 0x44: /* reg=%r8b/%r8w, X = %rax */ + case 0x45: /* reg=%r8b/%r8w, X = %r8 */ + case 0x46: /* reg=%r8b/%r8w, X = %rax,%r8,1 */ + case 0x47: /* reg=%r8b/%r8w, X = %r8,%r8,1 */ + { + switch(rip[1]) + { + case 0x00: /* add regb,(X) */ + case 0x01: /* add regl,(X) */ + opType = OP_STORE; + break; + case 0x02: /* add (X),regb */ + case 0x03: /* add (X),regl */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[2]) + { + case 0xb6: /* movzbl (X),regl */ + case 0xb7: /* movzwl (X),regl */ + case 0xbe: /* movsbl (X),regl */ + case 0xbf: /* movswl (X),regl */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + case 0x28: /* sub regb,(X) */ + case 0x29: /* sub regl,(X) */ + opType = OP_STORE; + break; + case 0x2a: /* sub (X),regb */ + case 0x2b: /* sub (X),regl */ + opType = OP_LOAD; + break; + case 0x38: /* cmp regb,(X) */ + case 0x39: /* cmp regl,(X) */ + opType = OP_LOAD; + break; + case 0x80: /* cmpb,... $,(X) */ + case 0x81: /* cmpl,... $,(X) */ + case 0x83: /* cmpl,... $,(X) */ + opType = CPLVirtualMemGetOpTypeImm(rip[2]); + break; + case 0x88: /* mov regb,(X) */ + case 0x89: /* mov regl,(X) */ + opType = OP_STORE; + break; + case 0x8a: /* mov (X),regb */ + case 0x8b: /* mov (X),regl */ + opType = OP_LOAD; + break; + case 0xc6: /* movb $,(X) */ + case 0xc7: /* movl $,(X) */ + opType = OP_STORE; + break; + case 0x84: /* test %al,(X) */ + opType = OP_LOAD; + break; + case 0xf6: /* testb $,(X) or notb (X) */ + case 0xf7: /* testl $,(X) or notl (X)*/ + { + if( rip[2] < 0x10 ) /* test (X) */ + opType = OP_LOAD; + else /* not (X) */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } + case 0x48: /* reg=%rax, X=%rax or %rax,%rax,1 */ + case 0x49: /* reg=%rax, X=%r8 or %r8,%rax,1 */ + case 0x4a: /* reg=%rax, X=%rax,%r8,1 */ + case 0x4b: /* reg=%rax, X=%r8,%r8,1 */ + case 0x4c: /* reg=%r8, X=%rax or %rax,%rax,1 */ + case 0x4d: /* reg=%r8, X=%r8 or %r8,%rax,1 */ + case 0x4e: /* reg=%r8, X=%rax,%r8,1 */ + case 0x4f: /* reg=%r8, X=%r8,%r8,1 */ + { + switch(rip[1]) + { + case 0x01: /* add reg,(X) */ + opType = OP_STORE; + break; + case 0x03: /* add (X),reg */ + opType = OP_LOAD; + break; + + case 0x09: /* or reg,(%rax) */ + opType = OP_STORE; + break; + case 0x0b: /* or (%rax),reg */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[2]) + { + case 0xc3: /* movnti reg,(X) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + case 0x21: /* and reg,(X) */ + opType = OP_STORE; + break; + case 0x23: /* and (X),reg */ + opType = OP_LOAD; + break; + + case 0x29: /* sub reg,(X) */ + opType = OP_STORE; + break; + case 0x2b: /* sub (X),reg */ + opType = OP_LOAD; + break; + + case 0x31: /* xor reg,(X) */ + opType = OP_STORE; + break; + case 0x33: /* xor (X),reg */ + opType = OP_LOAD; + break; + + case 0x39: /* cmp reg,(X) */ + opType = OP_LOAD; + break; + + case 0x81: + case 0x83: + opType = CPLVirtualMemGetOpTypeImm(rip[2]); + break; + + case 0x85: /* test reg,(X) */ + opType = OP_LOAD; + break; + + case 0x89: /* mov reg,(X) */ + opType = OP_STORE; + break; + case 0x8b: /* mov (X),reg */ + opType = OP_LOAD; + break; + + case 0xc7: /* movq $,(X) */ + opType = OP_STORE; + break; + + case 0xf7: + { + if( rip[2] < 0x10 ) /* testq $,(X) */ + opType = OP_LOAD; + else /* notq (X) */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } +#endif + case 0x66: + { + switch(rip[1]) + { + case 0x01: /* add %ax,(%rax) */ + opType = OP_STORE; + break; + case 0x03: /* add (%rax),%ax */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[2]) + { + case 0x2e: /* ucomisd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x6f: /* movdqa (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqa %xmm0,(%rax) */ + opType = OP_STORE; + break; + case 0xb6: /* movzbw (%rax),%ax */ + opType = OP_LOAD; + break; + case 0xe7: /* movntdq %xmm0,(%rax) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + case 0x29: /* sub %ax,(%rax) */ + opType = OP_STORE; + break; + case 0x2b: /* sub (%rax),%ax */ + opType = OP_LOAD; + break; + case 0x39: /* cmp %ax,(%rax) */ + opType = OP_LOAD; + break; +#if defined(__x86_64__) + case 0x41: /* reg = %ax (or %xmm0), X = %r8 */ + case 0x42: /* reg = %ax (or %xmm0), X = %rax,%r8,1 */ + case 0x43: /* reg = %ax (or %xmm0), X = %r8,%r8,1 */ + case 0x44: /* reg = %r8w (or %xmm8), X = %rax */ + case 0x45: /* reg = %r8w (or %xmm8), X = %r8 */ + case 0x46: /* reg = %r8w (or %xmm8), X = %rax,%r8,1 */ + case 0x47: /* reg = %r8w (or %xmm8), X = %r8,%r8,1 */ + { + switch(rip[2]) + { + case 0x01: /* add reg,(X) */ + opType = OP_STORE; + break; + case 0x03: /* add (X),reg */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[3]) + { + case 0x2e: /* ucomisd (X),reg */ + opType = OP_LOAD; + break; + case 0x6f: /* movdqa (X),reg */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqa reg,(X) */ + opType = OP_STORE; + break; + case 0xb6: /* movzbw (X),reg */ + opType = OP_LOAD; + break; + case 0xe7: /* movntdq reg,(X) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + case 0x29: /* sub reg,(X) */ + opType = OP_STORE; + break; + case 0x2b: /* sub (X),reg */ + opType = OP_LOAD; + break; + case 0x39: /* cmp reg,(X) */ + opType = OP_LOAD; + break; + case 0x81: /* cmpw,... $,(X) */ + case 0x83: /* cmpw,... $,(X) */ + opType = CPLVirtualMemGetOpTypeImm(rip[3]); + break; + case 0x85: /* test reg,(X) */ + opType = OP_LOAD; + break; + case 0x89: /* mov reg,(X) */ + opType = OP_STORE; + break; + case 0x8b: /* mov (X),reg */ + opType = OP_LOAD; + break; + case 0xc7: /* movw $,(X) */ + opType = OP_STORE; + break; + case 0xf7: + { + if( rip[3] < 0x10 ) /* testw $,(X) */ + opType = OP_LOAD; + else /* notw (X) */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } +#endif + case 0x81: /* cmpw,... $,(%rax) */ + case 0x83: /* cmpw,... $,(%rax) */ + opType = CPLVirtualMemGetOpTypeImm(rip[2]); + break; + + case 0x85: /* test %ax,(%rax) */ + opType = OP_LOAD; + break; + case 0x89: /* mov %ax,(%rax) */ + opType = OP_STORE; + break; + case 0x8b: /* mov (%rax),%ax */ + opType = OP_LOAD; + break; + case 0xc7: /* movw $,(%rax) */ + opType = OP_STORE; + break; + case 0xf3: + { + switch( rip[2] ) + { + case 0xa5: /* rep movsw %ds:(%rsi),%es:(%rdi) */ + opType = OP_MOVS_RSI_RDI; + break; + default: + break; + } + break; + } + case 0xf7: /* testw $,(%rax) or notw (%rax) */ + { + if( rip[2] < 0x10 ) /* test */ + opType = OP_LOAD; + else /* not */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } + case 0x80: /* cmpb,... $,(%rax) */ + case 0x81: /* cmpl,... $,(%rax) */ + case 0x83: /* cmpl,... $,(%rax) */ + opType = CPLVirtualMemGetOpTypeImm(rip[1]); + break; + case 0x84: /* test %al,(%rax) */ + case 0x85: /* test %eax,(%rax) */ + opType = OP_LOAD; + break; + case 0x88: /* mov %al,(%rax) */ + opType = OP_STORE; + break; + case 0x89: /* mov %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x8a: /* mov (%rax),%al */ + opType = OP_LOAD; + break; + case 0x8b: /* mov (%rax),%eax */ + opType = OP_LOAD; + break; + case 0xd9: /* 387 float */ + { + if( rip[1] < 0x08 ) /* flds (%eax) */ + opType = OP_LOAD; + else if( rip[1] >= 0x18 && rip[1] <= 0x20 ) /* fstps (%eax) */ + opType = OP_STORE; + break; + } + case 0xf2: /* SSE 2 */ + { + switch(rip[1]) + { + case 0x0f: + { + switch(rip[2]) + { + case 0x10: /* movsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x11: /* movsd %xmm0,(%rax) */ + opType = OP_STORE; + break; + case 0x58: /* addsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x59: /* mulsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x5c: /* subsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x5e: /* divsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } +#if defined(__x86_64__) + case 0x41: /* reg=%xmm0, X=%r8 or %r8,%rax,1 */ + case 0x42: /* reg=%xmm0, X=%rax,%r8,1 */ + case 0x43: /* reg=%xmm0, X=%r8,%r8,1 */ + case 0x44: /* reg=%xmm8, X=%rax or %rax,%rax,1*/ + case 0x45: /* reg=%xmm8, X=%r8 or %r8,%rax,1 */ + case 0x46: /* reg=%xmm8, X=%rax,%r8,1 */ + case 0x47: /* reg=%xmm8, X=%r8,%r8,1 */ + { + switch(rip[2]) + { + case 0x0f: + { + switch(rip[3]) + { + case 0x10: /* movsd (X),reg */ + opType = OP_LOAD; + break; + case 0x11: /* movsd reg,(X) */ + opType = OP_STORE; + break; + case 0x58: /* addsd (X),reg */ + opType = OP_LOAD; + break; + case 0x59: /* mulsd (X),reg */ + opType = OP_LOAD; + break; + case 0x5c: /* subsd (X),reg */ + opType = OP_LOAD; + break; + case 0x5e: /* divsd (X),reg */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + default: + break; + } + break; + } +#endif + default: + break; + } + break; + } + case 0xf3: + { + switch(rip[1]) + { + case 0x0f: /* SSE 2 */ + { + switch(rip[2]) + { + case 0x10: /* movss (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x11: /* movss %xmm0,(%rax) */ + opType = OP_STORE; + break; + case 0x6f: /* movdqu (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqu %xmm0,(%rax) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } +#if defined(__x86_64__) + case 0x41: /* reg=%xmm0, X=%r8 */ + case 0x42: /* reg=%xmm0, X=%rax,%r8,1 */ + case 0x43: /* reg=%xmm0, X=%r8,%r8,1 */ + case 0x44: /* reg=%xmm8, X = %rax */ + case 0x45: /* reg=%xmm8, X = %r8 */ + case 0x46: /* reg=%xmm8, X = %rax,%r8,1 */ + case 0x47: /* reg=%xmm8, X = %r8,%r8,1 */ + { + switch(rip[2]) + { + case 0x0f: /* SSE 2 */ + { + switch(rip[3]) + { + case 0x10: /* movss (X),reg */ + opType = OP_LOAD; + break; + case 0x11: /* movss reg,(X) */ + opType = OP_STORE; + break; + case 0x6f: /* movdqu (X),reg */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqu reg,(X) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + default: + break; + } + break; + } + case 0x48: + { + switch(rip[2]) + { + case 0xa5: /* rep movsq %ds:(%rsi),%es:(%rdi) */ + opType = OP_MOVS_RSI_RDI; + break; + default: + break; + } + break; + } +#endif + case 0xa4: /* rep movsb %ds:(%rsi),%es:(%rdi) */ + case 0xa5: /* rep movsl %ds:(%rsi),%es:(%rdi) */ + opType = OP_MOVS_RSI_RDI; + break; + case 0xa6: /* repz cmpsb %es:(%rdi),%ds:(%rsi) */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + case 0xf6: /* testb $,(%rax) or notb (%rax) */ + case 0xf7: /* testl $,(%rax) or notl (%rax) */ + { + if( rip[1] < 0x10 ) /* test */ + opType = OP_LOAD; + else /* not */ + opType = OP_STORE; + break; + } + default: + break; + } +#endif + return opType; +} + +/************************************************************************/ +/* CPLVirtualMemManagerPinAddrInternal() */ +/************************************************************************/ + +static int CPLVirtualMemManagerPinAddrInternal(CPLVirtualMemMsgToWorkerThread* msg) +{ + char wait_ready; + char response_buf[4]; + + /* Wait for the helper thread to be ready to process another request */ + while(TRUE) + { + int ret = read(pVirtualMemManager->pipefd_wait_thread[0], &wait_ready, 1); + if( ret < 0 && errno == EINTR ) + ; + else + { + assert(ret == 1); + break; + } + } + + /* Pass the address that caused the fault to the helper thread */ + assert(write(pVirtualMemManager->pipefd_to_thread[1], msg, sizeof(*msg)) + == sizeof(*msg)); + + /* Wait that the helper thread has fixed the fault */ + while(TRUE) + { + int ret = read(pVirtualMemManager->pipefd_from_thread[0], response_buf, 4); + if( ret < 0 && errno == EINTR ) + ; + else + { + assert(ret == 4); + break; + } + } + + /* In case the helper thread did not recognize the address as being */ + /* one that it should take care of, just rely on the previous SIGSEGV */ + /* handler (with might abort the process) */ + return( memcmp(response_buf, MAPPING_FOUND, 4) == 0 ); +} + +/************************************************************************/ +/* CPLVirtualMemPin() */ +/************************************************************************/ + +void CPLVirtualMemPin(CPLVirtualMem* ctxt, + void* pAddr, size_t nSize, int bWriteOp) +{ + if( ctxt->bFileMemoryMapped ) + return; + + CPLVirtualMemMsgToWorkerThread msg; + size_t i = 0, n; + + memset(&msg, 0, sizeof(msg)); + msg.hRequesterThread = pthread_self(); + msg.opType = (bWriteOp) ? OP_STORE : OP_LOAD; + + char* pBase = (char*)ALIGN_DOWN(pAddr, ctxt->nPageSize); + n = ((char*)pAddr - pBase + nSize + ctxt->nPageSize - 1) / ctxt->nPageSize; + for(i=0; inPageSize; + CPLVirtualMemManagerPinAddrInternal(&msg); + } +} + +/************************************************************************/ +/* CPLVirtualMemManagerSIGSEGVHandler() */ +/************************************************************************/ + +#if defined(__x86_64__) +#define REG_IP REG_RIP +#define REG_SI REG_RSI +#define REG_DI REG_RDI +#elif defined(__i386__) +#define REG_IP REG_EIP +#define REG_SI REG_ESI +#define REG_DI REG_EDI +#endif + +/* We must take care of only using "asynchronous-signal-safe" functions in a signal handler */ +/* pthread_self(), read() and write() are such. See */ +/* https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers */ +static void CPLVirtualMemManagerSIGSEGVHandler(int the_signal, + siginfo_t* the_info, + void* the_ctxt) +{ + CPLVirtualMemMsgToWorkerThread msg; + + memset(&msg, 0, sizeof(msg)); + msg.pFaultAddr = the_info->si_addr; + msg.hRequesterThread = pthread_self(); + msg.opType = OP_UNKNOWN; + +#if defined(__x86_64__) || defined(__i386__) + ucontext_t* the_ucontext = (ucontext_t* )the_ctxt; + const GByte* rip = (const GByte*)the_ucontext->uc_mcontext.gregs[REG_IP]; + msg.opType = CPLVirtualMemGetOpType(rip); + /*fprintfstderr("at rip %p, bytes: %02x %02x %02x %02x\n", + rip, rip[0], rip[1], rip[2], rip[3]);*/ + if( msg.opType == OP_MOVS_RSI_RDI ) + { + void* rsi = (void*)the_ucontext->uc_mcontext.gregs[REG_SI]; + void* rdi = (void*)the_ucontext->uc_mcontext.gregs[REG_DI]; + + /*fprintfstderr("fault=%p rsi=%p rsi=%p\n", msg.pFaultAddr, rsi, rdi);*/ + if( msg.pFaultAddr == rsi ) + { + /*fprintfstderr("load\n");*/ + msg.opType = OP_LOAD; + } + else if( msg.pFaultAddr == rdi ) + { + /*fprintfstderr("store\n");*/ + msg.opType = OP_STORE; + } + } +#ifdef DEBUG_VIRTUALMEM + else if( msg.opType == OP_UNKNOWN ) + { + static int bHasWarned = FALSE; + if( !bHasWarned ) + { + bHasWarned = TRUE; + fprintfstderr("at rip %p, unknown bytes: %02x %02x %02x %02x\n", + rip, rip[0], rip[1], rip[2], rip[3]); + } + } +#endif +#endif + + /*fprintfstderr("entering handler for %X (addr=%p)\n", pthread_self(), the_info->si_addr); */ + + if( the_info->si_code != SEGV_ACCERR ) + { + pVirtualMemManager->oldact.sa_sigaction(the_signal, the_info, the_ctxt); + return; + } + + if( !CPLVirtualMemManagerPinAddrInternal(&msg) ) + { + /* In case the helper thread did not recognize the address as being */ + /* one that it should take care of, just rely on the previous SIGSEGV */ + /* handler (with might abort the process) */ + pVirtualMemManager->oldact.sa_sigaction(the_signal, the_info, the_ctxt); + } + + /*fprintfstderr("leaving handler for %X (addr=%p)\n", pthread_self(), the_info->si_addr);*/ +} + +/************************************************************************/ +/* CPLVirtualMemManagerThread() */ +/************************************************************************/ + +static void CPLVirtualMemManagerThread(void* unused_param) +{ + (void)unused_param; + + while(TRUE) + { + char i_m_ready = 1; + int i; + CPLVirtualMem* ctxt = NULL; + int bMappingFound = FALSE; + CPLVirtualMemMsgToWorkerThread msg; + + /* Signal that we are ready to process a new request */ + assert(write(pVirtualMemManager->pipefd_wait_thread[1], &i_m_ready, 1) == 1); + + /* Fetch the address to process */ + assert(read(pVirtualMemManager->pipefd_to_thread[0], &msg, + sizeof(msg)) == sizeof(msg)); + + /* If CPLVirtualMemManagerTerminate() is called, it will use BYEBYE_ADDR as a */ + /* means to ask for our termination */ + if( msg.pFaultAddr == BYEBYE_ADDR ) + break; + + /* Lookup for a mapping that contains addr */ + CPLAcquireMutex(hVirtualMemManagerMutex, 1000.0); + for(i=0;inVirtualMemCount;i++) + { + ctxt = pVirtualMemManager->pasVirtualMem[i]; + if( (char*)msg.pFaultAddr >= (char*) ctxt->pData && + (char*)msg.pFaultAddr < (char*) ctxt->pData + ctxt->nSize ) + { + bMappingFound = TRUE; + break; + } + } + CPLReleaseMutex(hVirtualMemManagerMutex); + + if( bMappingFound ) + { + char* start_page_addr = (char*)ALIGN_DOWN(msg.pFaultAddr, ctxt->nPageSize); + int iPage = (int) + (((char*)start_page_addr - (char*)ctxt->pData) / ctxt->nPageSize); + + if( iPage == ctxt->iLastPage ) + { + /* In case 2 threads try to access the same page */ + /* concurrently it is possible that we are asked to mapped */ + /* the page again whereas it is always mapped. However */ + /* if that number of successive retries is too high, this */ + /* is certainly a sign that something else happen, like */ + /* trying to write-access a read-only page */ + /* 100 is a bit of magic number. I believe it must be */ + /* at least the number of concurrent threads. 100 seems */ + /* to be really safe ! */ + ctxt->nRetry ++; + /* fprintfstderr("retry on page %d : %d\n", (int)iPage, ctxt->nRetry); */ + if( ctxt->nRetry >= 100 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "CPLVirtualMemManagerThread: trying to " + "write into read-only mapping"); + assert(write(pVirtualMemManager->pipefd_from_thread[1], + MAPPING_NOT_FOUND, 4) == 4); + break; + } + else if( msg.opType != OP_LOAD && + ctxt->eAccessMode == VIRTUALMEM_READWRITE && + !TEST_BIT(ctxt->pabitRWMappedPages, iPage) ) + { + /* fprintfstderr("switching page %d to write mode\n", (int)iPage); */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + assert(mprotect(start_page_addr, ctxt->nPageSize, + PROT_READ | PROT_WRITE) == 0); + } + } + else + { + ctxt->iLastPage = iPage; + ctxt->nRetry = 0; + + if( TEST_BIT(ctxt->pabitMappedPages, iPage) ) + { + if( msg.opType != OP_LOAD && + ctxt->eAccessMode == VIRTUALMEM_READWRITE && + !TEST_BIT(ctxt->pabitRWMappedPages, iPage) ) + { + /*fprintfstderr("switching page %d to write mode\n", + iPage);*/ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + assert(mprotect(start_page_addr, ctxt->nPageSize, + PROT_READ | PROT_WRITE) == 0); + } + else + { + /*fprintfstderr("unexpected case for page %d\n", + iPage);*/ + } + } + else + { + void* pPageToFill; + size_t nToFill; + pPageToFill = CPLVirtualMemGetPageToFill(ctxt, start_page_addr); + + nToFill = ctxt->nPageSize; + if( start_page_addr + nToFill >= (char*) ctxt->pData + ctxt->nSize ) + nToFill = (char*) ctxt->pData + ctxt->nSize - start_page_addr; + + ctxt->pfnCachePage( + ctxt, + start_page_addr - (char*) ctxt->pData, + pPageToFill, + nToFill, + ctxt->pCbkUserData); + + /* Now remap this page to its target address and */ + /* register it in the LRU */ + CPLVirtualMemAddPage(ctxt, start_page_addr, pPageToFill, + msg.opType, msg.hRequesterThread); + } + } + + /* Warn the segfault handler that we have finished our job */ + assert(write(pVirtualMemManager->pipefd_from_thread[1], + MAPPING_FOUND, 4) == 4); + } + else + { + /* Warn the segfault handler that we have finished our job */ + /* but that the fault didn't occur in a memory range that is under */ + /* our responsability */ + CPLError(CE_Failure, CPLE_AppDefined, + "CPLVirtualMemManagerThread: no mapping found"); + assert(write(pVirtualMemManager->pipefd_from_thread[1], + MAPPING_NOT_FOUND, 4) == 4); + } + } +} + +/************************************************************************/ +/* CPLVirtualMemManagerInit() */ +/************************************************************************/ + +static void CPLVirtualMemManagerInit(void) +{ + CPLMutexHolderD(&hVirtualMemManagerMutex); + if( pVirtualMemManager != NULL ) + return; + + struct sigaction act; + pVirtualMemManager = (CPLVirtualMemManager*) CPLMalloc(sizeof(CPLVirtualMemManager)); + pVirtualMemManager->pasVirtualMem = NULL; + pVirtualMemManager->nVirtualMemCount = 0; + assert(pipe(pVirtualMemManager->pipefd_to_thread) == 0); + assert(pipe(pVirtualMemManager->pipefd_from_thread) == 0); + assert(pipe(pVirtualMemManager->pipefd_wait_thread) == 0); + + /* Install our custom SIGSEGV handler */ + act.sa_sigaction = CPLVirtualMemManagerSIGSEGVHandler; + sigemptyset (&act.sa_mask); + act.sa_flags = SA_SIGINFO; + assert(sigaction(SIGSEGV, &act, &pVirtualMemManager->oldact) == 0); + + /* Starts the helper thread */ + pVirtualMemManager->hHelperThread = + CPLCreateJoinableThread(CPLVirtualMemManagerThread, NULL); + assert(pVirtualMemManager->hHelperThread != NULL); +} + +/************************************************************************/ +/* CPLVirtualMemManagerTerminate() */ +/************************************************************************/ + +void CPLVirtualMemManagerTerminate(void) +{ + char wait_ready; + CPLVirtualMemMsgToWorkerThread msg; + + if( pVirtualMemManager == NULL ) + return; + + msg.pFaultAddr = BYEBYE_ADDR; + msg.opType = OP_UNKNOWN; + memset(&msg.hRequesterThread, 0, sizeof(msg.hRequesterThread)); + + /* Wait for the helper thread to be ready */ + assert(read(pVirtualMemManager->pipefd_wait_thread[0], + &wait_ready, 1) == 1); + + /* Ask it to terminate */ + assert(write(pVirtualMemManager->pipefd_to_thread[1], &msg, sizeof(msg)) == sizeof(msg)); + + /* Wait for its termination */ + CPLJoinThread(pVirtualMemManager->hHelperThread); + + /* Cleanup everything */ + while(pVirtualMemManager->nVirtualMemCount > 0) + CPLVirtualMemFree(pVirtualMemManager->pasVirtualMem[pVirtualMemManager->nVirtualMemCount - 1]); + CPLFree(pVirtualMemManager->pasVirtualMem); + + close(pVirtualMemManager->pipefd_to_thread[0]); + close(pVirtualMemManager->pipefd_to_thread[1]); + close(pVirtualMemManager->pipefd_from_thread[0]); + close(pVirtualMemManager->pipefd_from_thread[1]); + close(pVirtualMemManager->pipefd_wait_thread[0]); + close(pVirtualMemManager->pipefd_wait_thread[1]); + + /* Restore previous handler */ + sigaction(SIGSEGV, &pVirtualMemManager->oldact, NULL); + + CPLFree(pVirtualMemManager); + pVirtualMemManager = NULL; + + CPLDestroyMutex(hVirtualMemManagerMutex); + hVirtualMemManagerMutex = NULL; +} + +#else /* defined(__linux) && defined(CPL_MULTIPROC_PTHREAD) */ + +size_t CPLGetPageSize(void) +{ + return 0; +} + +CPLVirtualMem *CPLVirtualMemNew(size_t nSize, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemCachePageCbk pfnCachePage, + CPLVirtualMemUnCachePageCbk pfnUnCachePage, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "CPLVirtualMemNew() unsupported on this operating system / configuration"); + return NULL; +} + +int CPLIsVirtualMemFileMapAvailable(void) +{ + return FALSE; +} + +CPLVirtualMem *CPLVirtualMemFileMapNew(VSILFILE* fp, + vsi_l_offset nOffset, + vsi_l_offset nLength, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "CPLVirtualMemFileMapNew() unsupported on this operating system / configuration"); + return NULL; +} + +CPLVirtualMem *CPLVirtualMemDerivedNew(CPLVirtualMem* pVMemBase, + vsi_l_offset nOffset, + vsi_l_offset nSize, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "CPLVirtualMemDerivedNew() unsupported on this operating system / configuration"); + return NULL; +} + +void CPLVirtualMemFree(CPLVirtualMem* ctxt) +{ +} + +void* CPLVirtualMemGetAddr(CPLVirtualMem* ctxt) +{ + return NULL; +} + +size_t CPLVirtualMemGetSize(CPLVirtualMem* ctxt) +{ + return 0; +} + +int CPLVirtualMemIsFileMapping(CPLVirtualMem* ctxt) +{ + return FALSE; +} + +CPLVirtualMemAccessMode CPLVirtualMemGetAccessMode(CPLVirtualMem* ctxt) +{ + return VIRTUALMEM_READONLY; +} + +size_t CPLVirtualMemGetPageSize(CPLVirtualMem* ctxt) +{ + return 0; +} + +int CPLVirtualMemIsAccessThreadSafe(CPLVirtualMem* ctxt) +{ + return FALSE; +} + +void CPLVirtualMemDeclareThread(CPLVirtualMem* ctxt) +{ +} + +void CPLVirtualMemUnDeclareThread(CPLVirtualMem* ctxt) +{ +} + +void CPLVirtualMemPin(CPLVirtualMem* ctxt, + void* pAddr, size_t nSize, int bWriteOp) +{ +} + +void CPLVirtualMemManagerTerminate(void) +{ +} + +#endif /* defined(__linux) && defined(CPL_MULTIPROC_PTHREAD) */ diff --git a/cpl/cpl_virtualmem.h b/cpl/cpl_virtualmem.h new file mode 100644 index 0000000..95f63b7 --- /dev/null +++ b/cpl/cpl_virtualmem.h @@ -0,0 +1,389 @@ +/********************************************************************** + * $Id: cpl_virtualmem.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_virtualmem.h + * Project: CPL - Common Portability Library + * Purpose: Virtual memory + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_VIRTUAL_MEM_INCLUDED +#define _CPL_VIRTUAL_MEM_INCLUDED + +#include "cpl_port.h" +#include "cpl_vsi.h" + +CPL_C_START + +/** + * \file cpl_virtualmem.h + * + * Virtual memory management. + * + * This file provides mechanism to define virtual memory mappings, whose content + * is allocated transparently and filled on-the-fly. Those virtual memory mappings + * can be much larger than the available RAM, but only parts of the virtual + * memory mapping, in the limit of the allowed the cache size, will actually be + * physically allocated. + * + * This exploits low-level mechanisms of the operating system (virtual memory + * allocation, page protection and handler of virtual memory exceptions). + * + * It is also possible to create a virtual memory mapping from a file or part + * of a file. + * + * The current implementation is Linux only. + */ + +/** Opaque type that represents a virtual memory mapping. */ +typedef struct CPLVirtualMem CPLVirtualMem; + +/** Callback triggered when a still unmapped page of virtual memory is accessed. + * The callback has the responsibility of filling the page with relevant values + * + * @param ctxt virtual memory handle. + * @param nOffset offset of the page in the memory mapping. + * @param pPageToFill address of the page to fill. Note that the address might + * be a temporary location, and not at CPLVirtualMemGetAddr() + nOffset. + * @param nToFill number of bytes of the page. + * @param pUserData user data that was passed to CPLVirtualMemNew(). + */ +typedef void (*CPLVirtualMemCachePageCbk)(CPLVirtualMem* ctxt, + size_t nOffset, + void* pPageToFill, + size_t nToFill, + void* pUserData); + +/** Callback triggered when a dirty mapped page is going to be freed. + * (saturation of cache, or termination of the virtual memory mapping). + * + * @param ctxt virtual memory handle. + * @param nOffset offset of the page in the memory mapping. + * @param pPageToBeEvicted address of the page that will be flushed. Note that the address might + * be a temporary location, and not at CPLVirtualMemGetAddr() + nOffset. + * @param nToBeEvicted number of bytes of the page. + * @param pUserData user data that was passed to CPLVirtualMemNew(). + */ +typedef void (*CPLVirtualMemUnCachePageCbk)(CPLVirtualMem* ctxt, + size_t nOffset, + const void* pPageToBeEvicted, + size_t nToBeEvicted, + void* pUserData); + +/** Callback triggered when a virtual memory mapping is destroyed. + * @param pUserData user data that was passed to CPLVirtualMemNew(). + */ +typedef void (*CPLVirtualMemFreeUserData)(void* pUserData); + +/** Access mode of a virtual memory mapping. */ +typedef enum +{ + /*! The mapping is meant at being read-only, but writes will not be prevented. + Note that any content written will be lost. */ + VIRTUALMEM_READONLY, + /*! The mapping is meant at being read-only, and this will be enforced + through the operating system page protection mechanism. */ + VIRTUALMEM_READONLY_ENFORCED, + /*! The mapping is meant at being read-write, and modified pages can be saved + thanks to the pfnUnCachePage callback */ + VIRTUALMEM_READWRITE +} CPLVirtualMemAccessMode; + + +/** Return the size of a page of virtual memory. + * + * @return the page size. + * + * @since GDAL 2.0 + */ +size_t CPL_DLL CPLGetPageSize(void); + +/** Create a new virtual memory mapping. + * + * This will reserve an area of virtual memory of size nSize, whose size + * might be potentially much larger than the physical memory available. Initially, + * no physical memory will be allocated. As soon as memory pages will be accessed, + * they will be allocated transparently and filled with the pfnCachePage callback. + * When the allowed cache size is reached, the least recently used pages will + * be unallocated. + * + * On Linux AMD64 platforms, the maximum value for nSize is 128 TB. + * On Linux x86 platforms, the maximum value for nSize is 2 GB. + * + * Only supported on Linux for now. + * + * Note that on Linux, this function will install a SIGSEGV handler. The + * original handler will be restored by CPLVirtualMemManagerTerminate(). + * + * @param nSize size in bytes of the virtual memory mapping. + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM). + * @param nPageSizeHint hint for the page size. Must be a multiple of the + * system page size, returned by CPLGetPageSize(). + * Minimum value is generally 4096. Might be set to 0 to + * let the function determine a default page size. + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. + * @param eAccessMode permission to use for the virtual memory mapping. + * @param pfnCachePage callback triggered when a still unmapped page of virtual + * memory is accessed. The callback has the responsibility + * of filling the page with relevant values. + * @param pfnUnCachePage callback triggered when a dirty mapped page is going to + * be freed (saturation of cache, or termination of the + * virtual memory mapping). Might be NULL. + * @param pfnFreeUserData callback that can be used to free pCbkUserData. Might be + * NULL + * @param pCbkUserData user data passed to pfnCachePage and pfnUnCachePage. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 2.0 + */ + +CPLVirtualMem CPL_DLL *CPLVirtualMemNew(size_t nSize, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemCachePageCbk pfnCachePage, + CPLVirtualMemUnCachePageCbk pfnUnCachePage, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData); + + +/** Return if virtual memory mapping of a file is available. + * + * @return TRUE if virtual memory mapping of a file is available. + * @since GDAL 2.0 + */ +int CPL_DLL CPLIsVirtualMemFileMapAvailable(void); + +/** Create a new virtual memory mapping from a file. + * + * The file must be a "real" file recognized by the operating system, and not + * a VSI extended virtual file. + * + * In VIRTUALMEM_READWRITE mode, updates to the memory mapping will be written + * in the file. + * + * On Linux AMD64 platforms, the maximum value for nLength is 128 TB. + * On Linux x86 platforms, the maximum value for nLength is 2 GB. + * + * Only supported on Linux for now. + * + * @param fp Virtual file handle. + * @param nOffset Offset in the file to start the mapping from. + * @param nLength Length of the portion of the file to map into memory. + * @param eAccessMode Permission to use for the virtual memory mapping. This must + * be consistant with how the file has been opened. + * @param pfnFreeUserData callback that is called when the object is destroyed. + * @param pCbkUserData user data passed to pfnFreeUserData. + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 2.0 + */ +CPLVirtualMem CPL_DLL *CPLVirtualMemFileMapNew( VSILFILE* fp, + vsi_l_offset nOffset, + vsi_l_offset nLength, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData ); + +/** Create a new virtual memory mapping derived from an other virtual memory + * mapping. + * + * This may be usefull in case of creating mapping for pixel interleaved data. + * + * The new mapping takes a reference on the base mapping. + * + * @param pVMemBase Base virtual memory mapping + * @param nOffset Offset in the base virtual memory mapping from which to start + * the new mapping. + * @param nSize Size of the base virtual memory mapping to expose in the + * the new mapping. + * @param pfnFreeUserData callback that is called when the object is destroyed. + * @param pCbkUserData user data passed to pfnFreeUserData. + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 2.0 + */ +CPLVirtualMem CPL_DLL *CPLVirtualMemDerivedNew(CPLVirtualMem* pVMemBase, + vsi_l_offset nOffset, + vsi_l_offset nSize, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData); + +/** Free a virtual memory mapping. + * + * The pointer returned by CPLVirtualMemGetAddr() will no longer be valid. + * If the virtual memory mapping was created with read/write permissions and that + * they are dirty (i.e. modified) pages, they will be flushed through the + * pfnUnCachePage callback before being freed. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * + * @since GDAL 2.0 + */ +void CPL_DLL CPLVirtualMemFree(CPLVirtualMem* ctxt); + +/** Return the pointer to the start of a virtual memory mapping. + * + * The bytes in the range [p:p+CPLVirtualMemGetSize()-1] where p is the pointer + * returned by this function will be valid, until CPLVirtualMemFree() is called. + * + * Note that if a range of bytes used as an argument of a system call + * (such as read() or write()) contains pages that have not been "realized", the + * system call will fail with EFAULT. CPLVirtualMemPin() can be used to work + * around this issue. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the pointer to the start of a virtual memory mapping. + * + * @since GDAL 2.0 + */ +void CPL_DLL *CPLVirtualMemGetAddr(CPLVirtualMem* ctxt); + +/** Return the size of the virtual memory mapping. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the size of the virtual memory mapping. + * + * @since GDAL 2.0 + */ +size_t CPL_DLL CPLVirtualMemGetSize(CPLVirtualMem* ctxt); + +/** Return if the virtal memory mapping is a direct file mapping. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return TRUE if the virtal memory mapping is a direct file mapping. + * + * @since GDAL 2.0 + */ +int CPL_DLL CPLVirtualMemIsFileMapping(CPLVirtualMem* ctxt); + +/** Return the access mode of the virtual memory mapping. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the access mode of the virtual memory mapping. + * + * @since GDAL 2.0 + */ +CPLVirtualMemAccessMode CPL_DLL CPLVirtualMemGetAccessMode(CPLVirtualMem* ctxt); + +/** Return the page size associated to a virtual memory mapping. + * + * The value returned will be at least CPLGetPageSize(), but potentially + * larger. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the page size + * + * @since GDAL 2.0 + */ +size_t CPL_DLL CPLVirtualMemGetPageSize(CPLVirtualMem* ctxt); + +/** Return TRUE if this memory mapping can be accessed safely from concurrent + * threads. + * + * The situation that can cause problems is when several threads try to access + * a page of the mapping that is not yet mapped. + * + * The return value of this function depends on whether bSingleThreadUsage has + * been set of not in CPLVirtualMemNew() and/or the implementation. + * + * On Linux, this will always return TRUE if bSingleThreadUsage = FALSE. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return TRUE if this memory mapping can be accessed safely from concurrent + * threads. + * + * @since GDAL 2.0 + */ +int CPL_DLL CPLVirtualMemIsAccessThreadSafe(CPLVirtualMem* ctxt); + +/** Declare that a thread will access a virtual memory mapping. + * + * This function must be called by a thread that wants to access the + * content of a virtual memory mapping, except if the virtual memory mapping has + * been created with bSingleThreadUsage = TRUE. + * + * This function must be paired with CPLVirtualMemUnDeclareThread(). + * + * @param ctxt context returned by CPLVirtualMemNew(). + * + * @since GDAL 2.0 + */ +void CPL_DLL CPLVirtualMemDeclareThread(CPLVirtualMem* ctxt); + +/** Declare that a thread will stop accessing a virtual memory mapping. + * + * This function must be called by a thread that will no longer access the + * content of a virtual memory mapping, except if the virtual memory mapping has + * been created with bSingleThreadUsage = TRUE. + * + * This function must be paired with CPLVirtualMemDeclareThread(). + * + * @param ctxt context returned by CPLVirtualMemNew(). + * + * @since GDAL 2.0 + */ +void CPL_DLL CPLVirtualMemUnDeclareThread(CPLVirtualMem* ctxt); + +/** Make sure that a region of virtual memory will be realized. + * + * Calling this function is not required, but might be usefull when debugging + * a process with tools like gdb or valgrind that do not naturally like + * segmentation fault signals. + * + * It is also needed when wanting to provide part of virtual memory mapping + * to a system call such as read() or write(). If read() or write() is called + * on a memory region not yet realized, the call will fail with EFAULT. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @param pAddr the memory region to pin. + * @param nSize the size of the memory region. + * @param bWriteOp set to TRUE if the memory are will be accessed in write mode. + * + * @since GDAL 2.0 + */ +void CPL_DLL CPLVirtualMemPin(CPLVirtualMem* ctxt, + void* pAddr, size_t nSize, int bWriteOp); + +/** Cleanup any resource and handlers related to virtual memory. + * + * This function must be called after the last CPLVirtualMem object has + * been freed. + * + * @since GDAL 2.0 + */ +void CPL_DLL CPLVirtualMemManagerTerminate(void); + + +CPL_C_END + +#endif /* _CPL_VIRTUAL_MEM_INCLUDED */ diff --git a/cpl/cpl_vsi.h b/cpl/cpl_vsi.h index 672c066..9c3e04a 100644 --- a/cpl/cpl_vsi.h +++ b/cpl/cpl_vsi.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_vsi.h 18725 2010-02-04 21:02:19Z rouault $ + * $Id: cpl_vsi.h 27426 2014-05-30 21:13:32Z rouault $ * * Project: CPL - Common Portability Library * Author: Frank Warmerdam, warmerdam@pobox.com @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -78,7 +79,7 @@ CPL_C_START /* API. */ /* ==================================================================== */ -FILE CPL_DLL * VSIFOpen( const char *, const char * ); +FILE CPL_DLL * VSIFOpen( const char *, const char * ) CPL_WARN_UNUSED_RESULT; int CPL_DLL VSIFClose( FILE * ); int CPL_DLL VSIFSeek( FILE *, long, int ); long CPL_DLL VSIFTell( FILE * ); @@ -124,17 +125,36 @@ int CPL_DLL VSIStat( const char *, VSIStatBuf * ); /* ==================================================================== */ typedef GUIntBig vsi_l_offset; -FILE CPL_DLL * VSIFOpenL( const char *, const char * ); -int CPL_DLL VSIFCloseL( FILE * ); -int CPL_DLL VSIFSeekL( FILE *, vsi_l_offset, int ); -vsi_l_offset CPL_DLL VSIFTellL( FILE * ); -void CPL_DLL VSIRewindL( FILE * ); -size_t CPL_DLL VSIFReadL( void *, size_t, size_t, FILE * ); -size_t CPL_DLL VSIFWriteL( const void *, size_t, size_t, FILE * ); -int CPL_DLL VSIFEofL( FILE * ); -int CPL_DLL VSIFFlushL( FILE * ); -int CPL_DLL VSIFPrintfL( FILE *, const char *, ... ) CPL_PRINT_FUNC_FORMAT(2, 3); -int CPL_DLL VSIFPutcL( int, FILE * ); +/* Make VSIL_STRICT_ENFORCE active in DEBUG builds */ +#ifdef DEBUG +#define VSIL_STRICT_ENFORCE +#endif + +#ifdef VSIL_STRICT_ENFORCE +typedef struct _VSILFILE VSILFILE; +#else +typedef FILE VSILFILE; +#endif + +VSILFILE CPL_DLL * VSIFOpenL( const char *, const char * ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL VSIFCloseL( VSILFILE * ); +int CPL_DLL VSIFSeekL( VSILFILE *, vsi_l_offset, int ); +vsi_l_offset CPL_DLL VSIFTellL( VSILFILE * ); +void CPL_DLL VSIRewindL( VSILFILE * ); +size_t CPL_DLL VSIFReadL( void *, size_t, size_t, VSILFILE * ); +int CPL_DLL VSIFReadMultiRangeL( int nRanges, void ** ppData, const vsi_l_offset* panOffsets, const size_t* panSizes, VSILFILE * ); +size_t CPL_DLL VSIFWriteL( const void *, size_t, size_t, VSILFILE * ); +int CPL_DLL VSIFEofL( VSILFILE * ); +int CPL_DLL VSIFTruncateL( VSILFILE *, vsi_l_offset ); +int CPL_DLL VSIFFlushL( VSILFILE * ); +int CPL_DLL VSIFPrintfL( VSILFILE *, const char *, ... ) CPL_PRINT_FUNC_FORMAT(2, 3); +int CPL_DLL VSIFPutcL( int, VSILFILE * ); + +int CPL_DLL VSIIngestFile( VSILFILE* fp, + const char* pszFilename, + GByte** ppabyRet, + vsi_l_offset* pnSize, + GIntBig nMaxSize ); #if defined(VSI_STAT64_T) typedef struct VSI_STAT64_T VSIStatBufL; @@ -144,15 +164,25 @@ typedef struct VSI_STAT64_T VSIStatBufL; int CPL_DLL VSIStatL( const char *, VSIStatBufL * ); +#define VSI_STAT_EXISTS_FLAG 0x1 +#define VSI_STAT_NATURE_FLAG 0x2 +#define VSI_STAT_SIZE_FLAG 0x4 + +int CPL_DLL VSIStatExL( const char * pszFilename, VSIStatBufL * psStatBuf, int nFlags ); + +int CPL_DLL VSIIsCaseSensitiveFS( const char * pszFilename ); + +void CPL_DLL *VSIFGetNativeFileDescriptorL( VSILFILE* ); + /* ==================================================================== */ /* Memory allocation */ /* ==================================================================== */ -void CPL_DLL *VSICalloc( size_t, size_t ); -void CPL_DLL *VSIMalloc( size_t ); +void CPL_DLL *VSICalloc( size_t, size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL *VSIMalloc( size_t ) CPL_WARN_UNUSED_RESULT; void CPL_DLL VSIFree( void * ); -void CPL_DLL *VSIRealloc( void *, size_t ); -char CPL_DLL *VSIStrdup( const char * ); +void CPL_DLL *VSIRealloc( void *, size_t ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *VSIStrdup( const char * ) CPL_WARN_UNUSED_RESULT; /** VSIMalloc2 allocates (nSize1 * nSize2) bytes. @@ -161,7 +191,7 @@ char CPL_DLL *VSIStrdup( const char * ); If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned. CPLFree() or VSIFree() can be used to free memory allocated by this function. */ -void CPL_DLL *VSIMalloc2( size_t nSize1, size_t nSize2 ); +void CPL_DLL *VSIMalloc2( size_t nSize1, size_t nSize2 ) CPL_WARN_UNUSED_RESULT; /** VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes. @@ -170,7 +200,7 @@ void CPL_DLL *VSIMalloc2( size_t nSize1, size_t nSize2 ); If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be returned. CPLFree() or VSIFree() can be used to free memory allocated by this function. */ -void CPL_DLL *VSIMalloc3( size_t nSize1, size_t nSize2, size_t nSize3 ); +void CPL_DLL *VSIMalloc3( size_t nSize1, size_t nSize2, size_t nSize3 ) CPL_WARN_UNUSED_RESULT; /* ==================================================================== */ @@ -179,6 +209,7 @@ void CPL_DLL *VSIMalloc3( size_t nSize1, size_t nSize2, size_t nSize3 ); #define CPLReadDir VSIReadDir char CPL_DLL **VSIReadDir( const char * ); +char CPL_DLL **VSIReadDirRecursive( const char *pszPath ); int CPL_DLL VSIMkdir( const char * pathname, long mode ); int CPL_DLL VSIRmdir( const char * pathname ); int CPL_DLL VSIUnlink( const char * pathname ); @@ -191,12 +222,17 @@ char CPL_DLL *VSIStrerror( int ); void CPL_DLL VSIInstallMemFileHandler(void); void CPL_DLL VSIInstallLargeFileHandler(void); void CPL_DLL VSIInstallSubFileHandler(void); +void VSIInstallCurlFileHandler(void); +void VSIInstallCurlStreamingFileHandler(void); void VSIInstallGZipFileHandler(void); /* No reason to export that */ void VSIInstallZipFileHandler(void); /* No reason to export that */ +void VSIInstallStdinHandler(void); /* No reason to export that */ void VSIInstallStdoutHandler(void); /* No reason to export that */ +void CPL_DLL VSIInstallSparseFileHandler(void); +void VSIInstallTarFileHandler(void); /* No reason to export that */ void CPL_DLL VSICleanupFileManager(void); -FILE CPL_DLL *VSIFileFromMemBuffer( const char *pszFilename, +VSILFILE CPL_DLL *VSIFileFromMemBuffer( const char *pszFilename, GByte *pabyData, vsi_l_offset nDataLength, int bTakeOwnership ); @@ -204,6 +240,9 @@ GByte CPL_DLL *VSIGetMemFileBuffer( const char *pszFilename, vsi_l_offset *pnDataLength, int bUnlinkAndSeize ); +typedef size_t (*VSIWriteFunction)(const void* ptr, size_t size, size_t nmemb, FILE* stream); +void CPL_DLL VSIStdoutSetRedirection( VSIWriteFunction pFct, FILE* stream ); + /* ==================================================================== */ /* Time quering. */ /* ==================================================================== */ diff --git a/cpl/cpl_vsi_mem.cpp b/cpl/cpl_vsi_mem.cpp index e18df18..f12093d 100644 --- a/cpl/cpl_vsi_mem.cpp +++ b/cpl/cpl_vsi_mem.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_vsi_mem.cpp 18253 2009-12-10 19:53:21Z rouault $ + * $Id: cpl_vsi_mem.cpp 27157 2014-04-12 12:59:41Z rouault $ * * Project: VSI Virtual File System * Purpose: Implementation of Memory Buffer virtual IO functions. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -42,7 +43,7 @@ #endif -CPL_CVSID("$Id: cpl_vsi_mem.cpp 18253 2009-12-10 19:53:21Z rouault $"); +CPL_CVSID("$Id: cpl_vsi_mem.cpp 27157 2014-04-12 12:59:41Z rouault $"); /* ** Notes on Multithreading: @@ -90,6 +91,8 @@ class VSIMemFile vsi_l_offset nLength; vsi_l_offset nAllocLength; + int bEOF; + VSIMemFile(); virtual ~VSIMemFile(); @@ -108,6 +111,7 @@ class VSIMemHandle : public VSIVirtualHandle VSIMemFile *poFile; vsi_l_offset nOffset; int bUpdate; + int bEOF; virtual int Seek( vsi_l_offset nOffset, int nWhence ); virtual vsi_l_offset Tell(); @@ -115,6 +119,7 @@ class VSIMemHandle : public VSIVirtualHandle virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); virtual int Eof(); virtual int Close(); + virtual int Truncate( vsi_l_offset nNewSize ); }; /************************************************************************/ @@ -134,11 +139,13 @@ class VSIMemFilesystemHandler : public VSIFilesystemHandler virtual VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess); - virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf ); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); virtual int Unlink( const char *pszFilename ); virtual int Mkdir( const char *pszDirname, long nMode ); virtual int Rmdir( const char *pszDirname ); virtual char **ReadDir( const char *pszDirname ); + virtual int Rename( const char *oldpath, const char *newpath ); + static void NormalizePath( CPLString & ); }; @@ -161,6 +168,7 @@ VSIMemFile::VSIMemFile() pabyData = NULL; nLength = 0; nAllocLength = 0; + bEOF = FALSE; } /************************************************************************/ @@ -206,10 +214,16 @@ bool VSIMemFile::SetLength( vsi_l_offset nNewLength ) pabyNewData = (GByte *) VSIRealloc(pabyData, (size_t)nNewAlloc); if( pabyNewData == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot extend in-memory file to " CPL_FRMT_GUIB " bytes due to out-of-memory situation", + nNewAlloc); return false; + } /* Clear the new allocated part of the buffer */ - memset(pabyNewData + nAllocLength, 0, nNewAlloc - nAllocLength); + memset(pabyNewData + nAllocLength, 0, + (size_t) (nNewAlloc - nAllocLength)); pabyData = pabyNewData; nAllocLength = nNewAlloc; @@ -261,20 +275,16 @@ int VSIMemHandle::Seek( vsi_l_offset nOffset, int nWhence ) return -1; } - if( this->nOffset < 0 ) - { - this->nOffset = 0; - return -1; - } - + bEOF = FALSE; + if( this->nOffset > poFile->nLength ) { if( !bUpdate ) // Read-only files cannot be extended by seek. { CPLDebug( "VSIMemHandle", - "Attempt to extend read-only file '%s' to length %d from %d, .", + "Attempt to extend read-only file '%s' to length " CPL_FRMT_GUIB " from " CPL_FRMT_GUIB ".", poFile->osFilename.c_str(), - (int) this->nOffset, (int) poFile->nLength ); + this->nOffset, poFile->nLength ); this->nOffset = poFile->nLength; errno = EACCES; @@ -312,8 +322,15 @@ size_t VSIMemHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) if( nBytesToRead + nOffset > poFile->nLength ) { + if (poFile->nLength < nOffset) + { + bEOF = TRUE; + return 0; + } + nBytesToRead = (size_t)(poFile->nLength - nOffset); nCount = nBytesToRead / nSize; + bEOF = TRUE; } memcpy( pBuffer, poFile->pabyData + nOffset, (size_t)nBytesToRead ); @@ -357,7 +374,25 @@ size_t VSIMemHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) int VSIMemHandle::Eof() { - return nOffset == poFile->nLength; + return bEOF; +} + +/************************************************************************/ +/* Truncate() */ +/************************************************************************/ + +int VSIMemHandle::Truncate( vsi_l_offset nNewSize ) +{ + if( !bUpdate ) + { + errno = EACCES; + return -1; + } + + if (poFile->SetLength( nNewSize )) + return 0; + else + return -1; } /************************************************************************/ @@ -385,7 +420,7 @@ VSIMemFilesystemHandler::~VSIMemFilesystemHandler() { std::map::const_iterator iter; - for( iter = oFileList.begin(); iter != oFileList.end(); iter++ ) + for( iter = oFileList.begin(); iter != oFileList.end(); ++iter ) { iter->second->nRefCount--; delete iter->second; @@ -450,6 +485,7 @@ VSIMemFilesystemHandler::Open( const char *pszFilename, poHandle->poFile = poFile; poHandle->nOffset = 0; + poHandle->bEOF = FALSE; if( strstr(pszAccess,"w") || strstr(pszAccess,"+") || strstr(pszAccess,"a") ) poHandle->bUpdate = TRUE; @@ -469,14 +505,26 @@ VSIMemFilesystemHandler::Open( const char *pszFilename, /************************************************************************/ int VSIMemFilesystemHandler::Stat( const char * pszFilename, - VSIStatBufL * pStatBuf ) + VSIStatBufL * pStatBuf, + int nFlags ) { + (void) nFlags; + CPLMutexHolder oHolder( &hMutex ); CPLString osFilename = pszFilename; NormalizePath( osFilename ); + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + if ( osFilename == "/vsimem/" ) + { + pStatBuf->st_size = 0; + pStatBuf->st_mode = S_IFDIR; + return 0; + } + if( oFileList.find(osFilename) == oFileList.end() ) { errno = ENOENT; @@ -494,7 +542,7 @@ int VSIMemFilesystemHandler::Stat( const char * pszFilename, } else { - pStatBuf->st_size = (long)poFile->nLength; + pStatBuf->st_size = poFile->nLength; pStatBuf->st_mode = S_IFREG; } @@ -541,6 +589,8 @@ int VSIMemFilesystemHandler::Mkdir( const char * pszPathname, long nMode ) { + (void) nMode; + CPLMutexHolder oHolder( &hMutex ); CPLString osPathname = pszPathname; @@ -600,7 +650,7 @@ char **VSIMemFilesystemHandler::ReadDir( const char *pszPath ) int nItems=0; int nAllocatedItems=0; - for( iter = oFileList.begin(); iter != oFileList.end(); iter++ ) + for( iter = oFileList.begin(); iter != oFileList.end(); ++iter ) { const char *pszFilePath = iter->second->osFilename.c_str(); if( EQUALN(osPath,pszFilePath,nPathLen) @@ -630,7 +680,46 @@ char **VSIMemFilesystemHandler::ReadDir( const char *pszPath ) } /************************************************************************/ -/* NormalizePath() */ +/* Rename() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Rename( const char *pszOldPath, + const char *pszNewPath ) + +{ + CPLMutexHolder oHolder( &hMutex ); + + CPLString osOldPath = pszOldPath; + CPLString osNewPath = pszNewPath; + + NormalizePath( osOldPath ); + NormalizePath( osNewPath ); + + if ( osOldPath.compare(osNewPath) == 0 ) + return 0; + + if( oFileList.find(osOldPath) == oFileList.end() ) + { + errno = ENOENT; + return -1; + } + else + { + VSIMemFile* poFile = oFileList[osOldPath]; + + oFileList.erase( oFileList.find(osOldPath) ); + + Unlink(osNewPath); + + oFileList[osNewPath] = poFile; + poFile->osFilename = osNewPath; + + return 0; + } +} + +/************************************************************************/ +/* NormalizePath() */ /************************************************************************/ void VSIMemFilesystemHandler::NormalizePath( CPLString &oPath ) @@ -733,7 +822,7 @@ void VSIInstallMemFileHandler() * @return open file handle on created file (see VSIFOpenL()). */ -FILE *VSIFileFromMemBuffer( const char *pszFilename, +VSILFILE *VSIFileFromMemBuffer( const char *pszFilename, GByte *pabyData, vsi_l_offset nDataLength, int bTakeOwnership ) @@ -746,9 +835,15 @@ FILE *VSIFileFromMemBuffer( const char *pszFilename, VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *) VSIFileManager::GetHandler("/vsimem/"); + if (pszFilename == NULL) + return NULL; + + CPLString osFilename = pszFilename; + VSIMemFilesystemHandler::NormalizePath( osFilename ); + VSIMemFile *poFile = new VSIMemFile; - poFile->osFilename = pszFilename; + poFile->osFilename = osFilename; poFile->bOwnData = bTakeOwnership; poFile->pabyData = pabyData; poFile->nLength = nDataLength; @@ -756,11 +851,12 @@ FILE *VSIFileFromMemBuffer( const char *pszFilename, { CPLMutexHolder oHolder( &poHandler->hMutex ); + poHandler->Unlink(osFilename); poHandler->oFileList[poFile->osFilename] = poFile; poFile->nRefCount++; } - return (FILE *) poHandler->Open( pszFilename, "r+" ); + return (VSILFILE *) poHandler->Open( osFilename, "r+" ); } /************************************************************************/ @@ -790,12 +886,18 @@ GByte *VSIGetMemFileBuffer( const char *pszFilename, VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *) VSIFileManager::GetHandler("/vsimem/"); + if (pszFilename == NULL) + return NULL; + + CPLString osFilename = pszFilename; + VSIMemFilesystemHandler::NormalizePath( osFilename ); + CPLMutexHolder oHolder( &poHandler->hMutex ); - if( poHandler->oFileList.find(pszFilename) == poHandler->oFileList.end() ) + if( poHandler->oFileList.find(osFilename) == poHandler->oFileList.end() ) return NULL; - VSIMemFile *poFile = poHandler->oFileList[pszFilename]; + VSIMemFile *poFile = poHandler->oFileList[osFilename]; GByte *pabyData; pabyData = poFile->pabyData; @@ -810,7 +912,7 @@ GByte *VSIGetMemFileBuffer( const char *pszFilename, else poFile->bOwnData = FALSE; - poHandler->oFileList.erase( poHandler->oFileList.find(pszFilename) ); + poHandler->oFileList.erase( poHandler->oFileList.find(osFilename) ); --(poFile->nRefCount); delete poFile; } diff --git a/cpl/cpl_vsi_virtual.h b/cpl/cpl_vsi_virtual.h index 3ec9307..85bdb60 100644 --- a/cpl/cpl_vsi_virtual.h +++ b/cpl/cpl_vsi_virtual.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_vsi_virtual.h 15257 2008-08-30 21:15:54Z mloskot $ + * $Id: cpl_vsi_virtual.h 27452 2014-06-12 16:01:12Z goatbar $ * * Project: VSI Virtual File System * Purpose: Declarations for classes related to the virtual filesystem. @@ -10,6 +10,7 @@ * ****************************************************************************** * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -34,6 +35,7 @@ #define CPL_VSI_VIRTUAL_H_INCLUDED #include "cpl_vsi.h" +#include "cpl_string.h" #if defined(WIN32CE) # include "cpl_wince.h" @@ -54,10 +56,13 @@ class CPL_DLL VSIVirtualHandle { virtual int Seek( vsi_l_offset nOffset, int nWhence ) = 0; virtual vsi_l_offset Tell() = 0; virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ) = 0; + virtual int ReadMultiRange( int nRanges, void ** ppData, const vsi_l_offset* panOffsets, const size_t* panSizes ); virtual size_t Write( const void *pBuffer, size_t nSize,size_t nMemb)=0; virtual int Eof() = 0; virtual int Flush() {return 0;} virtual int Close() = 0; + virtual int Truncate( vsi_l_offset nNewSize ) { return -1; } + virtual void *GetNativeFileDescriptor() { return NULL; } virtual ~VSIVirtualHandle() { } }; @@ -73,17 +78,19 @@ class CPL_DLL VSIFilesystemHandler { virtual VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess) = 0; - virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf) = 0; + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags) = 0; virtual int Unlink( const char *pszFilename ) - { errno=ENOENT; return -1; } + { (void) pszFilename; errno=ENOENT; return -1; } virtual int Mkdir( const char *pszDirname, long nMode ) - { errno=ENOENT; return -1; } + {(void)pszDirname; (void)nMode; errno=ENOENT; return -1;} virtual int Rmdir( const char *pszDirname ) - { errno=ENOENT; return -1; } + { (void) pszDirname; errno=ENOENT; return -1; } virtual char **ReadDir( const char *pszDirname ) - { return NULL; } + { (void) pszDirname; return NULL; } virtual int Rename( const char *oldpath, const char *newpath ) - { errno=ENOENT; return -1; } + { (void) oldpath; (void)newpath; errno=ENOENT; return -1; } + virtual int IsCaseSensitive( const char* pszFilename ) + { (void) pszFilename; return TRUE; } }; /************************************************************************/ @@ -106,7 +113,84 @@ class CPL_DLL VSIFileManager static VSIFilesystemHandler *GetHandler( const char * ); static void InstallHandler( const std::string& osPrefix, VSIFilesystemHandler * ); - static void RemoveHandler( const std::string& osPrefix ); + /* RemoveHandler is never defined. */ + /* static void RemoveHandler( const std::string& osPrefix ); */ }; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIArchiveFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIArchiveEntryFileOffset +{ + public: + virtual ~VSIArchiveEntryFileOffset(); +}; + +typedef struct +{ + char *fileName; + vsi_l_offset uncompressed_size; + VSIArchiveEntryFileOffset* file_pos; + int bIsDir; + GIntBig nModifiedTime; +} VSIArchiveEntry; + +typedef struct +{ + int nEntries; + VSIArchiveEntry* entries; +} VSIArchiveContent; + +class VSIArchiveReader +{ + public: + virtual ~VSIArchiveReader(); + + virtual int GotoFirstFile() = 0; + virtual int GotoNextFile() = 0; + virtual VSIArchiveEntryFileOffset* GetFileOffset() = 0; + virtual GUIntBig GetFileSize() = 0; + virtual CPLString GetFileName() = 0; + virtual GIntBig GetModifiedTime() = 0; + virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset) = 0; +}; + +class VSIArchiveFilesystemHandler : public VSIFilesystemHandler +{ +protected: + void* hMutex; + /* We use a cache that contains the list of files containes in a VSIArchive file as */ + /* unarchive.c is quite inefficient in listing them. This speeds up access to VSIArchive files */ + /* containing ~1000 files like a CADRG product */ + std::map oFileList; + + virtual const char* GetPrefix() = 0; + virtual std::vector GetExtensions() = 0; + virtual VSIArchiveReader* CreateReader(const char* pszArchiveFileName) = 0; + +public: + VSIArchiveFilesystemHandler(); + virtual ~VSIArchiveFilesystemHandler(); + + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Rename( const char *oldpath, const char *newpath ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + + virtual const VSIArchiveContent* GetContentOfArchive(const char* archiveFilename, VSIArchiveReader* poReader = NULL); + virtual char* SplitFilename(const char *pszFilename, CPLString &osFileInArchive, int bCheckMainFileExists); + virtual VSIArchiveReader* OpenArchiveFile(const char* archiveFilename, const char* fileInArchiveName); + virtual int FindFileInArchive(const char* archiveFilename, const char* fileInArchiveName, const VSIArchiveEntry** archiveEntry); +}; + +VSIVirtualHandle* VSICreateBufferedReaderHandle(VSIVirtualHandle* poBaseHandle); +VSIVirtualHandle* VSICreateCachedFile( VSIVirtualHandle* poBaseHandle, size_t nChunkSize = 32768, size_t nCacheSize = 0 ); +VSIVirtualHandle* VSICreateGZipWritable( VSIVirtualHandle* poBaseHandle, int bRegularZLibIn, int bAutoCloseBaseHandle ); + #endif /* ndef CPL_VSI_VIRTUAL_H_INCLUDED */ diff --git a/cpl/cpl_vsil.cpp b/cpl/cpl_vsil.cpp index 936c8cd..4d64608 100644 --- a/cpl/cpl_vsil.cpp +++ b/cpl/cpl_vsil.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_vsil.cpp 18725 2010-02-04 21:02:19Z rouault $ + * $Id: cpl_vsil.cpp 27468 2014-06-26 17:22:38Z goatbar $ * * Project: VSI Virtual File System * Purpose: Implementation VSI*L File API and other file system access @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,10 +30,11 @@ ****************************************************************************/ #include "cpl_vsi_virtual.h" +#include "cpl_multiproc.h" #include "cpl_string.h" #include -CPL_CVSID("$Id: cpl_vsil.cpp 18725 2010-02-04 21:02:19Z rouault $"); +CPL_CVSID("$Id: cpl_vsil.cpp 27468 2014-06-26 17:22:38Z goatbar $"); /************************************************************************/ /* VSIReadDir() */ @@ -52,9 +54,10 @@ CPL_CVSID("$Id: cpl_vsil.cpp 18725 2010-02-04 21:02:19Z rouault $"); * This function used to be known as CPLReadDir(), but the old name is now * deprecated. * - * @param pszPath the relative, or absolute path of a directory to read. + * @param pszPath the relative, or absolute path of a directory to read. + * UTF-8 encoded. * @return The list of entries in the directory, or NULL if the directory - * doesn't exist. + * doesn't exist. Filenames are returned in UTF-8 encoding. */ char **VSIReadDir(const char *pszPath) @@ -65,6 +68,164 @@ char **VSIReadDir(const char *pszPath) return poFSHandler->ReadDir( pszPath ); } +/************************************************************************/ +/* VSIReadRecursive() */ +/************************************************************************/ + +typedef struct +{ + char **papszFiles; + int nCount; + int i; + char* pszPath; + char* pszDisplayedPath; +} VSIReadDirRecursiveTask; + +/** + * \brief Read names in a directory recursively. + * + * This function abstracts access to directory contents and subdirectories. + * It returns a list of strings containing the names of files and directories + * in this directory and all subdirectories. The resulting string list becomes + * the responsibility of the application and should be freed with CSLDestroy() + * when no longer needed. + * + * Note that no error is issued via CPLError() if the directory path is + * invalid, though NULL is returned. + * + * @param pszPathIn the relative, or absolute path of a directory to read. + * UTF-8 encoded. + * + * @return The list of entries in the directory and subdirectories + * or NULL if the directory doesn't exist. Filenames are returned in UTF-8 + * encoding. + * @since GDAL 1.10.0 + * + */ + +char **VSIReadDirRecursive( const char *pszPathIn ) +{ + CPLStringList oFiles = NULL; + char **papszFiles = NULL; + VSIStatBufL psStatBuf; + CPLString osTemp1, osTemp2; + int i = 0; + int nCount = -1; + + std::vector aoStack; + char* pszPath = CPLStrdup(pszPathIn); + char* pszDisplayedPath = NULL; + + while(TRUE) + { + if( nCount < 0 ) + { + // get listing + papszFiles = VSIReadDir( pszPath ); + + // get files and directories inside listing + nCount = papszFiles ? CSLCount( papszFiles ) : 0; + i = 0; + } + + for ( ; i < nCount; i++ ) + { + // Do not recurse up the tree. + if (EQUAL(".", papszFiles[i]) || EQUAL("..", papszFiles[i])) + continue; + + // build complete file name for stat + osTemp1.clear(); + osTemp1.append( pszPath ); + osTemp1.append( "/" ); + osTemp1.append( papszFiles[i] ); + + // if is file, add it + if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) != 0 ) + continue; + + if( VSI_ISREG( psStatBuf.st_mode ) ) + { + if( pszDisplayedPath ) + { + osTemp1.clear(); + osTemp1.append( pszDisplayedPath ); + osTemp1.append( "/" ); + osTemp1.append( papszFiles[i] ); + oFiles.AddString( osTemp1 ); + } + else + oFiles.AddString( papszFiles[i] ); + } + else if ( VSI_ISDIR( psStatBuf.st_mode ) ) + { + // add directory entry + osTemp2.clear(); + if( pszDisplayedPath ) + { + osTemp2.append( pszDisplayedPath ); + osTemp2.append( "/" ); + } + osTemp2.append( papszFiles[i] ); + osTemp2.append( "/" ); + oFiles.AddString( osTemp2.c_str() ); + + VSIReadDirRecursiveTask sTask; + sTask.papszFiles = papszFiles; + sTask.nCount = nCount; + sTask.i = i; + sTask.pszPath = CPLStrdup(pszPath); + sTask.pszDisplayedPath = pszDisplayedPath ? CPLStrdup(pszDisplayedPath) : NULL; + aoStack.push_back(sTask); + + CPLFree(pszPath); + pszPath = CPLStrdup( osTemp1.c_str() ); + + char* pszDisplayedPathNew; + if( pszDisplayedPath ) + pszDisplayedPathNew = CPLStrdup( CPLSPrintf("%s/%s", pszDisplayedPath, papszFiles[i]) ); + else + pszDisplayedPathNew = CPLStrdup( papszFiles[i] ); + CPLFree(pszDisplayedPath); + pszDisplayedPath = pszDisplayedPathNew; + + i = 0; + papszFiles = NULL; + nCount = -1; + + break; + } + } + + if( nCount >= 0 ) + { + CSLDestroy( papszFiles ); + + if( aoStack.size() ) + { + int iLast = (int)aoStack.size() - 1; + CPLFree(pszPath); + CPLFree(pszDisplayedPath); + nCount = aoStack[iLast].nCount; + papszFiles = aoStack[iLast].papszFiles; + i = aoStack[iLast].i + 1; + pszPath = aoStack[iLast].pszPath; + pszDisplayedPath = aoStack[iLast].pszDisplayedPath; + + aoStack.resize(iLast); + } + else + break; + } + } + + CPLFree(pszPath); + CPLFree(pszDisplayedPath); + + return oFiles.StealList(); +} + + /************************************************************************/ /* CPLReadDir() */ /* */ @@ -96,7 +257,7 @@ char **CPLReadDir( const char *pszPath ) * * Analog of the POSIX mkdir() function. * - * @param pszPathname the path to the directory to create. + * @param pszPathname the path to the directory to create. UTF-8 encoded. * @param mode the permissions mode. * * @return 0 on success or -1 on an error. @@ -125,7 +286,7 @@ int VSIMkdir( const char *pszPathname, long mode ) * * Analog of the POSIX unlink() function. * - * @param pszFilename the path of the file to be deleted. + * @param pszFilename the path of the file to be deleted. UTF-8 encoded. * * @return 0 on success or -1 on an error. */ @@ -155,8 +316,8 @@ int VSIUnlink( const char * pszFilename ) * * Analog of the POSIX rename() function. * - * @param oldpath the name of the file to be renamed. - * @param newpath the name the file should be given. + * @param oldpath the name of the file to be renamed. UTF-8 encoded. + * @param newpath the name the file should be given. UTF-8 encoded. * * @return 0 on success or -1 on an error. */ @@ -185,7 +346,7 @@ int VSIRename( const char * oldpath, const char * newpath ) * * Analog of the POSIX rmdir() function. * - * @param pszDirname the path of the directory to be deleted. + * @param pszDirname the path of the directory to be deleted. UTF-8 encoded. * * @return 0 on success or -1 on an error. */ @@ -208,7 +369,7 @@ int VSIRmdir( const char * pszDirname ) * * Fetches status information about a filesystem object (file, directory, etc). * The returned information is placed in the VSIStatBufL structure. For - * portability only the st_size (size in bytes), and st_mode (file type). + * portability, only use the st_size (size in bytes) and st_mode (file type). * This method is similar to VSIStat(), but will work on large files on * systems where this requires special calls. * @@ -217,7 +378,7 @@ int VSIRmdir( const char * pszDirname ) * * Analog of the POSIX stat() function. * - * @param pszFilename the path of the filesystem object to be queried. + * @param pszFilename the path of the filesystem object to be queried. UTF-8 encoded. * @param psStatBuf the structure to load with information. * * @return 0 on success or -1 on an error. @@ -225,6 +386,43 @@ int VSIRmdir( const char * pszDirname ) int VSIStatL( const char * pszFilename, VSIStatBufL *psStatBuf ) +{ + return VSIStatExL(pszFilename, psStatBuf, 0); +} + + +/************************************************************************/ +/* VSIStatExL() */ +/************************************************************************/ + +/** + * \brief Get filesystem object info. + * + * Fetches status information about a filesystem object (file, directory, etc). + * The returned information is placed in the VSIStatBufL structure. For + * portability, only use the st_size (size in bytes) and st_mode (file type). + * This method is similar to VSIStat(), but will work on large files on + * systems where this requires special calls. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX stat() function, with an extra parameter to specify + * which information is needed, which offers a potential for speed optimizations + * on specialized and potentially slow virtual filesystem objects (/vsigzip/, /vsicurl/) + * + * @param pszFilename the path of the filesystem object to be queried. UTF-8 encoded. + * @param psStatBuf the structure to load with information. + * @param nFlags 0 to get all information, or VSI_STAT_EXISTS_FLAG, VSI_STAT_NATURE_FLAG or + * VSI_STAT_SIZE_FLAG, or a combination of those to get partial info. + * + * @return 0 on success or -1 on an error. + * + * @since GDAL 1.8.0 + */ + +int VSIStatExL( const char * pszFilename, VSIStatBufL *psStatBuf, int nFlags ) + { char szAltPath[4]; /* enable to work on "C:" as if it were "C:\" */ @@ -238,10 +436,43 @@ int VSIStatL( const char * pszFilename, VSIStatBufL *psStatBuf ) pszFilename = szAltPath; } - VSIFilesystemHandler *poFSHandler = + VSIFilesystemHandler *poFSHandler = VSIFileManager::GetHandler( pszFilename ); - return poFSHandler->Stat( pszFilename, psStatBuf ); + if (nFlags == 0) + nFlags = VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG | VSI_STAT_SIZE_FLAG; + + return poFSHandler->Stat( pszFilename, psStatBuf, nFlags ); +} + +/************************************************************************/ +/* VSIIsCaseSensitiveFS() */ +/************************************************************************/ + +/** + * \brief Returns if the filenames of the filesystem are case sensitive. + * + * This method retrieves to which filesystem belongs the passed filename + * and return TRUE if the filenames of that filesystem are case sensitive. + * + * Currently, this will return FALSE only for Windows real filenames. Other + * VSI virtual filesystems are case sensitive. + * + * This methods avoid ugly #ifndef WIN32 / #endif code, that is wrong when + * dealing with virtual filenames. + * + * @param pszFilename the path of the filesystem object to be tested. UTF-8 encoded. + * + * @return TRUE if the filenames of the filesystem are case sensitive. + * + * @since GDAL 1.8.0 + */ +int VSIIsCaseSensitiveFS( const char * pszFilename ) +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename ); + + return poFSHandler->IsCaseSensitive( pszFilename ); } /************************************************************************/ @@ -255,28 +486,32 @@ int VSIStatL( const char * pszFilename, VSIStatBufL *psStatBuf ) * than 2GB) should be supported. Binary access is always implied and * the "b" does not need to be included in the pszAccess string. * - * Note that the "FILE *" returned by this function is not really a - * standard C library FILE *, and cannot be used with any functions other - * than the "VSI*L" family of functions. They aren't "real" FILE objects. + * Note that the "VSILFILE *" returned since GDAL 1.8.0 by this function is + * *NOT* a standard C library FILE *, and cannot be used with any functions + * other than the "VSI*L" family of functions. They aren't "real" FILE objects. + * + * On windows it is possible to define the configuration option + * GDAL_FILE_IS_UTF8 to have pszFilename treated as being in the local + * encoding instead of UTF-8, retoring the pre-1.8.0 behavior of VSIFOpenL(). * * This method goes through the VSIFileHandler virtualization and may * work on unusual filesystems such as in memory. * * Analog of the POSIX fopen() function. * - * @param pszFilename the file to open. + * @param pszFilename the file to open. UTF-8 encoded. * @param pszAccess access requested (ie. "r", "r+", "w". * * @return NULL on failure, or the file handle. */ -FILE *VSIFOpenL( const char * pszFilename, const char * pszAccess ) +VSILFILE *VSIFOpenL( const char * pszFilename, const char * pszAccess ) { VSIFilesystemHandler *poFSHandler = VSIFileManager::GetHandler( pszFilename ); - FILE* fp = (FILE *) poFSHandler->Open( pszFilename, pszAccess ); + VSILFILE* fp = (VSILFILE *) poFSHandler->Open( pszFilename, pszAccess ); VSIDebug3( "VSIFOpenL(%s,%s) = %p", pszFilename, pszAccess, fp ); @@ -302,7 +537,7 @@ FILE *VSIFOpenL( const char * pszFilename, const char * pszAccess ) * @return 0 on success or -1 on failure. */ -int VSIFCloseL( FILE * fp ) +int VSIFCloseL( VSILFILE * fp ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -337,7 +572,7 @@ int VSIFCloseL( FILE * fp ) * @return 0 on success or -1 one failure. */ -int VSIFSeekL( FILE * fp, vsi_l_offset nOffset, int nWhence ) +int VSIFSeekL( VSILFILE * fp, vsi_l_offset nOffset, int nWhence ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -365,7 +600,7 @@ int VSIFSeekL( FILE * fp, vsi_l_offset nOffset, int nWhence ) * @return file offset in bytes. */ -vsi_l_offset VSIFTellL( FILE * fp ) +vsi_l_offset VSIFTellL( VSILFILE * fp ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -377,7 +612,7 @@ vsi_l_offset VSIFTellL( FILE * fp ) /* VSIRewindL() */ /************************************************************************/ -void VSIRewindL( FILE * fp ) +void VSIRewindL( VSILFILE * fp ) { VSIFSeekL( fp, 0, SEEK_SET ); @@ -403,7 +638,7 @@ void VSIRewindL( FILE * fp ) * @return 0 on success or -1 on error. */ -int VSIFFlushL( FILE * fp ) +int VSIFFlushL( VSILFILE * fp ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -435,7 +670,7 @@ int VSIFFlushL( FILE * fp ) * @return number of objects successfully read. */ -size_t VSIFReadL( void * pBuffer, size_t nSize, size_t nCount, FILE * fp ) +size_t VSIFReadL( void * pBuffer, size_t nSize, size_t nCount, VSILFILE * fp ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -443,6 +678,43 @@ size_t VSIFReadL( void * pBuffer, size_t nSize, size_t nCount, FILE * fp ) return poFileHandle->Read( pBuffer, nSize, nCount ); } + +/************************************************************************/ +/* VSIFReadMultiRangeL() */ +/************************************************************************/ + +/** + * \brief Read several ranges of bytes from file. + * + * Reads nRanges objects of panSizes[i] bytes from the indicated file at the + * offset panOffsets[i] into the buffer ppData[i]. + * + * Ranges must be sorted in ascending start offset, and must not overlap each + * other. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory or /vsicurl/. + * + * @param nRanges number of ranges to read. + * @param ppData array of nRanges buffer into which the data should be read + * (ppData[i] must be at list panSizes[i] bytes). + * @param panOffsets array of nRanges offsets at which the data should be read. + * @param panSizes array of nRanges sizes of objects to read (in bytes). + * @param fp file handle opened with VSIFOpenL(). + * + * @return 0 in case of success, -1 otherwise. + * @since GDAL 1.9.0 + */ + +int VSIFReadMultiRangeL( int nRanges, void ** ppData, + const vsi_l_offset* panOffsets, + const size_t* panSizes, VSILFILE * fp ) +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->ReadMultiRange( nRanges, ppData, panOffsets, panSizes ); +} + /************************************************************************/ /* VSIFWriteL() */ /************************************************************************/ @@ -467,7 +739,7 @@ size_t VSIFReadL( void * pBuffer, size_t nSize, size_t nCount, FILE * fp ) * @return number of objects successfully written. */ -size_t VSIFWriteL( const void *pBuffer, size_t nSize, size_t nCount, FILE *fp ) +size_t VSIFWriteL( const void *pBuffer, size_t nSize, size_t nCount, VSILFILE *fp ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -482,8 +754,9 @@ size_t VSIFWriteL( const void *pBuffer, size_t nSize, size_t nCount, FILE *fp ) /** * \brief Test for end of file. * - * Returns TRUE (non-zero) if the file read/write offset is currently at the - * end of the file. + * Returns TRUE (non-zero) if an end-of-file condition occured during the + * previous read operation. The end-of-file flag is cleared by a successfull + * VSIFSeekL() call. * * This method goes through the VSIFileHandler virtualization and may * work on unusual filesystems such as in memory. @@ -495,7 +768,7 @@ size_t VSIFWriteL( const void *pBuffer, size_t nSize, size_t nCount, FILE *fp ) * @return TRUE if at EOF else FALSE. */ -int VSIFEofL( FILE * fp ) +int VSIFEofL( VSILFILE * fp ) { VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; @@ -503,6 +776,33 @@ int VSIFEofL( FILE * fp ) return poFileHandle->Eof(); } +/************************************************************************/ +/* VSIFTruncateL() */ +/************************************************************************/ + +/** + * \brief Truncate/expand the file to the specified size + + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX ftruncate() call. + * + * @param fp file handle opened with VSIFOpenL(). + * @param nNewSize new size in bytes. + * + * @return 0 on success + * @since GDAL 1.9.0 + */ + +int VSIFTruncateL( VSILFILE * fp, vsi_l_offset nNewSize ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Truncate(nNewSize); +} + /************************************************************************/ /* VSIFPrintfL() */ /************************************************************************/ @@ -521,7 +821,7 @@ int VSIFEofL( FILE * fp ) * @return the number of bytes written or -1 on an error. */ -int VSIFPrintfL( FILE *fp, const char *pszFormat, ... ) +int VSIFPrintfL( VSILFILE *fp, const char *pszFormat, ... ) { va_list args; @@ -538,13 +838,207 @@ int VSIFPrintfL( FILE *fp, const char *pszFormat, ... ) /* VSIFPutcL() */ /************************************************************************/ -int VSIFPutcL( int nChar, FILE * fp ) +int VSIFPutcL( int nChar, VSILFILE * fp ) { unsigned char cChar = (unsigned char)nChar; return VSIFWriteL(&cChar, 1, 1, fp); } +/************************************************************************/ +/* VSIIngestFile() */ +/************************************************************************/ + +/** + * \brief Ingest a file into memory. + * + * Read the whole content of a file into a memory buffer. + * + * Either fp or pszFilename can be NULL, but not both at the same time. + * + * If fp is passed non-NULL, it is the responsibility of the caller to + * close it. + * + * If non-NULL, the returned buffer is guaranteed to be NUL-terminated. + * + * @param fp file handle opened with VSIFOpenL(). + * @param pszFilename filename. + * @param ppabyRet pointer to the target buffer. *ppabyRet must be freed with + * VSIFree() + * @param pnSize pointer to variable to store the file size. May be NULL. + * @param nMaxSize maximum size of file allowed. If no limit, set to a negative + * value. + * + * @return TRUE in case of success. + * + * @since GDAL 1.11 + */ + +int VSIIngestFile( VSILFILE* fp, + const char* pszFilename, + GByte** ppabyRet, + vsi_l_offset* pnSize, + GIntBig nMaxSize) +{ + vsi_l_offset nDataLen = 0; + int bFreeFP = FALSE; + + if( fp == NULL && pszFilename == NULL ) + return FALSE; + if( ppabyRet == NULL ) + return FALSE; + + *ppabyRet = NULL; + if( pnSize != NULL ) + *pnSize = 0; + + if( NULL == fp ) + { + fp = VSIFOpenL( pszFilename, "rb" ); + if( NULL == fp ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot open file '%s'", pszFilename ); + return FALSE; + } + bFreeFP = TRUE; + } + else + VSIFSeekL(fp, 0, SEEK_SET); + + if( pszFilename == NULL || + strcmp(pszFilename, "/vsistdin/") == 0 ) + { + vsi_l_offset nDataAlloc = 0; + VSIFSeekL( fp, 0, SEEK_SET ); + while(TRUE) + { + if( nDataLen + 8192 + 1 > nDataAlloc ) + { + nDataAlloc = (nDataAlloc * 4) / 3 + 8192 + 1; + if( nDataAlloc > (vsi_l_offset)(size_t)nDataAlloc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input file too large to be opened" ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + GByte* pabyNew = (GByte*)VSIRealloc(*ppabyRet, nDataAlloc); + if( pabyNew == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocated " CPL_FRMT_GIB " bytes", + nDataAlloc ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + *ppabyRet = pabyNew; + } + int nRead = (int)VSIFReadL( *ppabyRet + nDataLen, 1, 8192, fp ); + nDataLen += nRead; + + if ( nMaxSize >= 0 && nDataLen > (vsi_l_offset)nMaxSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input file too large to be opened" ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( pnSize != NULL ) + *pnSize = 0; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + + if( pnSize != NULL ) + *pnSize += nRead; + (*ppabyRet)[nDataLen] = '\0'; + if( nRead == 0 ) + break; + } + } + else + { + VSIFSeekL( fp, 0, SEEK_END ); + nDataLen = VSIFTellL( fp ); + + // With "large" VSI I/O API we can read data chunks larger than VSIMalloc + // could allocate. Catch it here. + if ( nDataLen > (vsi_l_offset)(size_t)nDataLen || + (nMaxSize >= 0 && nDataLen > (vsi_l_offset)nMaxSize) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input file too large to be opened" ); + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + + VSIFSeekL( fp, 0, SEEK_SET ); + + *ppabyRet = (GByte*)VSIMalloc((size_t)(nDataLen + 1)); + if( NULL == *ppabyRet ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocated " CPL_FRMT_GIB " bytes", + nDataLen + 1 ); + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + + (*ppabyRet)[nDataLen] = '\0'; + if( ( nDataLen != VSIFReadL( *ppabyRet, 1, (size_t)nDataLen, fp ) ) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot read " CPL_FRMT_GIB " bytes", + nDataLen ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + if( pnSize != NULL ) + *pnSize = nDataLen; + } + if( bFreeFP ) + VSIFCloseL( fp ); + return TRUE; +} + +/************************************************************************/ +/* VSIFGetNativeFileDescriptorL() */ +/************************************************************************/ + +/** + * \brief Returns the "native" file descriptor for the virtual handle. + * + * This will only return a non-NULL value for "real" files handled by the + * operating system (to be opposed to GDAL virtual file systems). + * + * On POSIX systems, this will be a integer value ("fd") cast as a void*. + * On Windows systems, this will be the HANDLE. + * + * @param fp file handle opened with VSIFOpenL(). + * + * @return the native file descriptor, or NULL. + */ + +void *VSIFGetNativeFileDescriptorL( VSILFILE* fp ) +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->GetNativeFileDescriptor(); +} + + /************************************************************************/ /* ==================================================================== */ /* VSIFileManager() */ @@ -579,7 +1073,7 @@ VSIFileManager::~VSIFileManager() for( iter = oHandlers.begin(); iter != oHandlers.end(); - iter++ ) + ++iter ) { delete iter->second; } @@ -593,13 +1087,35 @@ VSIFileManager::~VSIFileManager() /************************************************************************/ static VSIFileManager *poManager = NULL; +static void* hVSIFileManagerMutex = NULL; VSIFileManager *VSIFileManager::Get() { - + static volatile int nConstructerPID = 0; + if( poManager != NULL ) + { + if( nConstructerPID != 0 ) + { + int nCurrentPID = (int)CPLGetPID(); + if( nConstructerPID != nCurrentPID ) + { + //printf("Thread %d: Waiting for VSIFileManager to be finished by other thread.\n", nCurrentPID); + { + CPLMutexHolder oHolder( &hVSIFileManagerMutex ); + } + //printf("Thread %d: End of wait for VSIFileManager construction to be finished\n", nCurrentPID); + CPLAssert(nConstructerPID == 0); + } + } + return poManager; + } + + CPLMutexHolder oHolder2( &hVSIFileManagerMutex ); if( poManager == NULL ) { + nConstructerPID = (int)CPLGetPID(); + //printf("Thread %d: VSIFileManager in construction\n", nConstructerPID); poManager = new VSIFileManager; VSIInstallLargeFileHandler(); VSIInstallSubFileHandler(); @@ -608,7 +1124,16 @@ VSIFileManager *VSIFileManager::Get() VSIInstallGZipFileHandler(); VSIInstallZipFileHandler(); #endif +#ifdef HAVE_CURL + VSIInstallCurlFileHandler(); + VSIInstallCurlStreamingFileHandler(); +#endif + VSIInstallStdinHandler(); VSIInstallStdoutHandler(); + VSIInstallSparseFileHandler(); + VSIInstallTarFileHandler(); + //printf("Thread %d: VSIFileManager construction finished\n", nConstructerPID); + nConstructerPID = 0; } return poManager; @@ -627,7 +1152,7 @@ VSIFilesystemHandler *VSIFileManager::GetHandler( const char *pszPath ) for( iter = poThis->oHandlers.begin(); iter != poThis->oHandlers.end(); - iter++ ) + ++iter ) { const char* pszIterKey = iter->first.c_str(); int nIterKeyLen = iter->first.size(); @@ -640,6 +1165,11 @@ VSIFilesystemHandler *VSIFileManager::GetHandler( const char *pszPath ) pszPath[nIterKeyLen-1] == '\\' && strncmp(pszPath,pszIterKey,nIterKeyLen-1) == 0 ) return iter->second; + + /* /vsimem should be treated as a match for /vsimem/ */ + if( nPathLen == nIterKeyLen - 1 + && strncmp(pszPath,pszIterKey,nIterKeyLen-1) == 0 ) + return iter->second; } return poThis->poDefaultHandler; @@ -671,4 +1201,41 @@ void VSICleanupFileManager() delete poManager; poManager = NULL; } + + if( hVSIFileManagerMutex != NULL ) + { + CPLDestroyMutex(hVSIFileManagerMutex); + hVSIFileManagerMutex = NULL; + } +} + +/************************************************************************/ +/* ReadMultiRange() */ +/************************************************************************/ + +int VSIVirtualHandle::ReadMultiRange( int nRanges, void ** ppData, + const vsi_l_offset* panOffsets, + const size_t* panSizes ) +{ + int nRet = 0; + vsi_l_offset nCurOffset = Tell(); + for(int i=0;i + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include +#include + +#define ENABLE_DEBUG 0 + +CPL_CVSID("$Id: cpl_vsil_abstract_archive.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* ~VSIArchiveEntryFileOffset() */ +/************************************************************************/ + +VSIArchiveEntryFileOffset::~VSIArchiveEntryFileOffset() +{ +} + +/************************************************************************/ +/* ~VSIArchiveReader() */ +/************************************************************************/ + +VSIArchiveReader::~VSIArchiveReader() +{ +} + +/************************************************************************/ +/* VSIArchiveFilesystemHandler() */ +/************************************************************************/ + +VSIArchiveFilesystemHandler::VSIArchiveFilesystemHandler() +{ + hMutex = NULL; +} + +/************************************************************************/ +/* ~VSIArchiveFilesystemHandler() */ +/************************************************************************/ + +VSIArchiveFilesystemHandler::~VSIArchiveFilesystemHandler() + +{ + std::map::const_iterator iter; + + for( iter = oFileList.begin(); iter != oFileList.end(); ++iter ) + { + VSIArchiveContent* content = iter->second; + int i; + for(i=0;inEntries;i++) + { + delete content->entries[i].file_pos; + CPLFree(content->entries[i].fileName); + } + CPLFree(content->entries); + delete content; + } + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* GetContentOfArchive() */ +/************************************************************************/ + +const VSIArchiveContent* VSIArchiveFilesystemHandler::GetContentOfArchive + (const char* archiveFilename, VSIArchiveReader* poReader) +{ + CPLMutexHolder oHolder( &hMutex ); + + if (oFileList.find(archiveFilename) != oFileList.end() ) + { + return oFileList[archiveFilename]; + } + + int bMustClose = (poReader == NULL); + if (poReader == NULL) + { + poReader = CreateReader(archiveFilename); + if (!poReader) + return NULL; + } + + if (poReader->GotoFirstFile() == FALSE) + { + if (bMustClose) + delete(poReader); + return NULL; + } + + VSIArchiveContent* content = new VSIArchiveContent; + content->nEntries = 0; + content->entries = NULL; + oFileList[archiveFilename] = content; + + std::set oSet; + + do + { + CPLString osFileName = poReader->GetFileName(); + const char* fileName = osFileName.c_str(); + + /* Remove ./ pattern at the beginning of a filename */ + if (fileName[0] == '.' && fileName[1] == '/') + { + fileName += 2; + if (fileName[0] == '\0') + continue; + } + + char* pszStrippedFileName = CPLStrdup(fileName); + char* pszIter; + for(pszIter = pszStrippedFileName;*pszIter;pszIter++) + { + if (*pszIter == '\\') + *pszIter = '/'; + } + + int bIsDir = strlen(fileName) > 0 && + fileName[strlen(fileName)-1] == '/'; + if (bIsDir) + { + /* Remove trailing slash */ + pszStrippedFileName[strlen(fileName)-1] = 0; + } + + if (oSet.find(pszStrippedFileName) == oSet.end()) + { + oSet.insert(pszStrippedFileName); + + /* Add intermediate directory structure */ + for(pszIter = pszStrippedFileName;*pszIter;pszIter++) + { + if (*pszIter == '/') + { + char* pszStrippedFileName2 = CPLStrdup(pszStrippedFileName); + pszStrippedFileName2[pszIter - pszStrippedFileName] = 0; + if (oSet.find(pszStrippedFileName2) == oSet.end()) + { + oSet.insert(pszStrippedFileName2); + + content->entries = (VSIArchiveEntry*)CPLRealloc(content->entries, + sizeof(VSIArchiveEntry) * (content->nEntries + 1)); + content->entries[content->nEntries].fileName = pszStrippedFileName2; + content->entries[content->nEntries].nModifiedTime = poReader->GetModifiedTime(); + content->entries[content->nEntries].uncompressed_size = 0; + content->entries[content->nEntries].bIsDir = TRUE; + content->entries[content->nEntries].file_pos = NULL; + if (ENABLE_DEBUG) + CPLDebug("VSIArchive", "[%d] %s : " CPL_FRMT_GUIB " bytes", content->nEntries+1, + content->entries[content->nEntries].fileName, + content->entries[content->nEntries].uncompressed_size); + content->nEntries++; + } + else + { + CPLFree(pszStrippedFileName2); + } + } + } + + content->entries = (VSIArchiveEntry*)CPLRealloc(content->entries, + sizeof(VSIArchiveEntry) * (content->nEntries + 1)); + content->entries[content->nEntries].fileName = pszStrippedFileName; + content->entries[content->nEntries].nModifiedTime = poReader->GetModifiedTime(); + content->entries[content->nEntries].uncompressed_size = poReader->GetFileSize(); + content->entries[content->nEntries].bIsDir = bIsDir; + content->entries[content->nEntries].file_pos = poReader->GetFileOffset(); + if (ENABLE_DEBUG) + CPLDebug("VSIArchive", "[%d] %s : " CPL_FRMT_GUIB " bytes", content->nEntries+1, + content->entries[content->nEntries].fileName, + content->entries[content->nEntries].uncompressed_size); + content->nEntries++; + } + else + { + CPLFree(pszStrippedFileName); + } + } while(poReader->GotoNextFile()); + + if (bMustClose) + delete(poReader); + + return content; +} + +/************************************************************************/ +/* FindFileInArchive() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::FindFileInArchive(const char* archiveFilename, + const char* fileInArchiveName, + const VSIArchiveEntry** archiveEntry) +{ + if (fileInArchiveName == NULL) + return FALSE; + + const VSIArchiveContent* content = GetContentOfArchive(archiveFilename); + if (content) + { + int i; + for(i=0;inEntries;i++) + { + if (strcmp(fileInArchiveName, content->entries[i].fileName) == 0) + { + if (archiveEntry) + *archiveEntry = &content->entries[i]; + return TRUE; + } + } + } + return FALSE; +} + +/************************************************************************/ +/* SplitFilename() */ +/************************************************************************/ + +char* VSIArchiveFilesystemHandler::SplitFilename(const char *pszFilename, + CPLString &osFileInArchive, + int bCheckMainFileExists) +{ + int i = 0; + + if (strcmp(pszFilename, GetPrefix()) == 0) + return NULL; + + /* Allow natural chaining of VSI drivers without requiring double slash */ + + CPLString osDoubleVsi(GetPrefix()); + osDoubleVsi += "/vsi"; + + if (strncmp(pszFilename, osDoubleVsi.c_str(), osDoubleVsi.size()) == 0) + pszFilename += strlen(GetPrefix()); + else + pszFilename += strlen(GetPrefix()) + 1; + + while(pszFilename[i]) + { + std::vector oExtensions = GetExtensions(); + std::vector::const_iterator iter; + int nToSkip = 0; + + for( iter = oExtensions.begin(); iter != oExtensions.end(); ++iter ) + { + const CPLString& osExtension = *iter; + if (EQUALN(pszFilename + i, osExtension.c_str(), strlen(osExtension.c_str()))) + { + nToSkip = strlen(osExtension.c_str()); + break; + } + } + + if (nToSkip != 0) + { + VSIStatBufL statBuf; + char* archiveFilename = CPLStrdup(pszFilename); + int bArchiveFileExists = FALSE; + + if (archiveFilename[i + nToSkip] == '/' || + archiveFilename[i + nToSkip] == '\\') + { + archiveFilename[i + nToSkip] = 0; + } + + if (!bCheckMainFileExists) + { + bArchiveFileExists = TRUE; + } + else + { + CPLMutexHolder oHolder( &hMutex ); + + if (oFileList.find(archiveFilename) != oFileList.end() ) + { + bArchiveFileExists = TRUE; + } + } + + if (!bArchiveFileExists) + { + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( archiveFilename ); + if (poFSHandler->Stat(archiveFilename, &statBuf, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 && + !VSI_ISDIR(statBuf.st_mode)) + { + bArchiveFileExists = TRUE; + } + } + + if (bArchiveFileExists) + { + if (pszFilename[i + nToSkip] == '/' || + pszFilename[i + nToSkip] == '\\') + { + char* pszArchiveInFileName = CPLStrdup(pszFilename + i + nToSkip + 1); + + /* Replace a/../b by b and foo/a/../b by foo/b */ + while(TRUE) + { + char* pszPrevDir = strstr(pszArchiveInFileName, "/../"); + if (pszPrevDir == NULL || pszPrevDir == pszArchiveInFileName) + break; + + char* pszPrevSlash = pszPrevDir - 1; + while(pszPrevSlash != pszArchiveInFileName && + *pszPrevSlash != '/') + pszPrevSlash --; + if (pszPrevSlash == pszArchiveInFileName) + memmove(pszArchiveInFileName, pszPrevDir + nToSkip, strlen(pszPrevDir + nToSkip) + 1); + else + memmove(pszPrevSlash + 1, pszPrevDir + nToSkip, strlen(pszPrevDir + nToSkip) + 1); + } + + osFileInArchive = pszArchiveInFileName; + CPLFree(pszArchiveInFileName); + } + else + osFileInArchive = ""; + + /* Remove trailing slash */ + if (osFileInArchive.size()) + { + char lastC = osFileInArchive[strlen(osFileInArchive) - 1]; + if (lastC == '\\' || lastC == '/') + osFileInArchive.resize(strlen(osFileInArchive) - 1); + } + + return archiveFilename; + } + CPLFree(archiveFilename); + } + i++; + } + return NULL; +} + +/************************************************************************/ +/* OpenArchiveFile() */ +/************************************************************************/ + +VSIArchiveReader* VSIArchiveFilesystemHandler::OpenArchiveFile(const char* archiveFilename, + const char* fileInArchiveName) +{ + VSIArchiveReader* poReader = CreateReader(archiveFilename); + + if (poReader == NULL) + { + return NULL; + } + + if (fileInArchiveName == NULL || strlen(fileInArchiveName) == 0) + { + if (poReader->GotoFirstFile() == FALSE) + { + delete(poReader); + return NULL; + } + + /* Skip optionnal leading subdir */ + CPLString osFileName = poReader->GetFileName(); + const char* fileName = osFileName.c_str(); + if (fileName[strlen(fileName)-1] == '/' || fileName[strlen(fileName)-1] == '\\') + { + if (poReader->GotoNextFile() == FALSE) + { + delete(poReader); + return NULL; + } + } + + if (poReader->GotoNextFile()) + { + CPLString msg; + msg.Printf("Support only 1 file in archive file %s when no explicit in-archive filename is specified", + archiveFilename); + const VSIArchiveContent* content = GetContentOfArchive(archiveFilename, poReader); + if (content) + { + int i; + msg += "\nYou could try one of the following :\n"; + for(i=0;inEntries;i++) + { + msg += CPLString().Printf(" %s/%s/%s\n", GetPrefix(), archiveFilename, content->entries[i].fileName); + } + } + + CPLError(CE_Failure, CPLE_NotSupported, "%s", msg.c_str()); + + delete(poReader); + return NULL; + } + } + else + { + const VSIArchiveEntry* archiveEntry = NULL; + if (FindFileInArchive(archiveFilename, fileInArchiveName, &archiveEntry) == FALSE || + archiveEntry->bIsDir) + { + delete(poReader); + return NULL; + } + if (!poReader->GotoFileOffset(archiveEntry->file_pos)) + { + delete poReader; + return NULL; + } + } + return poReader; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ) +{ + int ret = -1; + CPLString osFileInArchive; + + memset(pStatBuf, 0, sizeof(VSIStatBufL)); + + char* archiveFilename = SplitFilename(pszFilename, osFileInArchive, TRUE); + if (archiveFilename == NULL) + return -1; + + if (strlen(osFileInArchive) != 0) + { + if (ENABLE_DEBUG) CPLDebug("VSIArchive", "Looking for %s %s\n", + archiveFilename, osFileInArchive.c_str()); + + const VSIArchiveEntry* archiveEntry = NULL; + if (FindFileInArchive(archiveFilename, osFileInArchive, &archiveEntry)) + { + /* Patching st_size with uncompressed file size */ + pStatBuf->st_size = archiveEntry->uncompressed_size; + pStatBuf->st_mtime = (time_t)archiveEntry->nModifiedTime; + if (archiveEntry->bIsDir) + pStatBuf->st_mode = S_IFDIR; + else + pStatBuf->st_mode = S_IFREG; + ret = 0; + } + } + else + { + VSIArchiveReader* poReader = CreateReader(archiveFilename); + CPLFree(archiveFilename); + archiveFilename = NULL; + + if (poReader != NULL && poReader->GotoFirstFile()) + { + /* Skip optionnal leading subdir */ + CPLString osFileName = poReader->GetFileName(); + const char* fileName = osFileName.c_str(); + if (fileName[strlen(fileName)-1] == '/' || fileName[strlen(fileName)-1] == '\\') + { + if (poReader->GotoNextFile() == FALSE) + { + delete(poReader); + return -1; + } + } + + if (poReader->GotoNextFile()) + { + /* Several files in archive --> treat as dir */ + pStatBuf->st_size = 0; + pStatBuf->st_mode = S_IFDIR; + } + else + { + /* Patching st_size with uncompressed file size */ + pStatBuf->st_size = poReader->GetFileSize(); + pStatBuf->st_mtime = (time_t)poReader->GetModifiedTime(); + pStatBuf->st_mode = S_IFREG; + } + + ret = 0; + } + + delete(poReader); + } + + CPLFree(archiveFilename); + return ret; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Unlink( const char *pszFilename ) +{ + return -1; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Rename( const char *oldpath, const char *newpath ) +{ + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Mkdir( const char *pszDirname, long nMode ) +{ + return -1; +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Rmdir( const char *pszDirname ) +{ + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char** VSIArchiveFilesystemHandler::ReadDir( const char *pszDirname ) +{ + CPLString osInArchiveSubDir; + char* archiveFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE); + if (archiveFilename == NULL) + return NULL; + int lenInArchiveSubDir = strlen(osInArchiveSubDir); + + char **papszDir = NULL; + + const VSIArchiveContent* content = GetContentOfArchive(archiveFilename); + if (!content) + { + CPLFree(archiveFilename); + return NULL; + } + + if (ENABLE_DEBUG) CPLDebug("VSIArchive", "Read dir %s", pszDirname); + int i; + for(i=0;inEntries;i++) + { + const char* fileName = content->entries[i].fileName; + /* Only list entries at the same level of inArchiveSubDir */ + if (lenInArchiveSubDir != 0 && + strncmp(fileName, osInArchiveSubDir, lenInArchiveSubDir) == 0 && + (fileName[lenInArchiveSubDir] == '/' || fileName[lenInArchiveSubDir] == '\\') && + fileName[lenInArchiveSubDir + 1] != 0) + { + const char* slash = strchr(fileName + lenInArchiveSubDir + 1, '/'); + if (slash == NULL) + slash = strchr(fileName + lenInArchiveSubDir + 1, '\\'); + if (slash == NULL || slash[1] == 0) + { + char* tmpFileName = CPLStrdup(fileName); + if (slash != NULL) + { + tmpFileName[strlen(tmpFileName)-1] = 0; + } + if (ENABLE_DEBUG) + CPLDebug("VSIArchive", "Add %s as in directory %s\n", + tmpFileName + lenInArchiveSubDir + 1, pszDirname); + papszDir = CSLAddString(papszDir, tmpFileName + lenInArchiveSubDir + 1); + CPLFree(tmpFileName); + } + } + else if (lenInArchiveSubDir == 0 && + strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL) + { + /* Only list toplevel files and directories */ + if (ENABLE_DEBUG) CPLDebug("VSIArchive", "Add %s as in directory %s\n", fileName, pszDirname); + papszDir = CSLAddString(papszDir, fileName); + } + } + + CPLFree(archiveFilename); + return papszDir; +} diff --git a/cpl/cpl_vsil_buffered_reader.cpp b/cpl/cpl_vsil_buffered_reader.cpp new file mode 100644 index 0000000..527a121 --- /dev/null +++ b/cpl/cpl_vsil_buffered_reader.cpp @@ -0,0 +1,243 @@ +/****************************************************************************** + * $Id: cpl_vsil_buffered_reader.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: VSI Virtual File System + * Purpose: Implementation of buffered reader IO functions. + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2010-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* The intent of this class is to be a wrapper around an underlying virtual */ +/* handle and add very basic caching of last read bytes, so that a backward */ +/* seek of a few bytes doesn't require a seek on the underlying virtual handle. */ +/* This enable us to improve dramatically the performance of CPLReadLine2L() on */ +/* a gzip file */ + +#include "cpl_vsi_virtual.h" + +#define MAX_BUFFER_SIZE 65536 + +CPL_CVSID("$Id: cpl_vsil_buffered_reader.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +class VSIBufferedReaderHandle : public VSIVirtualHandle +{ + VSIVirtualHandle* poBaseHandle; + char pabyBuffer[MAX_BUFFER_SIZE]; + GUIntBig nBufferOffset; + int nBufferSize; + GUIntBig nCurOffset; + int bNeedBaseHandleSeek; + int bEOF; + + public: + + VSIBufferedReaderHandle(VSIVirtualHandle* poBaseHandle); + ~VSIBufferedReaderHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSICreateBufferedReaderHandle() */ +/************************************************************************/ + +VSIVirtualHandle* VSICreateBufferedReaderHandle(VSIVirtualHandle* poBaseHandle) +{ + return new VSIBufferedReaderHandle(poBaseHandle); +} + +/************************************************************************/ +/* VSIBufferedReaderHandle() */ +/************************************************************************/ + +VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle* poBaseHandle) +{ + this->poBaseHandle = poBaseHandle; + nBufferOffset = 0; + nBufferSize = 0; + nCurOffset = 0; + bNeedBaseHandleSeek = FALSE; + bEOF = FALSE; +} + +/************************************************************************/ +/* ~VSIBufferedReaderHandle() */ +/************************************************************************/ + +VSIBufferedReaderHandle::~VSIBufferedReaderHandle() +{ + delete poBaseHandle; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + //CPLDebug( "BUFFERED", "Seek(%d,%d)", (int)nOffset, (int)nWhence); + bEOF = FALSE; + if (nWhence == SEEK_CUR) + nCurOffset += nOffset; + else if (nWhence == SEEK_END) + { + poBaseHandle->Seek(nOffset, nWhence); + nCurOffset = poBaseHandle->Tell(); + bNeedBaseHandleSeek = TRUE; + } + else + nCurOffset = nOffset; + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIBufferedReaderHandle::Tell() +{ + //CPLDebug( "BUFFERED", "Tell() = %d", (int)nCurOffset); + return nCurOffset; +} +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIBufferedReaderHandle::Read( void *pBuffer, size_t nSize, size_t nMemb ) +{ + const size_t nTotalToRead = nSize * nMemb; + //CPLDebug( "BUFFERED", "Read(%d)", (int)nTotalToRead); + + if (nSize == 0) + return 0; + + if (nBufferSize != 0 && + nCurOffset >= nBufferOffset && nCurOffset <= nBufferOffset + nBufferSize) + { + /* We try to read from an offset located within the buffer */ + const int nReadInBuffer = (int) MIN(nTotalToRead, nBufferOffset + nBufferSize - nCurOffset); + memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer); + const int nToReadInFile = nTotalToRead - nReadInBuffer; + if (nToReadInFile > 0) + { + /* The beginning of the the data to read is located in the buffer */ + /* but the end must be read from the file */ + if (bNeedBaseHandleSeek) + poBaseHandle->Seek(nBufferOffset + nBufferSize, SEEK_SET); + bNeedBaseHandleSeek = FALSE; + //CPLAssert(poBaseHandle->Tell() == nBufferOffset + nBufferSize); + + const int nReadInFile = poBaseHandle->Read((GByte*)pBuffer + nReadInBuffer, 1, nToReadInFile); + const int nRead = nReadInBuffer + nReadInFile; + + nBufferSize = MIN(nRead, MAX_BUFFER_SIZE); + nBufferOffset = nCurOffset + nRead - nBufferSize; + memcpy(pabyBuffer, (GByte*)pBuffer + nRead - nBufferSize, nBufferSize); + + nCurOffset += nRead; + //CPLAssert(poBaseHandle->Tell() == nBufferOffset + nBufferSize); + //CPLAssert(poBaseHandle->Tell() == nCurOffset); + + bEOF = poBaseHandle->Eof(); + + return nRead / nSize; + } + else + { + /* The data to read is completely located within the buffer */ + nCurOffset += nTotalToRead; + return nTotalToRead / nSize; + } + } + else + { + /* We try either to read before or after the buffer, so a seek is necessary */ + poBaseHandle->Seek(nCurOffset, SEEK_SET); + bNeedBaseHandleSeek = FALSE; + const int nReadInFile = poBaseHandle->Read(pBuffer, 1, nTotalToRead); + nBufferSize = MIN(nReadInFile, MAX_BUFFER_SIZE); + nBufferOffset = nCurOffset + nReadInFile - nBufferSize; + memcpy(pabyBuffer, (GByte*)pBuffer + nReadInFile - nBufferSize, nBufferSize); + + nCurOffset += nReadInFile; + //CPLAssert(poBaseHandle->Tell() == nBufferOffset + nBufferSize); + //CPLAssert(poBaseHandle->Tell() == nCurOffset); + + bEOF = poBaseHandle->Eof(); + + return nReadInFile / nSize; + } + +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIBufferedReaderHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb ) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "VSIFWriteL is not supported on buffer reader streams\n"); + return 0; +} + + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Eof() +{ + return bEOF; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Flush() +{ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Close() +{ + if (poBaseHandle) + { + poBaseHandle->Close(); + delete poBaseHandle; + poBaseHandle = NULL; + } + return 0; +} diff --git a/cpl/cpl_vsil_cache.cpp b/cpl/cpl_vsil_cache.cpp new file mode 100644 index 0000000..8b92bcf --- /dev/null +++ b/cpl/cpl_vsil_cache.cpp @@ -0,0 +1,516 @@ +/****************************************************************************** + * $Id$ + * + * Project: VSI Virtual File System + * Purpose: Implementation of caching IO layer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * Copyright (c) 2011-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* ==================================================================== */ +/* VSICacheChunk */ +/* ==================================================================== */ +/************************************************************************/ + +class VSICacheChunk +{ +public: + VSICacheChunk() + { + poLRUPrev = poLRUNext = NULL; + nDataFilled = 0; + bDirty = FALSE; + pabyData = NULL; + } + + virtual ~VSICacheChunk() + { + VSIFree( pabyData ); + } + + bool Allocate( size_t nChunkSize ) + { + CPLAssert( pabyData == NULL ); + pabyData = (GByte *)VSIMalloc( nChunkSize ); + return (pabyData != NULL); + } + + int bDirty; + vsi_l_offset iBlock; + + VSICacheChunk *poLRUPrev; + VSICacheChunk *poLRUNext; + + vsi_l_offset nDataFilled; + GByte *pabyData; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSICachedFile */ +/* ==================================================================== */ +/************************************************************************/ + +class VSICachedFile : public VSIVirtualHandle +{ + public: + VSICachedFile( VSIVirtualHandle *poBaseHandle, + size_t nChunkSize, + size_t nCacheSize ); + ~VSICachedFile() { Close(); } + + void FlushLRU(); + int LoadBlocks( vsi_l_offset nStartBlock, size_t nBlockCount, + void *pBuffer, size_t nBufferSize ); + void Demote( VSICacheChunk * ); + + VSIVirtualHandle *poBase; + + vsi_l_offset nOffset; + vsi_l_offset nFileSize; + + GUIntBig nCacheUsed; + GUIntBig nCacheMax; + + size_t nChunkSize; + + VSICacheChunk *poLRUStart; + VSICacheChunk *poLRUEnd; + + std::vector apoCache; + + int bEOF; + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + virtual void *GetNativeFileDescriptor() { return poBase->GetNativeFileDescriptor(); } +}; + +/************************************************************************/ +/* VSICachedFile() */ +/************************************************************************/ + +VSICachedFile::VSICachedFile( VSIVirtualHandle *poBaseHandle, size_t nChunkSize, size_t nCacheSize ) + +{ + poBase = poBaseHandle; + this->nChunkSize = nChunkSize; + + nCacheUsed = 0; + if ( nCacheSize == 0 ) + nCacheMax = CPLScanUIntBig( + CPLGetConfigOption( "VSI_CACHE_SIZE", "25000000" ), 40 ); + else + nCacheMax = nCacheSize; + + poLRUStart = NULL; + poLRUEnd = NULL; + + poBase->Seek( 0, SEEK_END ); + nFileSize = poBase->Tell(); + + nOffset = 0; + bEOF = FALSE; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSICachedFile::Close() + +{ + size_t i; + for( i = 0; i < apoCache.size(); i++ ) + delete apoCache[i]; + + apoCache.resize( 0 ); + + poLRUStart = NULL; + poLRUEnd = NULL; + + nCacheUsed = 0; + + if( poBase ) + { + poBase->Close(); + delete poBase; + poBase = NULL; + } + + return 0; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSICachedFile::Seek( vsi_l_offset nReqOffset, int nWhence ) + +{ + bEOF = FALSE; + + if( nWhence == SEEK_SET ) + { + // use offset directly. + } + + else if( nWhence == SEEK_CUR ) + { + nReqOffset += nOffset; + } + + else if( nWhence == SEEK_END ) + { + nReqOffset += nFileSize; + } + + nOffset = nReqOffset; + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSICachedFile::Tell() + +{ + return nOffset; +} + +/************************************************************************/ +/* FlushLRU() */ +/************************************************************************/ + +void VSICachedFile::FlushLRU() + +{ + CPLAssert( poLRUStart != NULL ); + + VSICacheChunk *poBlock = poLRUStart; + + CPLAssert( nCacheUsed >= poBlock->nDataFilled ); + + nCacheUsed -= poBlock->nDataFilled; + + poLRUStart = poBlock->poLRUNext; + if( poLRUEnd == poBlock ) + poLRUEnd = NULL; + + if( poBlock->poLRUNext != NULL ) + poBlock->poLRUNext->poLRUPrev = NULL; + + CPLAssert( !poBlock->bDirty ); + + apoCache[poBlock->iBlock] = NULL; + + delete poBlock; +} + +/************************************************************************/ +/* Demote() */ +/* */ +/* Demote the indicated block to the end of the LRU list. */ +/* Potentially integrate the link into the list if it is not */ +/* already there. */ +/************************************************************************/ + +void VSICachedFile::Demote( VSICacheChunk *poBlock ) + +{ + // already at end? + if( poLRUEnd == poBlock ) + return; + + if( poLRUStart == poBlock ) + poLRUStart = poBlock->poLRUNext; + + if( poBlock->poLRUPrev != NULL ) + poBlock->poLRUPrev->poLRUNext = poBlock->poLRUNext; + + if( poBlock->poLRUNext != NULL ) + poBlock->poLRUNext->poLRUPrev = poBlock->poLRUPrev; + + poBlock->poLRUNext = NULL; + poBlock->poLRUPrev = NULL; + + if( poLRUEnd != NULL ) + poLRUEnd->poLRUNext = poBlock; + poLRUEnd = poBlock; + + if( poLRUStart == NULL ) + poLRUStart = poBlock; +} + +/************************************************************************/ +/* LoadBlocks() */ +/* */ +/* Load the desired set of blocks. Use pBuffer as a temporary */ +/* buffer if it would be helpful. */ +/************************************************************************/ + +int VSICachedFile::LoadBlocks( vsi_l_offset nStartBlock, size_t nBlockCount, + void *pBuffer, size_t nBufferSize ) + +{ + if( nBlockCount == 0 ) + return 1; + + if( apoCache.size() < nStartBlock + nBlockCount ) + apoCache.resize( nStartBlock + nBlockCount ); + +/* -------------------------------------------------------------------- */ +/* When we want to load only one block, we can directly load it */ +/* into the target buffer with no concern about intermediaries. */ +/* -------------------------------------------------------------------- */ + if( nBlockCount == 1 ) + { + poBase->Seek( (vsi_l_offset)nStartBlock * nChunkSize, SEEK_SET ); + + VSICacheChunk *poBlock = new VSICacheChunk(); + if ( !poBlock || !poBlock->Allocate( nChunkSize ) ) + { + delete poBlock; + return 0; + } + + apoCache[nStartBlock] = poBlock; + + poBlock->iBlock = nStartBlock; + poBlock->nDataFilled = poBase->Read( poBlock->pabyData, 1, nChunkSize ); + nCacheUsed += poBlock->nDataFilled; + + // Merges into the LRU list. + Demote( poBlock ); + + return 1; + } + +/* -------------------------------------------------------------------- */ +/* If the buffer is quite large but not quite large enough to */ +/* hold all the blocks we will take the pain of splitting the */ +/* io request in two in order to avoid allocating a large */ +/* temporary buffer. */ +/* -------------------------------------------------------------------- */ + if( nBufferSize > nChunkSize * 20 + && nBufferSize < nBlockCount * nChunkSize ) + { + if( !LoadBlocks( nStartBlock, 2, pBuffer, nBufferSize ) ) + return 0; + + return LoadBlocks( nStartBlock+2, nBlockCount-2, pBuffer, nBufferSize ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need to allocate our own buffer? */ +/* -------------------------------------------------------------------- */ + GByte *pabyWorkBuffer = (GByte *) pBuffer; + + if( nBufferSize < nChunkSize * nBlockCount ) + pabyWorkBuffer = (GByte *) CPLMalloc(nChunkSize * nBlockCount); + +/* -------------------------------------------------------------------- */ +/* Read the whole request into the working buffer. */ +/* -------------------------------------------------------------------- */ + if( poBase->Seek( (vsi_l_offset)nStartBlock * nChunkSize, SEEK_SET ) != 0 ) + return 0; + + size_t nDataRead = poBase->Read( pabyWorkBuffer, 1, nBlockCount*nChunkSize); + + if( nBlockCount * nChunkSize > nDataRead + nChunkSize - 1 ) + nBlockCount = (nDataRead + nChunkSize - 1) / nChunkSize; + + for( size_t i = 0; i < nBlockCount; i++ ) + { + VSICacheChunk *poBlock = new VSICacheChunk(); + if ( !poBlock || !poBlock->Allocate( nChunkSize ) ) + { + delete poBlock; + return 0; + } + + poBlock->iBlock = nStartBlock + i; + + CPLAssert( apoCache[i+nStartBlock] == NULL ); + + apoCache[i + nStartBlock] = poBlock; + + if( nDataRead >= (i+1) * nChunkSize ) + poBlock->nDataFilled = nChunkSize; + else + poBlock->nDataFilled = nDataRead - i*nChunkSize; + + memcpy( poBlock->pabyData, pabyWorkBuffer + i*nChunkSize, + (size_t) poBlock->nDataFilled ); + + nCacheUsed += poBlock->nDataFilled; + + // Merges into the LRU list. + Demote( poBlock ); + } + + if( pabyWorkBuffer != pBuffer ) + CPLFree( pabyWorkBuffer ); + + return 1; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSICachedFile::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + if( nOffset >= nFileSize ) + { + bEOF = TRUE; + return 0; + } + +/* ==================================================================== */ +/* Make sure the cache is loaded for the whole request region. */ +/* ==================================================================== */ + vsi_l_offset nStartBlock = nOffset / nChunkSize; + vsi_l_offset nEndBlock = (nOffset + nSize * nCount - 1) / nChunkSize; + + for( vsi_l_offset iBlock = nStartBlock; iBlock <= nEndBlock; iBlock++ ) + { + if( apoCache.size() <= iBlock || apoCache[iBlock] == NULL ) + { + size_t nBlocksToLoad = 1; + while( iBlock + nBlocksToLoad <= nEndBlock + && (apoCache.size() <= iBlock+nBlocksToLoad + || apoCache[iBlock+nBlocksToLoad] == NULL) ) + nBlocksToLoad++; + + LoadBlocks( iBlock, nBlocksToLoad, pBuffer, nSize * nCount ); + } + } + +/* ==================================================================== */ +/* Copy data into the target buffer to the extent possible. */ +/* ==================================================================== */ + size_t nAmountCopied = 0; + + while( nAmountCopied < nSize * nCount ) + { + vsi_l_offset iBlock = (nOffset + nAmountCopied) / nChunkSize; + size_t nThisCopy; + VSICacheChunk *poBlock = apoCache[iBlock]; + if( poBlock == NULL ) + { + /* We can reach that point when the amount to read exceeds */ + /* the cache size */ + LoadBlocks( iBlock, 1, ((GByte *) pBuffer) + nAmountCopied, + MIN(nSize * nCount - nAmountCopied, nChunkSize) ); + poBlock = apoCache[iBlock]; + CPLAssert(poBlock != NULL); + } + + vsi_l_offset nStartOffset = (vsi_l_offset)iBlock * nChunkSize; + nThisCopy = (size_t) + ((nStartOffset + poBlock->nDataFilled) + - nAmountCopied - nOffset); + + if( nThisCopy > nSize * nCount - nAmountCopied ) + nThisCopy = nSize * nCount - nAmountCopied; + + if( nThisCopy == 0 ) + break; + + memcpy( ((GByte *) pBuffer) + nAmountCopied, + poBlock->pabyData + + (nOffset + nAmountCopied) - nStartOffset, + nThisCopy ); + + nAmountCopied += nThisCopy; + } + + nOffset += nAmountCopied; + +/* -------------------------------------------------------------------- */ +/* Ensure the cache is reduced to our limit. */ +/* -------------------------------------------------------------------- */ + while( nCacheUsed > nCacheMax ) + FlushLRU(); + + size_t nRet = nAmountCopied / nSize; + if (nRet != nCount) + bEOF = TRUE; + return nRet; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSICachedFile::Write( const void * pBuffer, size_t nSize, size_t nCount ) + +{ + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSICachedFile::Eof() + +{ + return bEOF; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSICachedFile::Flush() + +{ + return 0; +} + +/************************************************************************/ +/* VSICreateCachedFile() */ +/************************************************************************/ + +VSIVirtualHandle * +VSICreateCachedFile( VSIVirtualHandle *poBaseHandle, size_t nChunkSize, size_t nCacheSize ) + +{ + return new VSICachedFile( poBaseHandle, nChunkSize, nCacheSize ); +} diff --git a/cpl/cpl_vsil_sparsefile.cpp b/cpl/cpl_vsil_sparsefile.cpp new file mode 100644 index 0000000..db855b1 --- /dev/null +++ b/cpl/cpl_vsil_sparsefile.cpp @@ -0,0 +1,578 @@ +/****************************************************************************** + * $Id: cpl_vsil_sparsefile.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: VSI Virtual File System + * Purpose: Implementation of sparse file virtual io driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "cpl_minixml.h" +#include + +#if defined(WIN32CE) +# include +#endif + +CPL_CVSID("$Id: cpl_vsil_sparsefile.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +class SFRegion { +public: + SFRegion() : fp(NULL), nDstOffset(0), nSrcOffset(0), nLength(0), + byValue(0), bTriedOpen(FALSE) {} + + CPLString osFilename; + VSILFILE *fp; + GUIntBig nDstOffset; + GUIntBig nSrcOffset; + GUIntBig nLength; + GByte byValue; + int bTriedOpen; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSISparseFileFilesystemHandler; + +class VSISparseFileHandle : public VSIVirtualHandle +{ + VSISparseFileFilesystemHandler* poFS; + + public: + VSISparseFileHandle(VSISparseFileFilesystemHandler* poFS) : poFS(poFS), nOverallLength(0), nCurOffset(0) {} + + GUIntBig nOverallLength; + GUIntBig nCurOffset; + + std::vector aoRegions; + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Close(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSISparseFileFilesystemHandler : public VSIFilesystemHandler +{ + std::map oRecOpenCount; + +public: + VSISparseFileFilesystemHandler(); + virtual ~VSISparseFileFilesystemHandler(); + + int DecomposePath( const char *pszPath, + CPLString &osFilename, + vsi_l_offset &nSparseFileOffset, + vsi_l_offset &nSparseFileSize ); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + + int GetRecCounter() { return oRecOpenCount[CPLGetPID()]; } + void IncRecCounter() { oRecOpenCount[CPLGetPID()] ++; } + void DecRecCounter() { oRecOpenCount[CPLGetPID()] --; } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileHandle */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSISparseFileHandle::Close() + +{ + unsigned int i; + + for( i = 0; i < aoRegions.size(); i++ ) + { + if( aoRegions[i].fp != NULL ) + VSIFCloseL( aoRegions[i].fp ); + } + + return 0; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSISparseFileHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + if( nWhence == SEEK_SET ) + nCurOffset = nOffset; + else if( nWhence == SEEK_CUR ) + { + nCurOffset += nOffset; + } + else if( nWhence == SEEK_END ) + { + nCurOffset = nOverallLength + nOffset; + } + else + { + errno = EINVAL; + return -1; + } + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSISparseFileHandle::Tell() + +{ + return nCurOffset; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSISparseFileHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ +/* -------------------------------------------------------------------- */ +/* Find what region we are in, searching linearly from the */ +/* start. */ +/* -------------------------------------------------------------------- */ + unsigned int iRegion; + + for( iRegion = 0; iRegion < aoRegions.size(); iRegion++ ) + { + if( nCurOffset >= aoRegions[iRegion].nDstOffset + && nCurOffset < aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Default to zeroing the buffer if no corresponding region was */ +/* found. */ +/* -------------------------------------------------------------------- */ + if( iRegion == aoRegions.size() ) + { + memset( pBuffer, 0, nSize * nCount ); + nCurOffset += nSize * nSize; + return nCount; + } + +/* -------------------------------------------------------------------- */ +/* If this request crosses region boundaries, split it into two */ +/* requests. */ +/* -------------------------------------------------------------------- */ + size_t nReturnCount = nCount; + GUIntBig nBytesRequested = nSize * nCount; + GUIntBig nBytesAvailable = + aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength; + + if( nCurOffset + nBytesRequested > nBytesAvailable ) + { + size_t nExtraBytes = + (size_t) (nCurOffset + nBytesRequested - nBytesAvailable); + // Recurse to get the rest of the request. + + GUIntBig nCurOffsetSave = nCurOffset; + nCurOffset += nBytesRequested - nExtraBytes; + size_t nBytesRead = + this->Read( ((char *) pBuffer) + nBytesRequested - nExtraBytes, + 1, nExtraBytes ); + nCurOffset = nCurOffsetSave; + + if( nBytesRead < nExtraBytes ) + nReturnCount -= (nExtraBytes-nBytesRead) / nSize; + + nBytesRequested -= nExtraBytes; + } + +/* -------------------------------------------------------------------- */ +/* Handle a constant region. */ +/* -------------------------------------------------------------------- */ + if( aoRegions[iRegion].osFilename.size() == 0 ) + { + memset( pBuffer, aoRegions[iRegion].byValue, + (size_t) nBytesRequested ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise handle as a file. */ +/* -------------------------------------------------------------------- */ + else + { + if( aoRegions[iRegion].fp == NULL ) + { + if( !aoRegions[iRegion].bTriedOpen ) + { + aoRegions[iRegion].fp = + VSIFOpenL( aoRegions[iRegion].osFilename, "r" ); + if( aoRegions[iRegion].fp == NULL ) + { + CPLDebug( "/vsisparse/", "Failed to open '%s'.", + aoRegions[iRegion].osFilename.c_str() ); + } + aoRegions[iRegion].bTriedOpen = TRUE; + } + if( aoRegions[iRegion].fp == NULL ) + { + return 0; + } + } + + if( VSIFSeekL( aoRegions[iRegion].fp, + nCurOffset + - aoRegions[iRegion].nDstOffset + + aoRegions[iRegion].nSrcOffset, + SEEK_SET ) != 0 ) + return 0; + + poFS->IncRecCounter(); + size_t nBytesRead = VSIFReadL( pBuffer, 1, (size_t) nBytesRequested, + aoRegions[iRegion].fp ); + poFS->DecRecCounter(); + + if( nBytesAvailable < nBytesRequested ) + nReturnCount = nBytesRead / nSize; + } + + nCurOffset += nReturnCount * nSize; + + return nReturnCount; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSISparseFileHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) + +{ + errno = EBADF; + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSISparseFileHandle::Eof() + +{ + return nCurOffset >= nOverallLength; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSISparseFileFilesystemHandler() */ +/************************************************************************/ + +VSISparseFileFilesystemHandler::VSISparseFileFilesystemHandler() + +{ +} + +/************************************************************************/ +/* ~VSISparseFileFilesystemHandler() */ +/************************************************************************/ + +VSISparseFileFilesystemHandler::~VSISparseFileFilesystemHandler() + +{ +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSISparseFileFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + CPLAssert( EQUALN(pszFilename,"/vsisparse/", 11) ); + + if( !EQUAL(pszAccess,"r") && !EQUAL(pszAccess,"rb") ) + { + errno = EACCES; + return NULL; + } + + /* Arbitrary number */ + if( GetRecCounter() == 32 ) + return NULL; + + CPLString osSparseFilePath = pszFilename + 11; + +/* -------------------------------------------------------------------- */ +/* Does this file even exist? */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osSparseFilePath, "r" ); + if( fp == NULL ) + return NULL; + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Read the XML file. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psXMLRoot = CPLParseXMLFile( osSparseFilePath ); + + if( psXMLRoot == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup the file handle on this file. */ +/* -------------------------------------------------------------------- */ + VSISparseFileHandle *poHandle = new VSISparseFileHandle(this); + +/* -------------------------------------------------------------------- */ +/* Translate the desired fields out of the XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRegion; + + for( psRegion = psXMLRoot->psChild; + psRegion != NULL; + psRegion = psRegion->psNext ) + { + if( psRegion->eType != CXT_Element ) + continue; + + if( !EQUAL(psRegion->pszValue,"SubfileRegion") + && !EQUAL(psRegion->pszValue,"ConstantRegion") ) + continue; + + SFRegion oRegion; + + oRegion.osFilename = CPLGetXMLValue( psRegion, "Filename", "" ); + if( atoi(CPLGetXMLValue( psRegion, "Filename.relative", "0" )) != 0 ) + { + CPLString osSFPath = CPLGetPath(osSparseFilePath); + oRegion.osFilename = CPLFormFilename( osSFPath, + oRegion.osFilename, NULL ); + } + + oRegion.nDstOffset = + CPLScanUIntBig( CPLGetXMLValue(psRegion,"DestinationOffset","0" ), + 32 ); + + oRegion.nSrcOffset = + CPLScanUIntBig( CPLGetXMLValue(psRegion,"SourceOffset","0" ), 32); + + oRegion.nLength = + CPLScanUIntBig( CPLGetXMLValue(psRegion,"RegionLength","0" ), 32); + + oRegion.byValue = (GByte) atoi(CPLGetXMLValue(psRegion,"Value","0" )); + + poHandle->aoRegions.push_back( oRegion ); + } + +/* -------------------------------------------------------------------- */ +/* Get sparse file length, use maximum bound of regions if not */ +/* explicit in file. */ +/* -------------------------------------------------------------------- */ + poHandle->nOverallLength = + CPLScanUIntBig( CPLGetXMLValue(psXMLRoot,"Length","0" ), 32); + if( poHandle->nOverallLength == 0 ) + { + unsigned int i; + + for( i = 0; i < poHandle->aoRegions.size(); i++ ) + { + poHandle->nOverallLength = MAX(poHandle->nOverallLength, + poHandle->aoRegions[i].nDstOffset + + poHandle->aoRegions[i].nLength); + } + } + + CPLDestroyXMLNode( psXMLRoot ); + + return poHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * psStatBuf, + int nFlags ) + +{ + VSIVirtualHandle *poFile = Open( pszFilename, "r" ); + + memset( psStatBuf, 0, sizeof(VSIStatBufL) ); + + if( poFile == NULL ) + return -1; + + poFile->Seek( 0, SEEK_END ); + size_t nLength = (size_t) poFile->Tell(); + delete poFile; + + int nResult = VSIStatExL( pszFilename + strlen("/vsisparse/"), + psStatBuf, nFlags ); + + psStatBuf->st_size = nLength; + + return nResult; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Unlink( const char * pszFilename ) + +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Mkdir( const char * pszPathname, + long nMode ) + +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Rmdir( const char * pszPathname ) + +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSISparseFileFilesystemHandler::ReadDir( const char *pszPath ) + +{ + errno = EACCES; + return NULL; +} + +/************************************************************************/ +/* VSIInstallSparseFileFilesystemHandler() */ +/************************************************************************/ + +/** + * Install /vsisparse/ virtual file handler. + * + * The sparse virtual file handler allows a virtual file to be composed + * from chunks of data in other files, potentially with large spaces in + * the virtual file set to a constant value. This can make it possible to + * test some sorts of operations on what seems to be a large file with + * image data set to a constant value. It is also helpful when wanting to + * add test files to the test suite that are too large, but for which most + * of the data can be ignored. It could, in theory, also be used to + * treat several files on different file systems as one large virtual file. + * + * The file referenced by /vsisparse/ should be an XML control file + * formatted something like: + * + * +\verbatim + + 87629264 + Stuff at start of file. + 251_head.dat + 0 + 0 + 2768 + + + RasterDMS node. + 251_rasterdms.dat + 87313104 + 0 + 160 + + + Stuff at end of file. + 251_tail.dat + 87611924 + 0 + 17340 + + + Default for the rest of the file. + 0 + 87629264 + 0 + + +\endverbatim + * + * Hopefully the values and semantics are fairly obvious. + * + * This driver is installed by default. + */ + +void VSIInstallSparseFileHandler() +{ + VSIFileManager::InstallHandler( "/vsisparse/", + new VSISparseFileFilesystemHandler ); +} + diff --git a/cpl/cpl_vsil_stdin.cpp b/cpl/cpl_vsil_stdin.cpp new file mode 100644 index 0000000..5390a97 --- /dev/null +++ b/cpl/cpl_vsil_stdin.cpp @@ -0,0 +1,394 @@ +/********************************************************************** + * $Id: cpl_vsil_stdin.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for stdin + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_vsi_virtual.h" + +#include +#ifdef WIN32 +#include +#include +#endif + +CPL_CVSID("$Id: cpl_vsil_stdin.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/* We buffer the first 1MB of standard input to enable drivers */ +/* to autodetect data. In the first MB, backward and forward seeking */ +/* is allowed, after only forward seeking will work */ +#define BUFFER_SIZE (1024 * 1024) + +static GByte* pabyBuffer; +static GUInt32 nBufferLen; +static GUIntBig nRealPos; + +/************************************************************************/ +/* VSIStdinInit() */ +/************************************************************************/ + +static void VSIStdinInit() +{ + if (pabyBuffer == NULL) + { +#ifdef WIN32 + setmode( fileno( stdin ), O_BINARY ); +#endif + pabyBuffer = (GByte*)CPLMalloc(BUFFER_SIZE); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdinFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdinFilesystemHandler : public VSIFilesystemHandler +{ +public: + VSIStdinFilesystemHandler(); + virtual ~VSIStdinFilesystemHandler(); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, + VSIStatBufL *pStatBuf, int nFlags ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdinHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdinHandle : public VSIVirtualHandle +{ + private: + GUIntBig nCurOff; + int ReadAndCache( void* pBuffer, int nToRead ); + + public: + VSIStdinHandle(); + virtual ~VSIStdinHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSIStdinHandle() */ +/************************************************************************/ + +VSIStdinHandle::VSIStdinHandle() +{ + nCurOff = 0; +} + +/************************************************************************/ +/* ~VSIStdinHandle() */ +/************************************************************************/ + +VSIStdinHandle::~VSIStdinHandle() +{ +} + + +/************************************************************************/ +/* ReadAndCache() */ +/************************************************************************/ + +int VSIStdinHandle::ReadAndCache( void* pBuffer, int nToRead ) +{ + CPLAssert(nCurOff == nRealPos); + + int nRead = fread(pBuffer, 1, nToRead, stdin); + + if (nRealPos < BUFFER_SIZE) + { + int nToCopy = MIN(BUFFER_SIZE - (int)nRealPos, nRead); + memcpy(pabyBuffer + nRealPos, pBuffer, nToCopy); + nBufferLen += nToCopy; + } + + nCurOff += nRead; + nRealPos = nCurOff; + + return nRead; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIStdinHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + if (nWhence == SEEK_SET && nOffset == nCurOff) + return 0; + + VSIStdinInit(); + if (nBufferLen == 0) + nRealPos = nBufferLen = fread(pabyBuffer, 1, BUFFER_SIZE, stdin); + + if (nWhence == SEEK_END) + { + if (nOffset != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Seek(xx != 0, SEEK_END) unsupported on /vsistdin"); + return -1; + } + + if (nBufferLen < BUFFER_SIZE) + { + nCurOff = nBufferLen; + return 0; + } + + CPLError(CE_Failure, CPLE_NotSupported, + "Seek(SEEK_END) unsupported on /vsistdin when stdin > 1 MB"); + return -1; + } + + if (nWhence == SEEK_CUR) + nOffset += nCurOff; + + if (nRealPos > nBufferLen && nOffset < nRealPos) + { + CPLError(CE_Failure, CPLE_NotSupported, + "backward Seek() unsupported on /vsistdin above first MB"); + return -1; + } + + if (nOffset < nBufferLen) + { + nCurOff = nOffset; + return 0; + } + + if (nOffset == nCurOff) + return 0; + + CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB, + nCurOff, nOffset); + + char abyTemp[8192]; + nCurOff = nRealPos; + while(TRUE) + { + int nToRead = (int) MIN(8192, nOffset - nCurOff); + int nRead = ReadAndCache( abyTemp, nToRead ); + + if (nRead < nToRead) + return -1; + if (nToRead < 8192) + break; + } + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIStdinHandle::Tell() +{ + return nCurOff; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIStdinHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + VSIStdinInit(); + + if (nCurOff < nBufferLen) + { + if (nCurOff + nSize * nCount < nBufferLen) + { + memcpy(pBuffer, pabyBuffer + nCurOff, nSize * nCount); + nCurOff += nSize * nCount; + return nCount; + } + + int nAlreadyCached = (int)(nBufferLen - nCurOff); + memcpy(pBuffer, pabyBuffer + nCurOff, nAlreadyCached); + + nCurOff += nAlreadyCached; + + int nRead = ReadAndCache( (GByte*)pBuffer + nAlreadyCached, + (int)(nSize*nCount - nAlreadyCached) ); + + return ((nRead + nAlreadyCached) / nSize); + } + + int nRead = ReadAndCache( pBuffer, (int)(nSize * nCount) ); + return nRead / nSize; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIStdinHandle::Write( const void * pBuffer, size_t nSize, + size_t nCount ) + +{ + CPLError(CE_Failure, CPLE_NotSupported, + "Write() unsupported on /vsistdin"); + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIStdinHandle::Eof() + +{ + if (nCurOff < nBufferLen) + return FALSE; + return feof(stdin); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIStdinHandle::Close() + +{ + return 0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdinFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSIStdinFilesystemHandler() */ +/************************************************************************/ + +VSIStdinFilesystemHandler::VSIStdinFilesystemHandler() +{ + pabyBuffer = NULL; + nBufferLen = 0; + nRealPos = 0; +} + +/************************************************************************/ +/* ~VSIStdinFilesystemHandler() */ +/************************************************************************/ + +VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler() +{ + CPLFree(pabyBuffer); + pabyBuffer = NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIStdinFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + if (strcmp(pszFilename, "/vsistdin/") != 0) + return NULL; + + if ( strchr(pszAccess, 'w') != NULL || + strchr(pszAccess, '+') != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Write or update mode not supported on /vsistdin"); + return NULL; + } + + return new VSIStdinHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIStdinFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * pStatBuf, + int nFlags ) + +{ + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + if (strcmp(pszFilename, "/vsistdin/") != 0) + return -1; + + if ((nFlags & VSI_STAT_SIZE_FLAG)) + { + VSIStdinInit(); + if (nBufferLen == 0) + nRealPos = nBufferLen = fread(pabyBuffer, 1, BUFFER_SIZE, stdin); + + pStatBuf->st_size = nBufferLen; + } + + pStatBuf->st_mode = S_IFREG; + return 0; +} + +/************************************************************************/ +/* VSIInstallStdinHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsistdin/ file system handler + * + * A special file handler is installed that allows reading from the standard + * input steam. + * + * The file operations available are of course limited to Read() and + * forward Seek() (full seek in the first MB of a file). + * + * @since GDAL 1.8.0 + */ +void VSIInstallStdinHandler() + +{ + VSIFileManager::InstallHandler( "/vsistdin/", new VSIStdinFilesystemHandler ); +} diff --git a/cpl/cpl_vsil_stdout.cpp b/cpl/cpl_vsil_stdout.cpp index 894617a..45e6059 100644 --- a/cpl/cpl_vsil_stdout.cpp +++ b/cpl/cpl_vsil_stdout.cpp @@ -1,12 +1,12 @@ /********************************************************************** - * $Id: cpl_vsil_stdout.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $ + * $Id: cpl_vsil_stdout.cpp 27426 2014-05-30 21:13:32Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Implement VSI large file api for stdout * Author: Even Rouault, * ********************************************************************** - * Copyright (c) 2010, Even Rouault + * Copyright (c) 2010-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -32,8 +32,34 @@ #include "cpl_vsi_virtual.h" #include +#ifdef WIN32 +#include +#include +#endif + +CPL_CVSID("$Id: cpl_vsil_stdout.cpp 27426 2014-05-30 21:13:32Z rouault $"); + +static VSIWriteFunction pWriteFunction = fwrite; +static FILE* pWriteStream = stdout; + +/************************************************************************/ +/* VSIStdoutSetRedirection() */ +/************************************************************************/ + +/** Set an alternative write function and output file handle instead of + * fwrite() / stdout. + * + * @param pFct Function with same signature as fwrite() + * @param stream File handle on which to output. Passed to pFct. + * + * @since GDAL 2.0 + */ +void VSIStdoutSetRedirection( VSIWriteFunction pFct, FILE* stream ) +{ + pWriteFunction = pFct; + pWriteStream = stream; +} -CPL_CVSID("$Id: cpl_vsil_stdout.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $"); /************************************************************************/ /* ==================================================================== */ @@ -46,7 +72,7 @@ class VSIStdoutFilesystemHandler : public VSIFilesystemHandler public: virtual VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess); - virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf ); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); }; /************************************************************************/ @@ -57,7 +83,10 @@ class VSIStdoutFilesystemHandler : public VSIFilesystemHandler class VSIStdoutHandle : public VSIVirtualHandle { + vsi_l_offset nOffset; + public: + VSIStdoutHandle() : nOffset(0) {} virtual int Seek( vsi_l_offset nOffset, int nWhence ); virtual vsi_l_offset Tell(); @@ -75,6 +104,10 @@ class VSIStdoutHandle : public VSIVirtualHandle int VSIStdoutHandle::Seek( vsi_l_offset nOffset, int nWhence ) { + if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) ) + return 0; + if( nWhence == SEEK_SET && nOffset == Tell() ) + return 0; CPLError(CE_Failure, CPLE_NotSupported, "Seek() unsupported on /vsistdout"); return -1; } @@ -85,7 +118,7 @@ int VSIStdoutHandle::Seek( vsi_l_offset nOffset, int nWhence ) vsi_l_offset VSIStdoutHandle::Tell() { - return ftell(stdout); + return nOffset; } /************************************************************************/ @@ -95,7 +128,10 @@ vsi_l_offset VSIStdoutHandle::Tell() int VSIStdoutHandle::Flush() { - return fflush( stdout ); + if( pWriteStream == stdout ) + return fflush( stdout ); + else + return 0; } /************************************************************************/ @@ -117,7 +153,9 @@ size_t VSIStdoutHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) { - return fwrite(pBuffer, nSize, nCount, stdout); + size_t nRet = pWriteFunction(pBuffer, nSize, nCount, pWriteStream); + nOffset += nSize * nRet; + return nRet; } /************************************************************************/ @@ -127,7 +165,7 @@ size_t VSIStdoutHandle::Write( const void * pBuffer, size_t nSize, int VSIStdoutHandle::Eof() { - return feof(stdout); + return 0; } /************************************************************************/ @@ -163,6 +201,11 @@ VSIStdoutFilesystemHandler::Open( const char *pszFilename, return NULL; } +#ifdef WIN32 + if ( strchr(pszAccess, 'b') != NULL ) + setmode( fileno( stdout ), O_BINARY ); +#endif + return new VSIStdoutHandle; } @@ -171,9 +214,185 @@ VSIStdoutFilesystemHandler::Open( const char *pszFilename, /************************************************************************/ int VSIStdoutFilesystemHandler::Stat( const char * pszFilename, - VSIStatBufL * pStatBuf ) + VSIStatBufL * pStatBuf, + int nFlags ) + +{ + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + return -1; +} + + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutRedirectFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdoutRedirectFilesystemHandler : public VSIFilesystemHandler +{ +public: + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutRedirectHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdoutRedirectHandle : public VSIVirtualHandle +{ + VSIVirtualHandle* poHandle; + public: + VSIStdoutRedirectHandle(VSIVirtualHandle* poHandle); + ~VSIStdoutRedirectHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSIStdoutRedirectHandle() */ +/************************************************************************/ + +VSIStdoutRedirectHandle::VSIStdoutRedirectHandle(VSIVirtualHandle* poHandle) +{ + this->poHandle = poHandle; +} + +/************************************************************************/ +/* ~VSIStdoutRedirectHandle() */ +/************************************************************************/ + +VSIStdoutRedirectHandle::~VSIStdoutRedirectHandle() +{ + delete poHandle; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + CPLError(CE_Failure, CPLE_NotSupported, "Seek() unsupported on /vsistdout_redirect"); + return -1; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIStdoutRedirectHandle::Tell() +{ + return poHandle->Tell(); +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Flush() + +{ + return poHandle->Flush(); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIStdoutRedirectHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + CPLError(CE_Failure, CPLE_NotSupported, "Read() unsupported on /vsistdout_redirect"); + return 0; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIStdoutRedirectHandle::Write( const void * pBuffer, size_t nSize, + size_t nCount ) { + return poHandle->Write(pBuffer, nSize, nCount); +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Eof() + +{ + return poHandle->Eof(); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Close() + +{ + return poHandle->Close(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutRedirectFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIStdoutRedirectFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + if ( strchr(pszAccess, 'r') != NULL || + strchr(pszAccess, '+') != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Read or update mode not supported on /vsistdout_redirect"); + return NULL; + } + + VSIVirtualHandle* poHandle = (VSIVirtualHandle* )VSIFOpenL( + pszFilename + strlen("/vsistdout_redirect/"), pszAccess); + if (poHandle == NULL) + return NULL; + + return new VSIStdoutRedirectHandle(poHandle); +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIStdoutRedirectFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * pStatBuf, + int nFlags ) + +{ + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + return -1; } @@ -181,8 +400,20 @@ int VSIStdoutFilesystemHandler::Stat( const char * pszFilename, /* VSIInstallStdoutHandler() */ /************************************************************************/ +/** + * \brief Install /vsistdout/ file system handler + * + * A special file handler is installed that allows writing to the standard + * output stream. + * + * The file operations available are of course limited to Write(). + * + * @since GDAL 1.8.0 + */ + void VSIInstallStdoutHandler() { VSIFileManager::InstallHandler( "/vsistdout/", new VSIStdoutFilesystemHandler ); + VSIFileManager::InstallHandler( "/vsistdout_redirect/", new VSIStdoutRedirectFilesystemHandler ); } diff --git a/cpl/cpl_vsil_subfile.cpp b/cpl/cpl_vsil_subfile.cpp index d84d6bf..db488de 100644 --- a/cpl/cpl_vsil_subfile.cpp +++ b/cpl/cpl_vsil_subfile.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_vsil_subfile.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $ + * $Id: cpl_vsil_subfile.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: VSI Virtual File System * Purpose: Implementation of subfile virtual IO functions. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -36,7 +37,7 @@ # include #endif -CPL_CVSID("$Id: cpl_vsil_subfile.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $"); +CPL_CVSID("$Id: cpl_vsil_subfile.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* ==================================================================== */ @@ -47,10 +48,12 @@ CPL_CVSID("$Id: cpl_vsil_subfile.cpp,v 1.1 2010-07-08 19:29:36 aboudreault Exp $ class VSISubFileHandle : public VSIVirtualHandle { public: - FILE *fp; + VSILFILE *fp; vsi_l_offset nSubregionOffset; vsi_l_offset nSubregionSize; - int bUpdate; + int bAtEOF; + + VSISubFileHandle() : fp(NULL), nSubregionOffset(0), nSubregionSize(0), bAtEOF(FALSE) {} virtual int Seek( vsi_l_offset nOffset, int nWhence ); virtual vsi_l_offset Tell(); @@ -79,7 +82,7 @@ class VSISubFileFilesystemHandler : public VSIFilesystemHandler virtual VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess); - virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf ); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); virtual int Unlink( const char *pszFilename ); virtual int Mkdir( const char *pszDirname, long nMode ); virtual int Rmdir( const char *pszDirname ); @@ -112,6 +115,8 @@ int VSISubFileHandle::Close() int VSISubFileHandle::Seek( vsi_l_offset nOffset, int nWhence ) { + bAtEOF = FALSE; + if( nWhence == SEEK_SET ) nOffset += nSubregionOffset; else if( nWhence == SEEK_CUR ) @@ -152,7 +157,33 @@ vsi_l_offset VSISubFileHandle::Tell() size_t VSISubFileHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) { - return VSIFReadL( pBuffer, nSize, nCount, fp ); + size_t nRet; + if (nSubregionSize == 0) + nRet = VSIFReadL( pBuffer, nSize, nCount, fp ); + else + { + if (nSize == 0) + return 0; + + vsi_l_offset nCurOffset = VSIFTellL(fp); + if (nCurOffset >= nSubregionOffset + nSubregionSize) + { + bAtEOF = TRUE; + return 0; + } + + size_t nByteToRead = nSize * nCount; + if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize) + { + int nRead = (int)VSIFReadL( pBuffer, 1, (size_t)(nSubregionOffset + nSubregionSize - nCurOffset), fp); + nRet = nRead / nSize; + } + else + nRet = VSIFReadL( pBuffer, nSize, nCount, fp ); + } + if( nRet < nCount ) + bAtEOF = TRUE; + return nRet; } /************************************************************************/ @@ -162,7 +193,26 @@ size_t VSISubFileHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) size_t VSISubFileHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) { - return VSIFWriteL( pBuffer, nSize, nCount, fp ); + bAtEOF = FALSE; + + if (nSubregionSize == 0) + return VSIFWriteL( pBuffer, nSize, nCount, fp ); + + if (nSize == 0) + return 0; + + vsi_l_offset nCurOffset = VSIFTellL(fp); + if (nCurOffset >= nSubregionOffset + nSubregionSize) + return 0; + + size_t nByteToWrite = nSize * nCount; + if (nCurOffset + nByteToWrite > nSubregionOffset + nSubregionSize) + { + int nWritten = (int)VSIFWriteL( pBuffer, 1, (size_t)(nSubregionOffset + nSubregionSize - nCurOffset), fp); + return nWritten / nSize; + } + else + return VSIFWriteL( pBuffer, nSize, nCount, fp ); } /************************************************************************/ @@ -172,7 +222,7 @@ size_t VSISubFileHandle::Write( const void * pBuffer, size_t nSize, size_t nCoun int VSISubFileHandle::Eof() { - return VSIFEofL( fp ); + return bAtEOF; } /************************************************************************/ @@ -226,7 +276,15 @@ VSISubFileFilesystemHandler::DecomposePath( const char *pszPath, for( i = 12; pszPath[i] != '\0'; i++ ) { if( pszPath[i] == '_' && nSubFileSize == 0 ) - nSubFileSize = CPLScanUIntBig(pszPath + i + 1, strlen(pszPath + i + 1)); + { + /* -1 is sometimes passed to mean that we don't know the file size */ + /* for example when creating a JPEG2000 datastream in a NITF file */ + /* Transform it into 0 for correct behaviour of Read(), Write() and Eof() */ + if (pszPath[i + 1] == '-') + nSubFileSize = 0; + else + nSubFileSize = CPLScanUIntBig(pszPath + i + 1, strlen(pszPath + i + 1)); + } else if( pszPath[i] == ',' ) { osFilename = pszPath + i + 1; @@ -261,10 +319,17 @@ VSISubFileFilesystemHandler::Open( const char *pszFilename, return NULL; } +/* -------------------------------------------------------------------- */ +/* We can't open the containing file with "w" access, so if tht */ +/* is requested use "r+" instead to update in place. */ +/* -------------------------------------------------------------------- */ + if( pszAccess[0] == 'w' ) + pszAccess = "r+"; + /* -------------------------------------------------------------------- */ /* Open the underlying file. */ /* -------------------------------------------------------------------- */ - FILE *fp = VSIFOpenL( osSubFilePath, pszAccess ); + VSILFILE *fp = VSIFOpenL( osSubFilePath, pszAccess ); if( fp == NULL ) return NULL; @@ -288,26 +353,29 @@ VSISubFileFilesystemHandler::Open( const char *pszFilename, /************************************************************************/ int VSISubFileFilesystemHandler::Stat( const char * pszFilename, - VSIStatBufL * psStatBuf ) + VSIStatBufL * psStatBuf, + int nFlags ) { CPLString osSubFilePath; vsi_l_offset nOff, nSize; + memset( psStatBuf, 0, sizeof(VSIStatBufL) ); + if( !DecomposePath( pszFilename, osSubFilePath, nOff, nSize ) ) { errno = ENOENT; return -1; } - int nResult = VSIStatL( osSubFilePath, psStatBuf ); + int nResult = VSIStatExL( osSubFilePath, psStatBuf, nFlags ); if( nResult == 0 ) { if( nSize != 0 ) - psStatBuf->st_size = (long)nSize; + psStatBuf->st_size = nSize; else - psStatBuf->st_size -= (long)nOff; + psStatBuf->st_size -= nOff; } return nResult; diff --git a/cpl/cpl_vsil_tar.cpp b/cpl/cpl_vsil_tar.cpp new file mode 100644 index 0000000..a0bb0b5 --- /dev/null +++ b/cpl/cpl_vsil_tar.cpp @@ -0,0 +1,337 @@ +/****************************************************************************** + * $Id: cpl_vsil_tar.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for tar files (.tar). + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" + +CPL_CVSID("$Id: cpl_vsil_tar.cpp 27044 2014-03-16 23:41:27Z rouault $"); + + +/************************************************************************/ +/* ==================================================================== */ +/* VSITarEntryFileOffset */ +/* ==================================================================== */ +/************************************************************************/ + +class VSITarEntryFileOffset : public VSIArchiveEntryFileOffset +{ +public: + GUIntBig nOffset; + + VSITarEntryFileOffset(GUIntBig nOffset) + { + this->nOffset = nOffset; + } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSITarReader */ +/* ==================================================================== */ +/************************************************************************/ + +class VSITarReader : public VSIArchiveReader +{ + private: + VSILFILE* fp; + GUIntBig nCurOffset; + GUIntBig nNextFileSize; + CPLString osNextFileName; + GIntBig nModifiedTime; + + public: + VSITarReader(const char* pszTarFileName); + virtual ~VSITarReader(); + + int IsValid() { return fp != NULL; } + + virtual int GotoFirstFile(); + virtual int GotoNextFile(); + virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSITarEntryFileOffset(nCurOffset); } + virtual GUIntBig GetFileSize() { return nNextFileSize; } + virtual CPLString GetFileName() { return osNextFileName; } + virtual GIntBig GetModifiedTime() { return nModifiedTime; } + virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset); +}; + + +/************************************************************************/ +/* VSIIsTGZ() */ +/************************************************************************/ + +static int VSIIsTGZ(const char* pszFilename) +{ + return (!EQUALN(pszFilename, "/vsigzip/", 9) && + ((strlen(pszFilename) > 4 && + EQUALN(pszFilename + strlen(pszFilename) - 4, ".tgz", 4)) || + (strlen(pszFilename) > 7 && + EQUALN(pszFilename + strlen(pszFilename) - 7, ".tar.gz", 7)))); +} + +/************************************************************************/ +/* VSITarReader() */ +/************************************************************************/ + +VSITarReader::VSITarReader(const char* pszTarFileName) +{ + fp = VSIFOpenL(pszTarFileName, "rb"); + nNextFileSize = 0; + nCurOffset = 0; + nModifiedTime = 0; +} + +/************************************************************************/ +/* ~VSITarReader() */ +/************************************************************************/ + +VSITarReader::~VSITarReader() +{ + if (fp) + VSIFCloseL(fp); +} + +/************************************************************************/ +/* GotoNextFile() */ +/************************************************************************/ + +int VSITarReader::GotoNextFile() +{ + char abyHeader[512]; + if (VSIFReadL(abyHeader, 512, 1, fp) != 1) + return FALSE; + + if (abyHeader[99] != '\0' || + abyHeader[107] != '\0' || + abyHeader[115] != '\0' || + abyHeader[123] != '\0' || + (abyHeader[135] != '\0' && abyHeader[135] != ' ') || + (abyHeader[147] != '\0' && abyHeader[147] != ' ') || + abyHeader[154] != '\0' || + abyHeader[155] != ' ') + { + return FALSE; + } + + osNextFileName = abyHeader; + nNextFileSize = 0; + int i; + for(i=0;i<11;i++) + nNextFileSize = nNextFileSize * 8 + (abyHeader[124+i] - '0'); + + nModifiedTime = 0; + for(i=0;i<11;i++) + nModifiedTime = nModifiedTime * 8 + (abyHeader[136+i] - '0'); + + nCurOffset = VSIFTellL(fp); + + GUIntBig nBytesToSkip = ((nNextFileSize + 511) / 512) * 512; + if( nBytesToSkip > (~((GUIntBig)0)) - nCurOffset ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad .tar structure"); + return FALSE; + } + + VSIFSeekL(fp, nBytesToSkip, SEEK_CUR); + + return TRUE; +} + +/************************************************************************/ +/* GotoFirstFile() */ +/************************************************************************/ + +int VSITarReader::GotoFirstFile() +{ + VSIFSeekL(fp, 0, SEEK_SET); + return GotoNextFile(); +} + +/************************************************************************/ +/* GotoFileOffset() */ +/************************************************************************/ + +int VSITarReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset) +{ + VSITarEntryFileOffset* pTarEntryOffset = (VSITarEntryFileOffset*)pOffset; + VSIFSeekL(fp, pTarEntryOffset->nOffset - 512, SEEK_SET); + return GotoNextFile(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSITarFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSITarFilesystemHandler : public VSIArchiveFilesystemHandler +{ +public: + virtual const char* GetPrefix() { return "/vsitar"; } + virtual std::vector GetExtensions(); + virtual VSIArchiveReader* CreateReader(const char* pszTarFileName); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); +}; + + +/************************************************************************/ +/* GetExtensions() */ +/************************************************************************/ + +std::vector VSITarFilesystemHandler::GetExtensions() +{ + std::vector oList; + oList.push_back(".tar.gz"); + oList.push_back(".tar"); + oList.push_back(".tgz"); + return oList; +} + +/************************************************************************/ +/* CreateReader() */ +/************************************************************************/ + +VSIArchiveReader* VSITarFilesystemHandler::CreateReader(const char* pszTarFileName) +{ + CPLString osTarInFileName; + + if (VSIIsTGZ(pszTarFileName)) + { + osTarInFileName = "/vsigzip/"; + osTarInFileName += pszTarFileName; + } + else + osTarInFileName = pszTarFileName; + + VSITarReader* poReader = new VSITarReader(osTarInFileName); + + if (!poReader->IsValid()) + { + delete poReader; + return NULL; + } + + if (!poReader->GotoFirstFile()) + { + delete poReader; + return NULL; + } + + return poReader; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle* VSITarFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess) +{ + char* tarFilename; + CPLString osTarInFileName; + + if (strchr(pszAccess, 'w') != NULL || + strchr(pszAccess, '+') != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Only read-only mode is supported for /vsitar"); + return NULL; + } + + tarFilename = SplitFilename(pszFilename, osTarInFileName, TRUE); + if (tarFilename == NULL) + return NULL; + + VSIArchiveReader* poReader = OpenArchiveFile(tarFilename, osTarInFileName); + if (poReader == NULL) + { + CPLFree(tarFilename); + return NULL; + } + + CPLString osSubFileName("/vsisubfile/"); + VSITarEntryFileOffset* pOffset = (VSITarEntryFileOffset*) poReader->GetFileOffset(); + osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, pOffset->nOffset); + osSubFileName += "_"; + osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, poReader->GetFileSize()); + osSubFileName += ","; + delete pOffset; + + if (VSIIsTGZ(tarFilename)) + { + osSubFileName += "/vsigzip/"; + osSubFileName += tarFilename; + } + else + osSubFileName += tarFilename; + + delete(poReader); + + CPLFree(tarFilename); + tarFilename = NULL; + + return (VSIVirtualHandle* )VSIFOpenL(osSubFileName, "rb"); +} + +/************************************************************************/ +/* VSIInstallTarFileHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsitar/ file system handler. + * + * A special file handler is installed that allows reading on-the-fly in TAR + * (regular .tar, or compressed .tar.gz/.tgz) archives. + * + * All portions of the file system underneath the base path "/vsitar/" will be + * handled by this driver. + * + * The syntax to open a file inside a zip file is /vsitar/path/to/the/file.tar/path/inside/the/tar/file + * were path/to/the/file.tar is relative or absolute and path/inside/the/tar/file + * is the relative path to the file inside the archive. + * + * If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows), + * so the line looks like /vsitar//home/gdal/... + * For example gdalinfo /vsitar/myarchive.tar/subdir1/file1.tif + * + * Syntaxic sugar : if the tar archive contains only one file located at its root, + * just mentionning "/vsitar/path/to/the/file.tar" will work + * + * VSIStatL() will return the uncompressed size in st_size member and file + * nature- file or directory - in st_mode member. + * + * Directory listing is available through VSIReadDir(). + * + * @since GDAL 1.8.0 + */ + +void VSIInstallTarFileHandler(void) +{ + VSIFileManager::InstallHandler( "/vsitar/", new VSITarFilesystemHandler() ); +} diff --git a/cpl/cpl_vsil_unix_stdio_64.cpp b/cpl/cpl_vsil_unix_stdio_64.cpp index a0530b5..27cf938 100644 --- a/cpl/cpl_vsil_unix_stdio_64.cpp +++ b/cpl/cpl_vsil_unix_stdio_64.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_vsil_unix_stdio_64.cpp 17402 2009-07-16 20:11:00Z warmerdam $ + * $Id: cpl_vsil_unix_stdio_64.cpp 27492 2014-07-06 10:21:05Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Implement VSI large file api for Unix platforms with fseek64() @@ -8,6 +8,7 @@ * ********************************************************************** * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -35,12 +36,15 @@ * ****************************************************************************/ +//#define VSI_COUNT_BYTES_READ + #include "cpl_port.h" #if !defined(WIN32) && !defined(WIN32CE) #include "cpl_vsi_virtual.h" #include "cpl_string.h" +#include "cpl_multiproc.h" #include #include @@ -48,7 +52,7 @@ #include #include -CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp 17402 2009-07-16 20:11:00Z warmerdam $"); +CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp 27492 2014-07-06 10:21:05Z rouault $"); #if defined(UNIX_STDIO_64) @@ -67,6 +71,9 @@ CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp 17402 2009-07-16 20:11:00Z warmerdam #ifndef VSI_STAT64_T #define VSI_STAT64_T stat64 #endif +#ifndef VSI_FTRUNCATE64 +#define VSI_FTRUNCATE64 ftruncate64 +#endif #else /* not UNIX_STDIO_64 */ @@ -85,6 +92,9 @@ CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp 17402 2009-07-16 20:11:00Z warmerdam #ifndef VSI_STAT64_T #define VSI_STAT64_T stat #endif +#ifndef VSI_FTRUNCATE64 +#define VSI_FTRUNCATE64 ftruncate +#endif #endif /* ndef UNIX_STDIO_64 */ @@ -96,15 +106,29 @@ CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp 17402 2009-07-16 20:11:00Z warmerdam class VSIUnixStdioFilesystemHandler : public VSIFilesystemHandler { +#ifdef VSI_COUNT_BYTES_READ + vsi_l_offset nTotalBytesRead; + void *hMutex; +#endif + public: + VSIUnixStdioFilesystemHandler(); +#ifdef VSI_COUNT_BYTES_READ + virtual ~VSIUnixStdioFilesystemHandler(); +#endif + virtual VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess); - virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf ); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); virtual int Unlink( const char *pszFilename ); virtual int Rename( const char *oldpath, const char *newpath ); virtual int Mkdir( const char *pszDirname, long nMode ); virtual int Rmdir( const char *pszDirname ); virtual char **ReadDir( const char *pszDirname ); + +#ifdef VSI_COUNT_BYTES_READ + void AddToTotal(vsi_l_offset nBytes); +#endif }; /************************************************************************/ @@ -115,12 +139,19 @@ class VSIUnixStdioFilesystemHandler : public VSIFilesystemHandler class VSIUnixStdioHandle : public VSIVirtualHandle { - public: FILE *fp; vsi_l_offset nOffset; + int bReadOnly; int bLastOpWrite; int bLastOpRead; int bAtEOF; +#ifdef VSI_COUNT_BYTES_READ + vsi_l_offset nTotalBytesRead; + VSIUnixStdioFilesystemHandler *poFS; +#endif + public: + VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, + FILE* fpIn, int bReadOnlyIn); virtual int Seek( vsi_l_offset nOffset, int nWhence ); virtual vsi_l_offset Tell(); @@ -129,8 +160,24 @@ class VSIUnixStdioHandle : public VSIVirtualHandle virtual int Eof(); virtual int Flush(); virtual int Close(); + virtual int Truncate( vsi_l_offset nNewSize ); + virtual void *GetNativeFileDescriptor() { return (void*) (size_t) fileno(fp); } }; + +/************************************************************************/ +/* VSIUnixStdioHandle() */ +/************************************************************************/ + +VSIUnixStdioHandle::VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, + FILE* fpIn, int bReadOnlyIn) : + fp(fpIn), nOffset(0), bReadOnly(bReadOnlyIn), bLastOpWrite(FALSE), bLastOpRead(FALSE), bAtEOF(FALSE) +#ifdef VSI_COUNT_BYTES_READ + , nTotalBytesRead(0), poFS(poFSIn) +#endif +{ +} + /************************************************************************/ /* Close() */ /************************************************************************/ @@ -140,6 +187,10 @@ int VSIUnixStdioHandle::Close() { VSIDebug1( "VSIUnixStdioHandle::Close(%p)", fp ); +#ifdef VSI_COUNT_BYTES_READ + poFS->AddToTotal(nTotalBytesRead); +#endif + return fclose( fp ); } @@ -150,13 +201,32 @@ int VSIUnixStdioHandle::Close() int VSIUnixStdioHandle::Seek( vsi_l_offset nOffset, int nWhence ) { + GByte abyTemp[4096]; + + bAtEOF = FALSE; + // seeks that do nothing are still surprisingly expensive with MSVCRT. // try and short circuit if possible. if( nWhence == SEEK_SET && nOffset == this->nOffset ) return 0; - if( nWhence == SEEK_END && nOffset == 0 && bAtEOF ) - return 0; + // on a read-only file, we can avoid a lseek() system call to be issued + // if the next position to seek to is within the buffered page + if( bReadOnly && nWhence == SEEK_SET ) + { + GIntBig nDiff = (GIntBig)nOffset - (GIntBig)this->nOffset; + if( nDiff > 0 && nDiff < 4096 ) + { + int nRead = (int)fread(abyTemp, 1, (int)nDiff, fp); + if( nRead == (int)nDiff ) + { + this->nOffset = nOffset; + bLastOpWrite = FALSE; + bLastOpRead = FALSE; + return 0; + } + } + } int nResult = VSI_FSEEK64( fp, nOffset, nWhence ); int nError = errno; @@ -165,22 +235,22 @@ int VSIUnixStdioHandle::Seek( vsi_l_offset nOffset, int nWhence ) if( nWhence == SEEK_SET ) { - VSIDebug3( "VSIUnixStdioHandle::Seek(%p,%d,SEEK_SET) = %d", + VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",SEEK_SET) = %d", fp, nOffset, nResult ); } else if( nWhence == SEEK_END ) { - VSIDebug3( "VSIUnixStdioHandle::Seek(%p,%d,SEEK_END) = %d", + VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",SEEK_END) = %d", fp, nOffset, nResult ); } else if( nWhence == SEEK_CUR ) { - VSIDebug3( "VSIUnixStdioHandle::Seek(%p,%d,SEEK_CUR) = %d", + VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",SEEK_CUR) = %d", fp, nOffset, nResult ); } else { - VSIDebug4( "VSIUnixStdioHandle::Seek(%p,%d,%d-Unknown) = %d", + VSIDebug4( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",%d-Unknown) = %d", fp, nOffset, nWhence, nResult ); } @@ -191,17 +261,14 @@ int VSIUnixStdioHandle::Seek( vsi_l_offset nOffset, int nWhence ) if( nWhence == SEEK_SET ) { this->nOffset = nOffset; - bAtEOF = FALSE; } else if( nWhence == SEEK_END ) { this->nOffset = VSI_FTELL64( fp ); - bAtEOF = TRUE; } else if( nWhence == SEEK_CUR ) { this->nOffset += nOffset; - bAtEOF = FALSE; } } @@ -274,10 +341,21 @@ size_t VSIUnixStdioHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) /* -------------------------------------------------------------------- */ /* Update current offset. */ /* -------------------------------------------------------------------- */ + +#ifdef VSI_COUNT_BYTES_READ + nTotalBytesRead += nSize * nResult; +#endif + nOffset += nSize * nResult; bLastOpWrite = FALSE; bLastOpRead = TRUE; - + + if (nResult != nCount) + { + nOffset = VSI_FTELL64( fp ); + bAtEOF = feof(fp); + } + return nResult; } @@ -327,21 +405,58 @@ size_t VSIUnixStdioHandle::Write( const void * pBuffer, size_t nSize, int VSIUnixStdioHandle::Eof() { - if( !bAtEOF ) - bAtEOF = feof(fp); - if( bAtEOF ) return 1; else return 0; } +/************************************************************************/ +/* Truncate() */ +/************************************************************************/ + +int VSIUnixStdioHandle::Truncate( vsi_l_offset nNewSize ) +{ + fflush(fp); + int nRet = VSI_FTRUNCATE64(fileno(fp), nNewSize); + return nRet; +} + + /************************************************************************/ /* ==================================================================== */ /* VSIUnixStdioFilesystemHandler */ /* ==================================================================== */ /************************************************************************/ +/************************************************************************/ +/* VSIUnixStdioFilesystemHandler() */ +/************************************************************************/ + +VSIUnixStdioFilesystemHandler::VSIUnixStdioFilesystemHandler() +#ifdef VSI_COUNT_BYTES_READ + : nTotalBytesRead(0), hMutex(NULL) +#endif +{ +} + +#ifdef VSI_COUNT_BYTES_READ +/************************************************************************/ +/* ~VSIUnixStdioFilesystemHandler() */ +/************************************************************************/ + +VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler() +{ + CPLDebug( "VSI", + "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB, + nTotalBytesRead ); + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} +#endif + /************************************************************************/ /* Open() */ /************************************************************************/ @@ -363,16 +478,24 @@ VSIUnixStdioFilesystemHandler::Open( const char *pszFilename, return NULL; } - VSIUnixStdioHandle *poHandle = new VSIUnixStdioHandle; - - poHandle->fp = fp; - poHandle->nOffset = 0; - poHandle->bLastOpWrite = FALSE; - poHandle->bLastOpRead = FALSE; - poHandle->bAtEOF = FALSE; + int bReadOnly = strcmp(pszAccess, "rb") == 0 || strcmp(pszAccess, "r") == 0; + VSIUnixStdioHandle *poHandle = new VSIUnixStdioHandle(this, fp, bReadOnly ); errno = nError; - return poHandle; + +/* -------------------------------------------------------------------- */ +/* If VSI_CACHE is set we want to use a cached reader instead */ +/* of more direct io on the underlying file. */ +/* -------------------------------------------------------------------- */ + if( bReadOnly + && CSLTestBoolean( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) ) + { + return VSICreateCachedFile( poHandle ); + } + else + { + return poHandle; + } } /************************************************************************/ @@ -380,7 +503,8 @@ VSIUnixStdioFilesystemHandler::Open( const char *pszFilename, /************************************************************************/ int VSIUnixStdioFilesystemHandler::Stat( const char * pszFilename, - VSIStatBufL * pStatBuf ) + VSIStatBufL * pStatBuf, + int nFlags) { return( VSI_STAT64( pszFilename, pStatBuf ) ); @@ -437,36 +561,18 @@ char **VSIUnixStdioFilesystemHandler::ReadDir( const char *pszPath ) { DIR *hDir; struct dirent *psDirEntry; - char **papszDir = NULL; + CPLStringList oDir; if (strlen(pszPath) == 0) pszPath = "."; if ( (hDir = opendir(pszPath)) != NULL ) { - /* In case of really big number of files in the directory, CSLAddString */ - /* can be slow (see #2158). We then directly build the list. */ - int nItems=0; - int nAllocatedItems=0; - while( (psDirEntry = readdir(hDir)) != NULL ) - { - if (nItems == 0) - { - papszDir = (char**) CPLCalloc(2,sizeof(char*)); - nAllocatedItems = 1; - } - else if (nItems >= nAllocatedItems) - { - nAllocatedItems = nAllocatedItems * 2; - papszDir = (char**)CPLRealloc(papszDir, - (nAllocatedItems+2)*sizeof(char*)); - } + // we want to avoid returning NULL for an empty list. + oDir.Assign( (char**) CPLCalloc(2,sizeof(char*)) ); - papszDir[nItems] = CPLStrdup(psDirEntry->d_name); - papszDir[nItems+1] = NULL; - - nItems++; - } + while( (psDirEntry = readdir(hDir)) != NULL ) + oDir.AddString( psDirEntry->d_name ); closedir( hDir ); } @@ -477,9 +583,22 @@ char **VSIUnixStdioFilesystemHandler::ReadDir( const char *pszPath ) */ } - return papszDir; + return oDir.StealList(); } +#ifdef VSI_COUNT_BYTES_READ +/************************************************************************/ +/* AddToTotal() */ +/************************************************************************/ + +void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes) +{ + CPLMutexHolder oHolder(&hMutex); + nTotalBytesRead += nBytes; +} + +#endif + /************************************************************************/ /* VSIInstallLargeFileHandler() */ /************************************************************************/ @@ -487,7 +606,7 @@ char **VSIUnixStdioFilesystemHandler::ReadDir( const char *pszPath ) void VSIInstallLargeFileHandler() { - VSIFileManager::InstallHandler( "", new VSIUnixStdioFilesystemHandler ); + VSIFileManager::InstallHandler( "", new VSIUnixStdioFilesystemHandler() ); } #endif /* ndef WIN32 */ diff --git a/cpl/cpl_vsil_win32.cpp b/cpl/cpl_vsil_win32.cpp index 0ad1dd3..1b6b5a4 100644 --- a/cpl/cpl_vsil_win32.cpp +++ b/cpl/cpl_vsil_win32.cpp @@ -1,5 +1,5 @@ /********************************************************************** - * $Id: cpl_vsil_win32.cpp 17269 2009-06-20 03:42:47Z warmerdam $ + * $Id: cpl_vsil_win32.cpp 27491 2014-07-05 11:24:48Z rouault $ * * Project: CPL - Common Portability Library * Purpose: Implement VSI large file api for Win32. @@ -7,6 +7,7 @@ * ********************************************************************** * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,7 +30,7 @@ #include "cpl_vsi_virtual.h" -CPL_CVSID("$Id: cpl_vsil_win32.cpp 17269 2009-06-20 03:42:47Z warmerdam $"); +CPL_CVSID("$Id: cpl_vsil_win32.cpp 27491 2014-07-05 11:24:48Z rouault $"); #if defined(WIN32) @@ -63,12 +64,14 @@ class VSIWin32FilesystemHandler : public VSIFilesystemHandler public: virtual VSIVirtualHandle *Open( const char *pszFilename, const char *pszAccess); - virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf ); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); virtual int Unlink( const char *pszFilename ); virtual int Rename( const char *oldpath, const char *newpath ); virtual int Mkdir( const char *pszDirname, long nMode ); virtual int Rmdir( const char *pszDirname ); virtual char **ReadDir( const char *pszDirname ); + virtual int IsCaseSensitive( const char* pszFilename ) + { (void) pszFilename; return FALSE; } }; /************************************************************************/ @@ -81,6 +84,7 @@ class VSIWin32Handle : public VSIVirtualHandle { public: HANDLE hFile; + int bEOF; virtual int Seek( vsi_l_offset nOffset, int nWhence ); virtual vsi_l_offset Tell(); @@ -89,6 +93,8 @@ class VSIWin32Handle : public VSIVirtualHandle virtual int Eof(); virtual int Flush(); virtual int Close(); + virtual int Truncate( vsi_l_offset nNewSize ); + virtual void *GetNativeFileDescriptor() { return (void*) hFile; } }; /************************************************************************/ @@ -197,6 +203,8 @@ int VSIWin32Handle::Seek( vsi_l_offset nOffset, int nWhence ) GUInt32 nMoveLow; LARGE_INTEGER li; + bEOF = FALSE; + switch(nWhence) { case SEEK_CUR: @@ -265,7 +273,9 @@ vsi_l_offset VSIWin32Handle::Tell() int VSIWin32Handle::Flush() { - FlushFileBuffers( hFile ); + /* Nothing needed to offer the same guarantee as POSIX fflush() */ + /* FlushFileBuffers() would be closer to fsync() */ + /* See http://trac.osgeo.org/gdal/ticket/5556 */ return 0; } @@ -289,6 +299,9 @@ size_t VSIWin32Handle::Read( void * pBuffer, size_t nSize, size_t nCount ) else nResult = dwSizeRead / nSize; + if( nResult != nCount ) + bEOF = TRUE; + return nResult; } @@ -323,14 +336,24 @@ size_t VSIWin32Handle::Write( const void *pBuffer, size_t nSize, size_t nCount) int VSIWin32Handle::Eof() { - vsi_l_offset nCur, nEnd; + return bEOF; +} + +/************************************************************************/ +/* Truncate() */ +/************************************************************************/ - nCur = Tell(); - Seek( 0, SEEK_END ); - nEnd = Tell(); +int VSIWin32Handle::Truncate( vsi_l_offset nNewSize ) +{ + vsi_l_offset nCur = Tell(); + Seek( nNewSize, SEEK_SET ); + BOOL bRes = SetEndOfFile( hFile ); Seek( nCur, SEEK_SET ); - return (nCur == nEnd); + if (bRes) + return 0; + else + return -1; } /************************************************************************/ @@ -350,24 +373,52 @@ VSIVirtualHandle *VSIWin32FilesystemHandler::Open( const char *pszFilename, DWORD dwDesiredAccess, dwCreationDisposition, dwFlagsAndAttributes; HANDLE hFile; - if( strchr(pszAccess, '+') != NULL || strchr(pszAccess, 'w') != 0 ) + if( strchr(pszAccess, '+') != NULL || + strchr(pszAccess, 'w') != 0 || + strchr(pszAccess, 'a') != 0 ) dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; else dwDesiredAccess = GENERIC_READ; if( strstr(pszAccess, "w") != NULL ) dwCreationDisposition = CREATE_ALWAYS; + else if( strstr(pszAccess, "a") != NULL ) + dwCreationDisposition = OPEN_ALWAYS; else dwCreationDisposition = OPEN_EXISTING; dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ) ? - FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL; +/* -------------------------------------------------------------------- */ +/* On Win32 consider treating the filename as utf-8 and */ +/* converting to wide characters to open. */ +/* -------------------------------------------------------------------- */ #ifndef WIN32CE - hFile = CreateFile( pszFilename, dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); -#else + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + hFile = CreateFileW( pwszFilename, dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, dwCreationDisposition, dwFlagsAndAttributes, + NULL ); + CPLFree( pwszFilename ); + } + else + { + hFile = CreateFile( pszFilename, dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, dwCreationDisposition, dwFlagsAndAttributes, + NULL ); + } + +/* -------------------------------------------------------------------- */ +/* On WinCE we only support plain ascii filenames. */ +/* -------------------------------------------------------------------- */ +#else /* def WIN32CE */ hFile = CE_CreateFileA( pszFilename, dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); @@ -378,12 +429,29 @@ VSIVirtualHandle *VSIWin32FilesystemHandler::Open( const char *pszFilename, errno = ErrnoFromGetLastError(); return NULL; } + +/* -------------------------------------------------------------------- */ +/* Create a VSI file handle. */ +/* -------------------------------------------------------------------- */ + VSIWin32Handle *poHandle = new VSIWin32Handle; + + poHandle->hFile = hFile; + poHandle->bEOF = FALSE; + + if (strchr(pszAccess, 'a') != 0) + poHandle->Seek(0, SEEK_END); + +/* -------------------------------------------------------------------- */ +/* If VSI_CACHE is set we want to use a cached reader instead */ +/* of more direct io on the underlying file. */ +/* -------------------------------------------------------------------- */ + if( (EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb")) + && CSLTestBoolean( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) ) + { + return VSICreateCachedFile( poHandle ); + } else { - VSIWin32Handle *poHandle = new VSIWin32Handle; - - poHandle->hFile = hFile; - return poHandle; } } @@ -393,20 +461,57 @@ VSIVirtualHandle *VSIWin32FilesystemHandler::Open( const char *pszFilename, /************************************************************************/ int VSIWin32FilesystemHandler::Stat( const char * pszFilename, - VSIStatBufL * pStatBuf ) + VSIStatBufL * pStatBuf, + int nFlags ) { - return( VSI_STAT64( pszFilename, pStatBuf ) ); + (void) nFlags; + +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wstat64( pwszFilename, pStatBuf ); + CPLFree( pwszFilename ); + + return nResult; + } + else +#endif + { + return( VSI_STAT64( pszFilename, pStatBuf ) ); + } } /************************************************************************/ + /* Unlink() */ /************************************************************************/ int VSIWin32FilesystemHandler::Unlink( const char * pszFilename ) { - return unlink( pszFilename ); +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wunlink( pwszFilename ); + CPLFree( pwszFilename ); + return nResult; + } + else +#endif + { + return unlink( pszFilename ); + } } /************************************************************************/ @@ -417,7 +522,26 @@ int VSIWin32FilesystemHandler::Rename( const char *oldpath, const char *newpath ) { - return rename( oldpath, newpath ); +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszOldPath = + CPLRecodeToWChar( oldpath, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + wchar_t *pwszNewPath = + CPLRecodeToWChar( newpath, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wrename( pwszOldPath, pwszNewPath ); + CPLFree( pwszOldPath ); + CPLFree( pwszNewPath ); + return nResult; + } + else +#endif + { + return rename( oldpath, newpath ); + } } /************************************************************************/ @@ -429,7 +553,23 @@ int VSIWin32FilesystemHandler::Mkdir( const char * pszPathname, { (void) nMode; - return mkdir( pszPathname ); +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszPathname, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wmkdir( pwszFilename ); + CPLFree( pwszFilename ); + return nResult; + } + else +#endif + { + return mkdir( pszPathname ); + } } /************************************************************************/ @@ -439,7 +579,23 @@ int VSIWin32FilesystemHandler::Mkdir( const char * pszPathname, int VSIWin32FilesystemHandler::Rmdir( const char * pszPathname ) { - return rmdir( pszPathname ); +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszPathname, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wrmdir( pwszFilename ); + CPLFree( pwszFilename ); + return nResult; + } + else +#endif + { + return rmdir( pszPathname ); + } } /************************************************************************/ @@ -449,53 +605,77 @@ int VSIWin32FilesystemHandler::Rmdir( const char * pszPathname ) char **VSIWin32FilesystemHandler::ReadDir( const char *pszPath ) { - struct _finddata_t c_file; - intptr_t hFile; - char *pszFileSpec, **papszDir = NULL; - - if (strlen(pszPath) == 0) - pszPath = "."; - - pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath)); - - if ( (hFile = _findfirst( pszFileSpec, &c_file )) != -1L ) +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) { - /* In case of really big number of files in the directory, CSLAddString */ - /* can be slow (see #2158). We then directly build the list. */ - int nItems=0; - int nAllocatedItems=0; - do + struct _wfinddata_t c_file; + intptr_t hFile; + char *pszFileSpec; + CPLStringList oDir; + + if (strlen(pszPath) == 0) + pszPath = "."; + + pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath)); + wchar_t *pwszFileSpec = + CPLRecodeToWChar( pszFileSpec, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + if ( (hFile = _wfindfirst( pwszFileSpec, &c_file )) != -1L ) { - if (nItems == 0) - { - papszDir = (char**) CPLCalloc(2,sizeof(char*)); - nAllocatedItems = 1; - } - else if (nItems >= nAllocatedItems) + do { - nAllocatedItems = nAllocatedItems * 2; - papszDir = (char**)CPLRealloc(papszDir, - (nAllocatedItems+2)*sizeof(char*)); - } - - papszDir[nItems] = CPLStrdup(c_file.name); - papszDir[nItems+1] = NULL; + oDir.AddStringDirectly( + CPLRecodeFromWChar(c_file.name,CPL_ENC_UCS2,CPL_ENC_UTF8)); + } while( _wfindnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } + else + { + /* Should we generate an error??? + * For now we'll just return NULL (at the end of the function) + */ + } - nItems++; - } while( _findnext( hFile, &c_file ) == 0 ); + CPLFree(pszFileSpec); + CPLFree(pwszFileSpec); - _findclose( hFile ); + return oDir.StealList(); } else +#endif { - /* Should we generate an error??? - * For now we'll just return NULL (at the end of the function) - */ - } - - CPLFree(pszFileSpec); + struct _finddata_t c_file; + intptr_t hFile; + char *pszFileSpec; + CPLStringList oDir; + + if (strlen(pszPath) == 0) + pszPath = "."; + + pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath)); + + if ( (hFile = _findfirst( pszFileSpec, &c_file )) != -1L ) + { + do + { + oDir.AddString(c_file.name); + } while( _findnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } + else + { + /* Should we generate an error??? + * For now we'll just return NULL (at the end of the function) + */ + } - return papszDir; + CPLFree(pszFileSpec); + + return oDir.StealList(); + } } /************************************************************************/ diff --git a/cpl/cpl_vsisimple.cpp b/cpl/cpl_vsisimple.cpp index aa2c8dd..790073e 100644 --- a/cpl/cpl_vsisimple.cpp +++ b/cpl/cpl_vsisimple.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cpl_vsisimple.cpp 18568 2010-01-17 02:49:31Z rouault $ + * $Id: cpl_vsisimple.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: Common Portability Library * Purpose: Simple implementation of POSIX VSI functions. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -38,6 +39,7 @@ #include "cpl_port.h" #include "cpl_vsi.h" #include "cpl_error.h" +#include "cpl_string.h" /* Uncomment to check consistent usage of VSIMalloc(), VSIRealloc(), */ /* VSICalloc(), VSIFree(), VSIStrdup() */ @@ -47,11 +49,19 @@ /* DEBUG_VSIMALLOC must also be defined */ //#define DEBUG_VSIMALLOC_STATS +/* Highly experimental, and likely buggy. Do not use, except for fixing it! */ +/* DEBUG_VSIMALLOC must also be defined */ +//#define DEBUG_VSIMALLOC_MPROTECT + +#ifdef DEBUG_VSIMALLOC_MPROTECT +#include +#endif + /* Uncomment to print every memory allocation or deallocation. */ /* DEBUG_VSIMALLOC must also be defined */ //#define DEBUG_VSIMALLOC_VERBOSE -CPL_CVSID("$Id: cpl_vsisimple.cpp 18568 2010-01-17 02:49:31Z rouault $"); +CPL_CVSID("$Id: cpl_vsisimple.cpp 27044 2014-03-16 23:41:27Z rouault $"); /* for stat() */ @@ -84,12 +94,31 @@ CPL_CVSID("$Id: cpl_vsisimple.cpp 18568 2010-01-17 02:49:31Z rouault $"); FILE *VSIFOpen( const char * pszFilename, const char * pszAccess ) { - FILE *fp = fopen( (char *) pszFilename, (char *) pszAccess ); - int nError = errno; + FILE *fp = NULL; + int nError; - VSIDebug3( "VSIFOpen(%s,%s) = %p", pszFilename, pszAccess, fp ); +#if defined(WIN32) && !defined(WIN32CE) + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + wchar_t *pwszAccess = + CPLRecodeToWChar( pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + fp = _wfopen( pwszFilename, pwszAccess ); + + CPLFree( pwszFilename ); + CPLFree( pwszAccess ); + } + else +#endif + fp = fopen( (char *) pszFilename, (char *) pszAccess ); + + nError = errno; + VSIDebug3( "VSIFOpen(%s,%s) = %p", pszFilename, pszAccess, fp ); errno = nError; + return( fp ); } @@ -304,12 +333,20 @@ static GUIntBig nVSICallocs = 0; static GUIntBig nVSIReallocs = 0; static GUIntBig nVSIFrees = 0; +/*size_t GetMaxTotalAllocs() +{ + return nMaxTotalAllocs; +}*/ + /************************************************************************/ /* VSIShowMemStats() */ /************************************************************************/ void VSIShowMemStats() { + char* pszShowMemStats = getenv("CPL_SHOW_MEM_STATS"); + if (pszShowMemStats == NULL || pszShowMemStats[0] == '\0' ) + return; printf("Current VSI memory usage : " CPL_FRMT_GUIB " bytes\n", (GUIntBig)nCurrentTotalAllocs); printf("Maximum VSI memory usage : " CPL_FRMT_GUIB " bytes\n", @@ -327,6 +364,11 @@ void VSIShowMemStats() } #endif +#ifdef DEBUG_VSIMALLOC +static GIntBig nMaxPeakAllocSize = -1; +static GIntBig nMaxCumulAllocSize = -1; +#endif + /************************************************************************/ /* VSICalloc() */ /************************************************************************/ @@ -342,19 +384,60 @@ void *VSICalloc( size_t nCount, size_t nSize ) (int)nCount, (int)nSize); return NULL; } - void* ptr = VSIMalloc(nCount * nSize); + if (nMaxPeakAllocSize < 0) + { + char* pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE"); + nMaxPeakAllocSize = (pszMaxPeakAllocSize) ? atoi(pszMaxPeakAllocSize) : 0; + char* pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE"); + nMaxCumulAllocSize = (pszMaxCumulAllocSize) ? atoi(pszMaxCumulAllocSize) : 0; + } + if (nMaxPeakAllocSize > 0 && (GIntBig)nMul > nMaxPeakAllocSize) + return NULL; +#ifdef DEBUG_VSIMALLOC_STATS + if (nMaxCumulAllocSize > 0 && (GIntBig)nCurrentTotalAllocs + (GIntBig)nMul > nMaxCumulAllocSize) + return NULL; +#endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + char* ptr = NULL; + size_t nPageSize = getpagesize(); + posix_memalign((void**)&ptr, nPageSize, (3 * sizeof(void*) + nMul + nPageSize - 1) & ~(nPageSize - 1)); + if (ptr == NULL) + return NULL; + memset(ptr + 2 * sizeof(void*), 0, nMul); +#else + char* ptr = (char*) calloc(1, 3 * sizeof(void*) + nMul); if (ptr == NULL) return NULL; +#endif - memset(ptr, 0, nCount * nSize); -#ifdef DEBUG_VSIMALLOC_STATS + ptr[0] = 'V'; + ptr[1] = 'S'; + ptr[2] = 'I'; + ptr[3] = 'M'; + memcpy(ptr + sizeof(void*), &nMul, sizeof(void*)); + ptr[2 * sizeof(void*) + nMul + 0] = 'E'; + ptr[2 * sizeof(void*) + nMul + 1] = 'V'; + ptr[2 * sizeof(void*) + nMul + 2] = 'S'; + ptr[2 * sizeof(void*) + nMul + 3] = 'I'; +#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) { CPLMutexHolderD(&hMemStatMutex); +#ifdef DEBUG_VSIMALLOC_VERBOSE + fprintf(stderr, "Thread[%p] VSICalloc(%d,%d) = %p\n", + (void*)CPLGetPID(), (int)nCount, (int)nSize, ptr + 2 * sizeof(void*)); +#endif +#ifdef DEBUG_VSIMALLOC_STATS nVSICallocs ++; - nVSIMallocs --; + if (nMaxTotalAllocs == 0) + atexit(VSIShowMemStats); + nCurrentTotalAllocs += nMul; + if (nCurrentTotalAllocs > nMaxTotalAllocs) + nMaxTotalAllocs = nCurrentTotalAllocs; +#endif } #endif - return ptr; + return ptr + 2 * sizeof(void*); #else return( calloc( nCount, nSize ) ); #endif @@ -368,20 +451,44 @@ void *VSIMalloc( size_t nSize ) { #ifdef DEBUG_VSIMALLOC - char* ptr = (char*) malloc(4 + sizeof(size_t) + nSize); + if (nMaxPeakAllocSize < 0) + { + char* pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE"); + nMaxPeakAllocSize = (pszMaxPeakAllocSize) ? atoi(pszMaxPeakAllocSize) : 0; + char* pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE"); + nMaxCumulAllocSize = (pszMaxCumulAllocSize) ? atoi(pszMaxCumulAllocSize) : 0; + } + if (nMaxPeakAllocSize > 0 && (GIntBig)nSize > nMaxPeakAllocSize) + return NULL; +#ifdef DEBUG_VSIMALLOC_STATS + if (nMaxCumulAllocSize > 0 && (GIntBig)nCurrentTotalAllocs + (GIntBig)nSize > nMaxCumulAllocSize) + return NULL; +#endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + char* ptr = NULL; + size_t nPageSize = getpagesize(); + posix_memalign((void**)&ptr, nPageSize, (3 * sizeof(void*) + nSize + nPageSize - 1) & ~(nPageSize - 1)); +#else + char* ptr = (char*) malloc(3 * sizeof(void*) + nSize); +#endif if (ptr == NULL) return NULL; ptr[0] = 'V'; ptr[1] = 'S'; ptr[2] = 'I'; ptr[3] = 'M'; - memcpy(ptr + 4, &nSize, sizeof(size_t)); + memcpy(ptr + sizeof(void*), &nSize, sizeof(void*)); + ptr[2 * sizeof(void*) + nSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nSize + 3] = 'I'; #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) { CPLMutexHolderD(&hMemStatMutex); #ifdef DEBUG_VSIMALLOC_VERBOSE fprintf(stderr, "Thread[%p] VSIMalloc(%d) = %p\n", - (void*)CPLGetPID(), (int)nSize, ptr + 4 + sizeof(size_t)); + (void*)CPLGetPID(), (int)nSize, ptr + 2 * sizeof(void*)); #endif #ifdef DEBUG_VSIMALLOC_STATS nVSIMallocs ++; @@ -393,14 +500,14 @@ void *VSIMalloc( size_t nSize ) #endif } #endif - return ptr + 4 + sizeof(size_t); + return ptr + 2 * sizeof(void*); #else return( malloc( nSize ) ); #endif } #ifdef DEBUG_VSIMALLOC -void VSICheckMarker(char* ptr) +void VSICheckMarkerBegin(char* ptr) { if (memcmp(ptr, "VSIM", 4) != 0) { @@ -410,6 +517,15 @@ void VSICheckMarker(char* ptr) } } +void VSICheckMarkerEnd(char* ptr, size_t nEnd) +{ + if (memcmp(ptr + nEnd, "EVSI", 4) != 0) + { + CPLError(CE_Fatal, CPLE_AppDefined, + "Memory has been written after the end of %p", ptr); + } +} + #endif /************************************************************************/ @@ -423,24 +539,75 @@ void * VSIRealloc( void * pData, size_t nNewSize ) if (pData == NULL) return VSIMalloc(nNewSize); - char* ptr = ((char*)pData) - 4 - sizeof(size_t); - VSICheckMarker(ptr); -#ifdef DEBUG_VSIMALLOC_STATS + char* ptr = ((char*)pData) - 2 * sizeof(void*); + VSICheckMarkerBegin(ptr); + size_t nOldSize; - memcpy(&nOldSize, ptr + 4, sizeof(size_t)); + memcpy(&nOldSize, ptr + sizeof(void*), sizeof(void*)); + VSICheckMarkerEnd(ptr, 2 * sizeof(void*) + nOldSize); + ptr[2 * sizeof(void*) + nOldSize + 0] = 'I'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'E'; + + if (nMaxPeakAllocSize < 0) + { + char* pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE"); + nMaxPeakAllocSize = (pszMaxPeakAllocSize) ? atoi(pszMaxPeakAllocSize) : 0; + } + if (nMaxPeakAllocSize > 0 && (GIntBig)nNewSize > nMaxPeakAllocSize) + return NULL; +#ifdef DEBUG_VSIMALLOC_STATS + if (nMaxCumulAllocSize > 0 && (GIntBig)nCurrentTotalAllocs + (GIntBig)nNewSize - (GIntBig)nOldSize > nMaxCumulAllocSize) + return NULL; #endif - ptr = (char*) realloc(ptr, nNewSize + 4 + sizeof(size_t)); - if (ptr == NULL) +#ifdef DEBUG_VSIMALLOC_MPROTECT + char* newptr = NULL; + size_t nPageSize = getpagesize(); + posix_memalign((void**)&newptr, nPageSize, (nNewSize + 3 * sizeof(void*) + nPageSize - 1) & ~(nPageSize - 1)); + if (newptr == NULL) + { + ptr[2 * sizeof(void*) + nOldSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'I'; return NULL; - memcpy(ptr + 4, &nNewSize, sizeof(size_t)); + } + memcpy(newptr + 2 * sizeof(void*), pData, nOldSize); + ptr[0] = 'M'; + ptr[1] = 'I'; + ptr[2] = 'S'; + ptr[3] = 'V'; + free(ptr); + newptr[0] = 'V'; + newptr[1] = 'S'; + newptr[2] = 'I'; + newptr[3] = 'M'; +#else + void* newptr = realloc(ptr, nNewSize + 3 * sizeof(void*)); + if (newptr == NULL) + { + ptr[2 * sizeof(void*) + nOldSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'I'; + return NULL; + } +#endif + ptr = (char*) newptr; + memcpy(ptr + sizeof(void*), &nNewSize, sizeof(void*)); + ptr[2 * sizeof(void*) + nNewSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nNewSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nNewSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nNewSize + 3] = 'I'; #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) { CPLMutexHolderD(&hMemStatMutex); #ifdef DEBUG_VSIMALLOC_VERBOSE fprintf(stderr, "Thread[%p] VSIRealloc(%p, %d) = %p\n", - (void*)CPLGetPID(), pData, (int)nNewSize, ptr + 4 + sizeof(size_t)); + (void*)CPLGetPID(), pData, (int)nNewSize, ptr + 2 * sizeof(void*)); #endif #ifdef DEBUG_VSIMALLOC_STATS nVSIReallocs ++; @@ -451,7 +618,7 @@ void * VSIRealloc( void * pData, size_t nNewSize ) #endif } #endif - return ptr + 4 + sizeof(size_t); + return ptr + 2 * sizeof(void*); #else return( realloc( pData, nNewSize ) ); #endif @@ -468,15 +635,20 @@ void VSIFree( void * pData ) if (pData == NULL) return; - char* ptr = ((char*)pData) - 4 - sizeof(size_t); - VSICheckMarker(ptr); + char* ptr = ((char*)pData) - 2 * sizeof(void*); + VSICheckMarkerBegin(ptr); + size_t nOldSize; + memcpy(&nOldSize, ptr + sizeof(void*), sizeof(void*)); + VSICheckMarkerEnd(ptr, 2 * sizeof(void*) + nOldSize); ptr[0] = 'M'; ptr[1] = 'I'; ptr[2] = 'S'; ptr[3] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 0] = 'I'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'E'; #if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) - size_t nOldSize; - memcpy(&nOldSize, ptr + 4, sizeof(size_t)); { CPLMutexHolderD(&hMemStatMutex); #ifdef DEBUG_VSIMALLOC_VERBOSE @@ -489,7 +661,13 @@ void VSIFree( void * pData ) #endif } #endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + mprotect(ptr, nOldSize + 2 * sizeof(void*), PROT_NONE); +#else free(ptr); +#endif + #else if( pData != NULL ) free( pData ); @@ -506,6 +684,8 @@ char *VSIStrdup( const char * pszString ) #ifdef DEBUG_VSIMALLOC int nSize = strlen(pszString) + 1; char* ptr = (char*) VSIMalloc(nSize); + if (ptr == NULL) + return NULL; memcpy(ptr, pszString, nSize); return ptr; #else @@ -672,11 +852,23 @@ void CPL_DLL *VSIMalloc3( size_t nSize1, size_t nSize2, size_t nSize3 ) int VSIStat( const char * pszFilename, VSIStatBuf * pStatBuf ) { -#if defined(macos_pre10) - return -1; -#else - return( stat( pszFilename, pStatBuf ) ); -#endif +#if defined(WIN32) && !defined(WIN32CE) + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wstat( pwszFilename, (struct _stat *) pStatBuf ); + + CPLFree( pwszFilename ); + + return nResult; + } + else +#endif + return( stat( pszFilename, pStatBuf ) ); } /************************************************************************/ diff --git a/cpl/cplgetsymbol.cpp b/cpl/cplgetsymbol.cpp index 0af104c..bb9c749 100644 --- a/cpl/cplgetsymbol.cpp +++ b/cpl/cplgetsymbol.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cplgetsymbol.cpp 16702 2009-04-01 20:42:49Z rouault $ + * $Id: cplgetsymbol.cpp 27460 2014-06-18 12:36:06Z rouault $ * * Project: Common Portability Library * Purpose: Fetch a function pointer from a shared library / DLL. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,7 +30,7 @@ #include "cpl_conv.h" -CPL_CVSID("$Id: cplgetsymbol.cpp 16702 2009-04-01 20:42:49Z rouault $"); +CPL_CVSID("$Id: cplgetsymbol.cpp 27460 2014-06-18 12:36:06Z rouault $"); /* ==================================================================== */ @@ -140,13 +141,21 @@ void *CPLGetSymbol( const char * pszLibrary, const char * pszSymbolName ) { void *pLibrary; void *pSymbol; + UINT uOldErrorMode; + + /* Avoid error boxes to pop up (#5211, #5525) */ + uOldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); pLibrary = LoadLibrary(pszLibrary); - if( pLibrary == NULL ) + + if( pLibrary <= (void*)HINSTANCE_ERROR ) { LPVOID lpMsgBuf = NULL; int nLastError = GetLastError(); - + + /* Restore old error mode */ + SetErrorMode(uOldErrorMode); + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, @@ -160,6 +169,9 @@ void *CPLGetSymbol( const char * pszLibrary, const char * pszSymbolName ) return NULL; } + /* Restore old error mode */ + SetErrorMode(uOldErrorMode); + pSymbol = (void *) GetProcAddress( (HINSTANCE) pLibrary, pszSymbolName ); if( pSymbol == NULL ) diff --git a/cpl/cplstring.cpp b/cpl/cplstring.cpp index e88dab5..de3d542 100644 --- a/cpl/cplstring.cpp +++ b/cpl/cplstring.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: cplstring.cpp 11044 2007-03-22 12:04:04Z dron $ + * $Id: cplstring.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: GDAL * Purpose: CPLString implementation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2011, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,7 @@ #include "cpl_string.h" #include -CPL_CVSID("$Id: cplstring.cpp 11044 2007-03-22 12:04:04Z dron $"); +CPL_CVSID("$Id: cplstring.cpp 27044 2014-03-16 23:41:27Z rouault $"); /* * The CPLString class is derived from std::string, so the vast majority @@ -203,3 +204,244 @@ CPLString &CPLString::Trim() return *this; } + +/************************************************************************/ +/* Recode() */ +/************************************************************************/ + +CPLString &CPLString::Recode( const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ + if( pszSrcEncoding == NULL ) + pszSrcEncoding = CPL_ENC_UTF8; + if( pszDstEncoding == NULL ) + pszDstEncoding = CPL_ENC_UTF8; + + if( strcmp(pszSrcEncoding,pszDstEncoding) == 0 ) + return *this; + + char *pszRecoded = CPLRecode( c_str(), + pszSrcEncoding, + pszDstEncoding ); + + if( pszRecoded == NULL ) + return *this; + + assign( pszRecoded ); + CPLFree( pszRecoded ); + + return *this; +} + +/************************************************************************/ +/* ifind() */ +/************************************************************************/ + +/** + * Case insensitive find() alternative. + * + * @param str substring to find. + * @param pos offset in the string at which the search starts. + * @return the position of substring in the string or std::string::npos if not found. + * @since GDAL 1.9.0 + */ + +size_t CPLString::ifind( const std::string & str, size_t pos ) const + +{ + return ifind( str.c_str(), pos ); +} + +/** + * Case insensitive find() alternative. + * + * @param s substring to find. + * @param nPos offset in the string at which the search starts. + * @return the position of the substring in the string or std::string::npos if not found. + * @since GDAL 1.9.0 + */ + +size_t CPLString::ifind( const char *s, size_t nPos ) const + +{ + const char *pszHaystack = c_str(); + char chFirst = (char) ::tolower( s[0] ); + int nTargetLen = strlen(s); + + if( nPos > size() ) + nPos = size(); + + pszHaystack += nPos; + + while( *pszHaystack != '\0' ) + { + if( chFirst == ::tolower(*pszHaystack) ) + { + if( EQUALN(pszHaystack,s,nTargetLen) ) + return nPos; + } + + nPos++; + pszHaystack++; + } + + return std::string::npos; +} + +/************************************************************************/ +/* toupper() */ +/************************************************************************/ + +/** + * Convert to upper case in place. + */ + +CPLString &CPLString::toupper() + +{ + size_t i; + + for( i = 0; i < size(); i++ ) + (*this)[i] = (char) ::toupper( (*this)[i] ); + + return *this; +} + +/************************************************************************/ +/* tolower() */ +/************************************************************************/ + +/** + * Convert to lower case in place. + */ + +CPLString &CPLString::tolower() + +{ + size_t i; + + for( i = 0; i < size(); i++ ) + (*this)[i] = (char) ::tolower( (*this)[i] ); + + return *this; +} + +/************************************************************************/ +/* CPLURLGetValue() */ +/************************************************************************/ + +/** + * Return the value matching a key from a key=value pair in a URL. + * + * @param pszURL the URL. + * @param pszKey the key to find. + * @return the value of empty string if not found. + * @since GDAL 1.9.0 + */ +CPLString CPLURLGetValue(const char* pszURL, const char* pszKey) +{ + CPLString osKey(pszKey); + osKey += "="; + size_t nKeyPos = CPLString(pszURL).ifind(osKey); + if (nKeyPos != std::string::npos && nKeyPos > 0 && + (pszURL[nKeyPos-1] == '?' || pszURL[nKeyPos-1] == '&')) + { + CPLString osValue(pszURL + nKeyPos + strlen(osKey)); + const char* pszValue = osValue.c_str(); + const char* pszSep = strchr(pszValue, '&'); + if (pszSep) + { + osValue.resize(pszSep - pszValue); + } + return osValue; + } + return ""; +} + +/************************************************************************/ +/* CPLURLAddKVP() */ +/************************************************************************/ + +/** + * Return a new URL with a new key=value pair. + * + * @param pszURL the URL. + * @param pszKey the key to find. + * @param pszValue the value of the key (may be NULL to unset an existing KVP). + * @return the modified URL. + * @since GDAL 1.9.0 + */ +CPLString CPLURLAddKVP(const char* pszURL, const char* pszKey, + const char* pszValue) +{ + CPLString osURL(pszURL); + if (strchr(osURL, '?') == NULL) + osURL += "?"; + pszURL = osURL.c_str(); + + CPLString osKey(pszKey); + osKey += "="; + size_t nKeyPos = osURL.ifind(osKey); + if (nKeyPos != std::string::npos && nKeyPos > 0 && + (pszURL[nKeyPos-1] == '?' || pszURL[nKeyPos-1] == '&')) + { + CPLString osNewURL(osURL); + osNewURL.resize(nKeyPos); + if (pszValue) + { + osNewURL += osKey; + osNewURL += pszValue; + } + const char* pszNext = strchr(pszURL + nKeyPos, '&'); + if (pszNext) + { + if (osNewURL[osNewURL.size()-1] == '&' + || osNewURL[osNewURL.size()-1] == '?' ) + osNewURL += pszNext + 1; + else + osNewURL += pszNext; + } + return osNewURL; + } + else + { + if (pszValue) + { + if (osURL[osURL.size()-1] != '&' && osURL[osURL.size()-1] != '?') + osURL += '&'; + osURL += osKey; + osURL += pszValue; + } + return osURL; + } +} + +/************************************************************************/ +/* CPLOPrintf() */ +/************************************************************************/ + +CPLString CPLOPrintf( const char *pszFormat, ... ) + +{ + va_list args; + CPLString osTarget; + + va_start( args, pszFormat ); + osTarget.vPrintf( pszFormat, args ); + va_end( args ); + + return osTarget; +} + +/************************************************************************/ +/* CPLOvPrintf() */ +/************************************************************************/ + +CPLString CPLOvPrintf( const char *pszFormat, va_list args ) + +{ + CPLString osTarget; + osTarget.vPrintf( pszFormat, args); + return osTarget; +} diff --git a/cpl/cplstringlist.cpp b/cpl/cplstringlist.cpp new file mode 100644 index 0000000..08516dd --- /dev/null +++ b/cpl/cplstringlist.cpp @@ -0,0 +1,739 @@ +/****************************************************************************** + * $Id: cplstringlist.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL + * Purpose: CPLStringList implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: cplstringlist.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* CPLStringList() */ +/************************************************************************/ + +CPLStringList::CPLStringList() + +{ + Initialize(); +} + +/************************************************************************/ +/* CPLStringList() */ +/************************************************************************/ + +/** + * CPLStringList constructor. + * + * @param papszListIn the NULL terminated list of strings to consume. + * @param bTakeOwnership TRUE if the CPLStringList should take ownership + * of the list of strings which implies responsibility to free them. + */ + +CPLStringList::CPLStringList( char **papszListIn, int bTakeOwnership ) + +{ + Initialize(); + Assign( papszListIn, bTakeOwnership ); +} + +/************************************************************************/ +/* CPLStringList() */ +/************************************************************************/ + +//! Copy constructor +CPLStringList::CPLStringList( const CPLStringList &oOther ) + +{ + Initialize(); + Assign( oOther.papszList, FALSE ); + + // We don't want to just retain a reference to the others list + // as we don't want to make assumptions about it's lifetime that + // might surprise the client developer. + MakeOurOwnCopy(); + bIsSorted = oOther.bIsSorted; +} + +/************************************************************************/ +/* operator=() */ +/************************************************************************/ + +CPLStringList &CPLStringList::operator=(const CPLStringList& oOther) +{ + if (this != &oOther) + { + Assign( oOther.papszList, FALSE ); + + // We don't want to just retain a reference to the others list + // as we don't want to make assumptions about it's lifetime that + // might surprise the client developer. + MakeOurOwnCopy(); + bIsSorted = oOther.bIsSorted; + } + + return *this; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void CPLStringList::Initialize() + +{ + papszList = NULL; + nCount = 0; + nAllocation = 0; + bOwnList = FALSE; + bIsSorted = FALSE; +} + +/************************************************************************/ +/* ~CPLStringList() */ +/************************************************************************/ + +CPLStringList::~CPLStringList() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +/** + * Clear the string list. + */ +CPLStringList &CPLStringList::Clear() + +{ + if( bOwnList ) + { + CSLDestroy( papszList ); + papszList = NULL; + + bOwnList = FALSE; + nAllocation = 0; + nCount = 0; + } + + return *this; +} + +/************************************************************************/ +/* Assign() */ +/************************************************************************/ + +/** + * Assign a list of strings. + * + * + * @param papszListIn the NULL terminated list of strings to consume. + * @param bTakeOwnership TRUE if the CPLStringList should take ownership + * of the list of strings which implies responsibility to free them. + * + * @return a reference to the CPLStringList on which it was invoked. + */ + +CPLStringList &CPLStringList::Assign( char **papszListIn, int bTakeOwnership ) + +{ + Clear(); + + papszList = papszListIn; + bOwnList = bTakeOwnership; + + if( papszList == NULL || *papszList == NULL ) + nCount = 0; + else + nCount = -1; // unknown + + nAllocation = 0; + bIsSorted = FALSE; + + return *this; +} + +/************************************************************************/ +/* Count() */ +/************************************************************************/ + +/** + * @return count of strings in the list, zero if empty. + */ + +int CPLStringList::Count() const + +{ + if( nCount == -1 ) + { + if( papszList == NULL ) + { + nCount = nAllocation = 0; + } + else + { + nCount = CSLCount( papszList ); + nAllocation = MAX(nCount+1,nAllocation); + } + } + + return nCount; +} + +/************************************************************************/ +/* MakeOurOwnCopy() */ +/* */ +/* If we don't own the list, a copy is made which we own. */ +/* Necessary if we are going to modify the list. */ +/************************************************************************/ + +void CPLStringList::MakeOurOwnCopy() + +{ + if( bOwnList ) + return; + + if( papszList == NULL ) + return; + + Count(); + bOwnList = TRUE; + papszList = CSLDuplicate( papszList ); + nAllocation = nCount+1; +} + +/************************************************************************/ +/* EnsureAllocation() */ +/* */ +/* Ensure we have enough room allocated for at least the */ +/* requested number of strings (so nAllocation will be at least */ +/* one more than the target) */ +/************************************************************************/ + +void CPLStringList::EnsureAllocation( int nMaxList ) + +{ + if( !bOwnList ) + MakeOurOwnCopy(); + + if( nAllocation <= nMaxList ) + { + nAllocation = MAX(nAllocation*2 + 20,nMaxList+1); + if( papszList == NULL ) + { + papszList = (char **) CPLCalloc(nAllocation,sizeof(char*)); + bOwnList = TRUE; + nCount = 0; + } + else + papszList = (char **) CPLRealloc(papszList, nAllocation*sizeof(char*)); + } +} + +/************************************************************************/ +/* AddStringDirectly() */ +/************************************************************************/ + +/** + * Add a string to the list. + * + * This method is similar to AddString(), but ownership of the + * pszNewString is transferred to the CPLStringList class. + * + * @param pszNewString the string to add to the list. + */ + +CPLStringList &CPLStringList::AddStringDirectly( char *pszNewString ) + +{ + if( nCount == -1 ) + Count(); + + EnsureAllocation( nCount+1 ); + + papszList[nCount++] = pszNewString; + papszList[nCount] = NULL; + + bIsSorted = FALSE; + + return *this; +} + +/************************************************************************/ +/* AddString() */ +/************************************************************************/ + +/** + * Add a string to the list. + * + * A copy of the passed in string is made and inserted in the list. + * + * @param pszNewString the string to add to the list. + */ + +CPLStringList &CPLStringList::AddString( const char *pszNewString ) + +{ + return AddStringDirectly( CPLStrdup( pszNewString ) ); +} + +/************************************************************************/ +/* AddNameValue() */ +/************************************************************************/ + +/** + * A a name=value entry to the list. + * + * A key=value string is prepared and appended to the list. There is no + * check for other values for the same key in the list. + * + * @param pszKey the key name to add. + * @param pszValue the key value to add. + */ + +CPLStringList &CPLStringList::AddNameValue( const char *pszKey, + const char *pszValue ) + +{ + if (pszKey == NULL || pszValue==NULL) + return *this; + + MakeOurOwnCopy(); + +/* -------------------------------------------------------------------- */ +/* Format the line. */ +/* -------------------------------------------------------------------- */ + char *pszLine; + pszLine = (char *) CPLMalloc(strlen(pszKey)+strlen(pszValue)+2); + sprintf( pszLine, "%s=%s", pszKey, pszValue ); + +/* -------------------------------------------------------------------- */ +/* If we don't need to keep the sort order things are pretty */ +/* straight forward. */ +/* -------------------------------------------------------------------- */ + if( !IsSorted() ) + return AddStringDirectly( pszLine ); + +/* -------------------------------------------------------------------- */ +/* Find the proper insertion point. */ +/* -------------------------------------------------------------------- */ + CPLAssert( IsSorted() ); + int iKey = FindSortedInsertionPoint( pszLine ); + InsertStringDirectly( iKey, pszLine ); + bIsSorted = TRUE; // we have actually preserved sort order. + + return *this; +} + +/************************************************************************/ +/* SetNameValue() */ +/************************************************************************/ + +/** + * Set name=value entry in the list. + * + * Similar to AddNameValue(), except if there is already a value for + * the key in the list it is replaced instead of adding a new entry to + * the list. If pszValue is NULL any existing key entry is removed. + * + * @param pszKey the key name to add. + * @param pszValue the key value to add. + */ + +CPLStringList &CPLStringList::SetNameValue( const char *pszKey, + const char *pszValue ) + +{ + int iKey = FindName( pszKey ); + + if( iKey == -1 ) + return AddNameValue( pszKey, pszValue ); + + Count(); + MakeOurOwnCopy(); + + CPLFree( papszList[iKey] ); + if( pszValue == NULL ) // delete entry + { + + // shift everything down by one. + do + { + papszList[iKey] = papszList[iKey+1]; + } + while( papszList[iKey++] != NULL ); + + nCount--; + } + else + { + char *pszLine; + pszLine = (char *) CPLMalloc(strlen(pszKey)+strlen(pszValue)+2); + sprintf( pszLine, "%s=%s", pszKey, pszValue ); + + papszList[iKey] = pszLine; + } + + return *this; +} + +/************************************************************************/ +/* operator[] */ +/************************************************************************/ + +/** + * Fetch entry "i". + * + * Fetches the requested item in the list. Note that the returned string + * remains owned by the CPLStringList. If "i" is out of range NULL is + * returned. + * + * @param i the index of the list item to return. + * @return selected entry in the list. + */ +char *CPLStringList::operator[]( int i ) + +{ + if( nCount == -1 ) + Count(); + + if( i < 0 || i >= nCount ) + return NULL; + else + return papszList[i]; +} + +const char *CPLStringList::operator[]( int i ) const + +{ + if( nCount == -1 ) + Count(); + + if( i < 0 || i >= nCount ) + return NULL; + else + return papszList[i]; +} + +/************************************************************************/ +/* StealList() */ +/************************************************************************/ + +/** + * Seize ownership of underlying string array. + * + * This method is simmilar to List(), except that the returned list is + * now owned by the caller and the CPLStringList is emptied. + * + * @return the C style string list. + */ +char **CPLStringList::StealList() + +{ + char **papszRetList = papszList; + + bOwnList = FALSE; + papszList = NULL; + nCount = 0; + nAllocation = 0; + + return papszRetList; +} + +/************************************************************************/ +/* llCompareStr() */ +/* */ +/* Note this is case insensitive! This is because we normally */ +/* treat key value keywords as case insensitive. */ +/************************************************************************/ +static int llCompareStr(const void *a, const void *b) +{ + return STRCASECMP((*(const char **)a),(*(const char **)b)); +} + +/************************************************************************/ +/* Sort() */ +/************************************************************************/ + +/** + * Sort the entries in the list and mark list sorted. + * + * Note that once put into "sorted" mode, the CPLStringList will attempt to + * keep things in sorted order through calls to AddString(), + * AddStringDirectly(), AddNameValue(), SetNameValue(). Complete list + * assignments (via Assign() and operator= will clear the sorting state. + * When in sorted order FindName(), FetchNameValue() and FetchNameValueDef() + * will do a binary search to find the key, substantially improve lookup + * performance in large lists. + */ + +CPLStringList &CPLStringList::Sort() + +{ + Count(); + MakeOurOwnCopy(); + + qsort( papszList, nCount, sizeof(char*), llCompareStr ); + bIsSorted = TRUE; + + return *this; +} + +/************************************************************************/ +/* FindName() */ +/************************************************************************/ + +/** + * Get index of given name/value keyword. + * + * Note that this search is for a line in the form name=value or name:value. + * Use FindString() or PartialFindString() for searches not based on name=value + * pairs. + * + * @param pszKey the name to search for. + * + * @return the string list index of this name, or -1 on failure. + */ + +int CPLStringList::FindName( const char *pszKey ) const + +{ + if( !IsSorted() ) + return CSLFindName( papszList, pszKey ); + + // If we are sorted, we can do an optimized binary search. + int iStart=0, iEnd=nCount-1; + int nKeyLen = strlen(pszKey); + + while( iStart <= iEnd ) + { + int iMiddle = (iEnd+iStart)/2; + const char *pszMiddle = papszList[iMiddle]; + + if (EQUALN(pszMiddle, pszKey, nKeyLen) + && (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':') ) + return iMiddle; + + if( STRCASECMP(pszKey,pszMiddle) < 0 ) + iEnd = iMiddle-1; + else + iStart = iMiddle+1; + } + + return -1; +} + +/************************************************************************/ +/* FetchBoolean() */ +/************************************************************************/ +/** + * + * Check for boolean key value. + * + * In a CPLStringList of "Name=Value" pairs, look to see if there is a key + * with the given name, and if it can be interpreted as being TRUE. If + * the key appears without any "=Value" portion it will be considered true. + * If the value is NO, FALSE or 0 it will be considered FALSE otherwise + * if the key appears in the list it will be considered TRUE. If the key + * doesn't appear at all, the indicated default value will be returned. + * + * @param pszKey the key value to look for (case insensitive). + * @param bDefault the value to return if the key isn't found at all. + * + * @return TRUE or FALSE + */ + +int CPLStringList::FetchBoolean( const char *pszKey, int bDefault ) const + +{ + const char *pszValue = FetchNameValue( pszKey ); + + if( pszValue == NULL ) + return bDefault; + else + return CSLTestBoolean( pszValue ); +} + +/************************************************************************/ +/* FetchNameValue() */ +/************************************************************************/ + +/** + * Fetch value associated with this key name. + * + * If this list sorted, a fast binary search is done, otherwise a linear + * scan is done. Name lookup is case insensitive. + * + * @param pszName the key name to search for. + * + * @return the corresponding value or NULL if not found. The returned string + * should not be modified and points into internal object state that may + * change on future calls. + */ + +const char *CPLStringList::FetchNameValue( const char *pszName ) const + +{ + int iKey = FindName( pszName ); + + if( iKey == -1 ) + return NULL; + + CPLAssert( papszList[iKey][strlen(pszName)] == '=' + || papszList[iKey][strlen(pszName)] == ':' ); + + return papszList[iKey] + strlen(pszName)+1; +} + +/************************************************************************/ +/* FetchNameValueDef() */ +/************************************************************************/ + +/** + * Fetch value associated with this key name. + * + * If this list sorted, a fast binary search is done, otherwise a linear + * scan is done. Name lookup is case insensitive. + * + * @param pszName the key name to search for. + * @param pszDefault the default value returned if the named entry isn't found. + * + * @return the corresponding value or the passed default if not found. + */ + +const char *CPLStringList::FetchNameValueDef( const char *pszName, + const char *pszDefault ) const + +{ + const char *pszValue = FetchNameValue( pszName ); + if( pszValue == NULL ) + return pszDefault; + else + return pszValue; +} + +/************************************************************************/ +/* InsertString() */ +/************************************************************************/ + +/** + * \fn CPLStringList *CPLStringList::InsertString( int nInsertAtLineNo, + * const char *pszNewLine ); + * + * \brief Insert into the list at identified location. + * + * This method will insert a string into the list at the identified + * location. The insertion point must be within or at the end of the list. + * The following entries are pushed down to make space. + * + * @param nInsertAtLineNo the line to insert at, zero to insert at front. + * @param pszNewLine to the line to insert. This string will be copied. + */ + + +/************************************************************************/ +/* InsertStringDirectly() */ +/************************************************************************/ + +/** + * Insert into the list at identified location. + * + * This method will insert a string into the list at the identified + * location. The insertion point must be within or at the end of the list. + * The following entries are pushed down to make space. + * + * @param nInsertAtLineNo the line to insert at, zero to insert at front. + * @param pszNewLine to the line to insert, the ownership of this string + * will be taken over the by the object. It must have been allocated on the + * heap. + */ + +CPLStringList &CPLStringList::InsertStringDirectly( int nInsertAtLineNo, + char *pszNewLine ) + +{ + if( nCount == -1 ) + Count(); + + EnsureAllocation( nCount+1 ); + + if( nInsertAtLineNo < 0 || nInsertAtLineNo > nCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CPLStringList::InsertString() requested beyond list end." ); + return *this; + } + + bIsSorted = FALSE; + + for( int i = nCount; i > nInsertAtLineNo; i-- ) + papszList[i] = papszList[i-1]; + + papszList[nInsertAtLineNo] = pszNewLine; + papszList[++nCount] = NULL; + + return *this; +} + +/************************************************************************/ +/* FindSortedInsertionPoint() */ +/* */ +/* Find the location at which the indicated line should be */ +/* inserted in order to keep things in sorted order. */ +/************************************************************************/ + +int CPLStringList::FindSortedInsertionPoint( const char *pszLine ) + +{ + CPLAssert( IsSorted() ); + + int iStart=0, iEnd=nCount-1; + + while( iStart <= iEnd ) + { + int iMiddle = (iEnd+iStart)/2; + const char *pszMiddle = papszList[iMiddle]; + + if( STRCASECMP(pszLine,pszMiddle) < 0 ) + iEnd = iMiddle-1; + else + iStart = iMiddle+1; + } + + iEnd++; + CPLAssert( iEnd >= 0 && iEnd <= nCount ); + CPLAssert( iEnd == 0 + || STRCASECMP(pszLine,papszList[iEnd-1]) >= 0 ); + CPLAssert( iEnd == nCount + || STRCASECMP(pszLine,papszList[iEnd]) <= 0 ); + + return iEnd; +} diff --git a/cpl/gdal_csv.h b/cpl/gdal_csv.h new file mode 100644 index 0000000..e4f430c --- /dev/null +++ b/cpl/gdal_csv.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * $Id: gdal_csv.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Common Portability Library + * Purpose: Functions for reading and scaning CSV (comma separated, + * variable length text files holding tables) files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_CSV_H_INCLUDED +#define GDAL_CSV_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START +const char * GDALDefaultCSVFilename( const char *pszBasename ); +CPL_C_END + +#endif diff --git a/cpl/makefile.vc b/cpl/makefile.vc index 58a4352..5a728cf 100755 --- a/cpl/makefile.vc +++ b/cpl/makefile.vc @@ -23,7 +23,11 @@ OBJ = cpl_conv.obj \ cpl_strtod.obj \ cpl_vsil_subfile.obj \ cpl_recode_stub.obj \ - cpl_vsil_stdout.obj + cpl_vsil_stdout.obj \ + cpl_vsil_stdout.obj \ + cplstringlist.obj cpl_recode.obj cpl_recode_stub.obj cpl_vsil_stdin.obj cpl_vsil_stdout.obj \ + cpl_vsil_sparsefile.obj cpl_vsil_tar.obj cpl_vsil_cache.obj cpl_vsil_abstract_archive.obj cpl_time.obj \ + cpl_hash_set.obj cpl_list.obj cpl_progress.obj cpl_spawn.obj cpl_virtualmem.obj LIB = cpl.lib diff --git a/mitab/GNUmakefile b/mitab/GNUmakefile index 3ce1147..e54f263 100644 --- a/mitab/GNUmakefile +++ b/mitab/GNUmakefile @@ -32,7 +32,7 @@ MITAB_SHARED_LIB_FULLNAME=libmitab.so.1.0.1 # -ldl doesn't work on MacOSX ifneq "$(shell uname -s)" "Darwin" - DL_LIB = -ldl + DL_LIB = -ldl -lpthread endif LIBS = $(MITAB_LIB) ../ogr/ogr.a ../cpl/cpl.a $(LIB_DBMALLOC) $(DL_LIB) @@ -75,7 +75,7 @@ $(MITAB_LIB): $(MITABLIB_OBJS) $(MITAB_SHARED_LIB_FULLNAME): $(MITABLIB_OBJS) rm -f $(MITAB_SHARED_LIB_FULLNAME) $(MITAB_SHARED_LIB_SONAME) $(MITAB_SHARED_LIB_LINKNAME) - $(CXX) $(MITABLIB_OBJS) ../ogr/ogr.a ../cpl/cpl.a -shared -Wl,-soname,$(MITAB_SHARED_LIB_SONAME) -o $(MITAB_SHARED_LIB_FULLNAME) -ldl + $(CXX) $(MITABLIB_OBJS) ../ogr/ogr.a ../cpl/cpl.a -shared -Wl,-soname,$(MITAB_SHARED_LIB_SONAME) -o $(MITAB_SHARED_LIB_FULLNAME) -ldl -lpthread ln -s $(MITAB_SHARED_LIB_FULLNAME) $(MITAB_SHARED_LIB_SONAME) ln -s $(MITAB_SHARED_LIB_SONAME) $(MITAB_SHARED_LIB_LINKNAME) diff --git a/mitab/makefile.vc b/mitab/makefile.vc index 1d39408..2c14279 100755 --- a/mitab/makefile.vc +++ b/mitab/makefile.vc @@ -36,6 +36,8 @@ OPTFLAGS = $(OPTFLAGS) -DMITAB_CDECL MITAB_DLL = mitab.dll !endif +EXTERNAL_LIBS = wsock32.lib ws2_32.lib + LIBS = $(TABLIB) ../ogr/ogr.lib ../cpl/cpl.lib default: $(TABLIB) tab2tab.exe ogrinfo.exe mitabc_test.exe $(MITAB_DLL) @@ -44,22 +46,22 @@ $(TABLIB): $(TAB_OBJS) lib /out:$(TABLIB) $(TAB_OBJS) tab2tab.exe: tab2tab.cpp $(LIBS) - $(CC) $(CFLAGS) tab2tab.cpp $(LIBS) + $(CC) $(CFLAGS) tab2tab.cpp $(LIBS) $(EXTERNAL_LIBS) mitabc_test.exe: mitabc_test.c $(LIBS) - $(CC) $(CFLAGS) mitabc_test.c $(LIBS) + $(CC) $(CFLAGS) mitabc_test.c $(LIBS) $(EXTERNAL_LIBS) ogr2ogr.exe: ogr2ogr.cpp $(OGRTAB_OBJS) $(LIBS) - $(CC) $(CFLAGS) ogr2ogr.cpp $(OGRTAB_OBJS) $(LIBS) + $(CC) $(CFLAGS) ogr2ogr.cpp $(OGRTAB_OBJS) $(LIBS) $(EXTERNAL_LIBS) ogrinfo.exe: ogrinfo.cpp $(OGRTAB_OBJS) $(LIBS) - $(CC) $(CFLAGS) ogrinfo.cpp $(OGRTAB_OBJS) $(LIBS) + $(CC) $(CFLAGS) ogrinfo.cpp $(OGRTAB_OBJS) $(LIBS) $(EXTERNAL_LIBS) tabindex.exe: tabindex.cpp $(LIBS) - $(CC) $(CFLAGS) tabindex.cpp $(LIBS) + $(CC) $(CFLAGS) tabindex.cpp $(LIBS) $(EXTERNAL_LIBS) $(MITAB_DLL): $(LIBS) $(TAB_OBJS) - link /dll /out:$(MITAB_DLL) /implib:mitab_i.lib $(TAB_OBJS) $(LIBS) + link /dll /out:$(MITAB_DLL) /implib:mitab_i.lib $(TAB_OBJS) $(LIBS) $(EXTERNAL_LIBS) clean: del *.obj diff --git a/mitab/mitab_bounds.cpp b/mitab/mitab_bounds.cpp index f66ef0b..cbc985e 100644 --- a/mitab/mitab_bounds.cpp +++ b/mitab/mitab_bounds.cpp @@ -1158,12 +1158,12 @@ GBool MITABLookupCoordSysBounds(TABProjInfo *psCS, **********************************************************************/ int MITABLoadCoordSysTable(const char *pszFname) { - FILE *fp; + VSILFILE *fp; int nStatus = 0, iLine = 0; MITABFreeCoordSysTable(); - if ((fp = VSIFOpen(pszFname, "rt")) != NULL) + if ((fp = VSIFOpenL(pszFname, "rt")) != NULL) { const char *pszLine; int iEntry=0, numEntries=100; @@ -1172,7 +1172,7 @@ int MITABLoadCoordSysTable(const char *pszFname) sizeof(MapInfoBoundsInfo *)); gpapsExtBoundsList[0] = NULL; - while( (pszLine = CPLReadLine(fp)) != NULL) + while( (pszLine = CPLReadLineL(fp)) != NULL) { double dXMin, dYMin, dXMax, dYMax; TABProjInfo sProj; @@ -1216,7 +1216,7 @@ int MITABLoadCoordSysTable(const char *pszFname) gpapsExtBoundsList[++iEntry] = NULL; } - VSIFClose(fp); + VSIFCloseL(fp); } return nStatus; diff --git a/mitab/mitab_datfile.cpp b/mitab/mitab_datfile.cpp index 261106a..107da83 100644 --- a/mitab/mitab_datfile.cpp +++ b/mitab/mitab_datfile.cpp @@ -202,7 +202,7 @@ int TABDATFile::Open(const char *pszFname, const char *pszAccess, * Open file for reading *----------------------------------------------------------------*/ m_pszFname = CPLStrdup(pszFname); - m_fp = VSIFOpen(m_pszFname, pszAccess); + m_fp = VSIFOpenL(m_pszFname, pszAccess); m_eTableType = eTableType; if (m_fp == NULL) @@ -311,8 +311,8 @@ int TABDATFile::Close() WriteHeader(); char cEOF = 26; - if (VSIFSeek(m_fp, 0L, SEEK_END) == 0) - VSIFWrite(&cEOF, 1, 1, m_fp); + if (VSIFSeekL(m_fp, 0L, SEEK_END) == 0) + VSIFWriteL(&cEOF, 1, 1, m_fp); } // Delete all structures @@ -329,7 +329,7 @@ int TABDATFile::Close() } // Close file - VSIFClose(m_fp); + VSIFCloseL(m_fp); m_fp = NULL; CPLFree(m_pszFname); diff --git a/mitab/mitab_idfile.cpp b/mitab/mitab_idfile.cpp index 7af3ef8..347c741 100644 --- a/mitab/mitab_idfile.cpp +++ b/mitab/mitab_idfile.cpp @@ -154,7 +154,7 @@ int TABIDFile::Open(const char *pszFname, const char *pszAccess) /*----------------------------------------------------------------- * Open file *----------------------------------------------------------------*/ - m_fp = VSIFOpen(m_pszFname, pszAccess); + m_fp = VSIFOpenL(m_pszFname, pszAccess); if (m_fp == NULL) { @@ -171,8 +171,8 @@ int TABIDFile::Open(const char *pszFname, const char *pszAccess) * READ access: * Establish the number of object IDs from the size of the file *------------------------------------------------------------*/ - VSIStatBuf sStatBuf; - if ( VSIStat(m_pszFname, &sStatBuf) == -1 ) + VSIStatBufL sStatBuf; + if ( VSIStatL(m_pszFname, &sStatBuf) == -1 ) { CPLError(CE_Failure, CPLE_FileIO, "stat() failed for %s\n", m_pszFname); @@ -242,7 +242,7 @@ int TABIDFile::Close() m_poIDBlock = NULL; // Close file - VSIFClose(m_fp); + VSIFCloseL(m_fp); m_fp = NULL; CPLFree(m_pszFname); diff --git a/mitab/mitab_imapinfofile.cpp b/mitab/mitab_imapinfofile.cpp index 9c7d2d0..c1702e2 100644 --- a/mitab/mitab_imapinfofile.cpp +++ b/mitab/mitab_imapinfofile.cpp @@ -206,14 +206,14 @@ IMapInfoFile *IMapInfoFile::SmartOpen(const char *pszFname, * .TAB file ... is it a TABFileView or a TABFile? * We have to read the .tab header to find out. *------------------------------------------------------------*/ - FILE *fp; + VSILFILE *fp; const char *pszLine; char *pszAdjFname = CPLStrdup(pszFname); GBool bFoundFields = FALSE, bFoundView=FALSE, bFoundSeamless=FALSE; TABAdjustFilenameExtension(pszAdjFname); - fp = VSIFOpen(pszAdjFname, "r"); - while(fp && (pszLine = CPLReadLine(fp)) != NULL) + fp = VSIFOpenL(pszAdjFname, "r"); + while(fp && (pszLine = CPLReadLineL(fp)) != NULL) { while (isspace((unsigned char)*pszLine)) pszLine++; if (EQUALN(pszLine, "Fields", 6)) @@ -232,7 +232,7 @@ IMapInfoFile *IMapInfoFile::SmartOpen(const char *pszFname, poFile = new TABFile; if (fp) - VSIFClose(fp); + VSIFCloseL(fp); CPLFree(pszAdjFname); } diff --git a/mitab/mitab_indfile.cpp b/mitab/mitab_indfile.cpp index 883de44..8e01ef2 100644 --- a/mitab/mitab_indfile.cpp +++ b/mitab/mitab_indfile.cpp @@ -185,7 +185,7 @@ int TABINDFile::Open(const char *pszFname, const char *pszAccess, /*----------------------------------------------------------------- * Open file *----------------------------------------------------------------*/ - m_fp = VSIFOpen(m_pszFname, pszAccess); + m_fp = VSIFOpenL(m_pszFname, pszAccess); if (m_fp == NULL) { @@ -282,7 +282,7 @@ int TABINDFile::Close() /*----------------------------------------------------------------- * Close file *----------------------------------------------------------------*/ - VSIFClose(m_fp); + VSIFCloseL(m_fp); m_fp = NULL; CPLFree(m_pszFname); @@ -309,8 +309,8 @@ int TABINDFile::ReadHeader() /*----------------------------------------------------------------- * In ReadWrite mode, we need to init BlockManager with file size *----------------------------------------------------------------*/ - VSIStatBuf sStatBuf; - if (m_eAccessMode == TABReadWrite && VSIStat(m_pszFname, &sStatBuf) != -1) + VSIStatBufL sStatBuf; + if (m_eAccessMode == TABReadWrite && VSIStatL(m_pszFname, &sStatBuf) != -1) { m_oBlockManager.SetLastPtr(((sStatBuf.st_size-1)/512)*512); } @@ -948,7 +948,7 @@ TABINDNode::~TABINDNode() * * Returns 0 on success, -1 on error. **********************************************************************/ -int TABINDNode::InitNode(FILE *fp, int nBlockPtr, +int TABINDNode::InitNode(VSILFILE *fp, int nBlockPtr, int nKeyLength, int nSubTreeDepth, GBool bUnique, TABBinBlockManager *poBlockMgr /*=NULL*/, diff --git a/mitab/mitab_mapcoordblock.cpp b/mitab/mitab_mapcoordblock.cpp index 5b17278..dd18dc0 100644 --- a/mitab/mitab_mapcoordblock.cpp +++ b/mitab/mitab_mapcoordblock.cpp @@ -147,7 +147,7 @@ TABMAPCoordBlock::~TABMAPCoordBlock() int TABMAPCoordBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy /* = TRUE */, - FILE *fpSrc /* = NULL */, + VSILFILE *fpSrc /* = NULL */, int nOffset /* = 0 */) { int nStatus; @@ -254,7 +254,7 @@ int TABMAPCoordBlock::CommitToFile() * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABMAPCoordBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, +int TABMAPCoordBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset /* = 0*/) { CPLErrorReset(); diff --git a/mitab/mitab_mapfile.cpp b/mitab/mitab_mapfile.cpp index d5cb734..5aa17df 100644 --- a/mitab/mitab_mapfile.cpp +++ b/mitab/mitab_mapfile.cpp @@ -251,7 +251,7 @@ TABMAPFile::~TABMAPFile() int TABMAPFile::Open(const char *pszFname, const char *pszAccess, GBool bNoErrorMsg /* = FALSE */) { - FILE *fp=NULL; + VSILFILE *fp=NULL; TABRawBinBlock *poBlock=NULL; if (m_fp) @@ -291,7 +291,7 @@ int TABMAPFile::Open(const char *pszFname, const char *pszAccess, /*----------------------------------------------------------------- * Open file *----------------------------------------------------------------*/ - fp = VSIFOpen(pszFname, pszAccess); + fp = VSIFOpenL(pszFname, pszAccess); // TODO: In Read/Write mode we should also preload the chain of deleted // blocks in the blockManager. Not needed for read-only or write-only. @@ -319,7 +319,7 @@ int TABMAPFile::Open(const char *pszFname, const char *pszAccess, if (poBlock) delete poBlock; poBlock = NULL; - VSIFClose(fp); + VSIFCloseL(fp); CPLError(CE_Failure, CPLE_FileIO, "Open() failed: %s does not appear to be a valid .MAP file", pszFname); @@ -552,7 +552,7 @@ int TABMAPFile::Close() // Close file if (m_fp) - VSIFClose(m_fp); + VSIFCloseL(m_fp); m_fp = NULL; CPLFree(m_pszFname); @@ -2204,8 +2204,8 @@ TABRawBinBlock *TABMAPFile::GetIndexObjectBlock( int nFileOffset ) *---------------------------------------------------------------*/ GByte abyData[512]; - if (VSIFSeek(m_fp, nFileOffset, SEEK_SET) != 0 - || VSIFRead(abyData, sizeof(GByte), 512, m_fp) != 512 ) + if (VSIFSeekL(m_fp, nFileOffset, SEEK_SET) != 0 + || VSIFReadL(abyData, sizeof(GByte), 512, m_fp) != 512 ) { CPLError(CE_Failure, CPLE_FileIO, "GetIndexBlock() failed reading %d bytes at offset %d.", diff --git a/mitab/mitab_mapheaderblock.cpp b/mitab/mitab_mapheaderblock.cpp index b8a53bd..c593f06 100644 --- a/mitab/mitab_mapheaderblock.cpp +++ b/mitab/mitab_mapheaderblock.cpp @@ -266,7 +266,7 @@ TABMAPHeaderBlock::~TABMAPHeaderBlock() int TABMAPHeaderBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy /* = TRUE */, - FILE *fpSrc /* = NULL */, + VSILFILE *fpSrc /* = NULL */, int nOffset /* = 0 */) { int i, nStatus; @@ -909,7 +909,7 @@ int TABMAPHeaderBlock::CommitToFile() * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABMAPHeaderBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, +int TABMAPHeaderBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset /* = 0*/) { int i; diff --git a/mitab/mitab_mapindexblock.cpp b/mitab/mitab_mapindexblock.cpp index 77d3eb3..32a4cdc 100644 --- a/mitab/mitab_mapindexblock.cpp +++ b/mitab/mitab_mapindexblock.cpp @@ -137,7 +137,7 @@ TABMAPIndexBlock::~TABMAPIndexBlock() int TABMAPIndexBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy /* = TRUE */, - FILE *fpSrc /* = NULL */, + VSILFILE *fpSrc /* = NULL */, int nOffset /* = 0 */) { int nStatus; @@ -263,7 +263,7 @@ int TABMAPIndexBlock::CommitToFile() * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABMAPIndexBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, +int TABMAPIndexBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset /* = 0*/) { /*----------------------------------------------------------------- diff --git a/mitab/mitab_mapobjectblock.cpp b/mitab/mitab_mapobjectblock.cpp index 9c664c5..cfcf66b 100644 --- a/mitab/mitab_mapobjectblock.cpp +++ b/mitab/mitab_mapobjectblock.cpp @@ -155,7 +155,7 @@ TABMAPObjectBlock::~TABMAPObjectBlock() int TABMAPObjectBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy /* = TRUE */, - FILE *fpSrc /* = NULL */, + VSILFILE *fpSrc /* = NULL */, int nOffset /* = 0 */) { int nStatus; @@ -335,7 +335,7 @@ int TABMAPObjectBlock::CommitToFile() * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABMAPObjectBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, +int TABMAPObjectBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset /* = 0*/) { /*----------------------------------------------------------------- diff --git a/mitab/mitab_maptoolblock.cpp b/mitab/mitab_maptoolblock.cpp index ce7237e..f79e130 100644 --- a/mitab/mitab_maptoolblock.cpp +++ b/mitab/mitab_maptoolblock.cpp @@ -123,7 +123,7 @@ GBool TABMAPToolBlock::EndOfChain() int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy /* = TRUE */, - FILE *fpSrc /* = NULL */, + VSILFILE *fpSrc /* = NULL */, int nOffset /* = 0 */) { int nStatus; @@ -224,7 +224,7 @@ int TABMAPToolBlock::CommitToFile() * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABMAPToolBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, +int TABMAPToolBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset /* = 0*/) { /*----------------------------------------------------------------- diff --git a/mitab/mitab_middatafile.cpp b/mitab/mitab_middatafile.cpp index 7267d98..fe3b3cf 100644 --- a/mitab/mitab_middatafile.cpp +++ b/mitab/mitab_middatafile.cpp @@ -152,7 +152,7 @@ int MIDDATAFile::Open(const char *pszFname, const char *pszAccess) * Open file for reading *----------------------------------------------------------------*/ m_pszFname = CPLStrdup(pszFname); - m_fp = VSIFOpen(m_pszFname, pszAccess); + m_fp = VSIFOpenL(m_pszFname, pszAccess); if (m_fp == NULL) { @@ -161,7 +161,7 @@ int MIDDATAFile::Open(const char *pszFname, const char *pszAccess) return -1; } - SetEof(VSIFEof(m_fp)); + SetEof(FALSE); return 0; } @@ -172,8 +172,8 @@ int MIDDATAFile::Rewind() else { - VSIRewind(m_fp); - SetEof(VSIFEof(m_fp)); + VSIRewindL(m_fp); + SetEof(FALSE); } return 0; } @@ -184,11 +184,11 @@ int MIDDATAFile::Close() return 0; // Close file - VSIFClose(m_fp); + VSIFCloseL(m_fp); m_fp = NULL; // clear readline buffer. - CPLReadLine( NULL ); + CPLReadLineL( NULL ); CPLFree(m_pszFname); m_pszFname = NULL; @@ -204,12 +204,11 @@ const char *MIDDATAFile::GetLine() if (m_eAccessMode == TABRead) { - pszLine = CPLReadLine(m_fp); - - SetEof(VSIFEof(m_fp)); + pszLine = CPLReadLineL(m_fp); if (pszLine == NULL) { + SetEof(TRUE); m_szLastRead[0] = '\0'; } else @@ -255,7 +254,9 @@ void MIDDATAFile::WriteLine(const char *pszFormat,...) if (m_eAccessMode == TABWrite && m_fp) { va_start(args, pszFormat); - vfprintf( m_fp, pszFormat, args ); + CPLString osStr; + osStr.vPrintf( pszFormat, args ); + VSIFWriteL( osStr.c_str(), 1, osStr.size(), m_fp); va_end(args); } else diff --git a/mitab/mitab_ogr_datasource.cpp b/mitab/mitab_ogr_datasource.cpp index b5488a1..b12d83e 100644 --- a/mitab/mitab_ogr_datasource.cpp +++ b/mitab/mitab_ogr_datasource.cpp @@ -128,7 +128,7 @@ OGRTABDataSource::~OGRTABDataSource() int OGRTABDataSource::Create( const char * pszName, char **papszOptions ) { - VSIStatBuf sStat; + VSIStatBufL sStat; const char *pszOpt; CPLAssert( m_pszName == NULL ); @@ -152,7 +152,7 @@ int OGRTABDataSource::Create( const char * pszName, char **papszOptions ) /* -------------------------------------------------------------------- */ if( strlen(CPLGetExtension(pszName)) == 0 ) { - if( VSIStat( pszName, &sStat ) == 0 ) + if( VSIStatL( pszName, &sStat ) == 0 ) { if( !VSI_ISDIR(sStat.st_mode) ) { @@ -212,48 +212,31 @@ int OGRTABDataSource::Create( const char * pszName, char **papszOptions ) /* Open an existing file, or directory of files. */ /************************************************************************/ -int OGRTABDataSource::Open( const char * pszName, int bTestOpen ) +int OGRTABDataSource::Open( GDALOpenInfo* poOpenInfo, int bTestOpen ) { - VSIStatBuf stat; - CPLAssert( m_pszName == NULL ); - - m_pszName = CPLStrdup( pszName ); -/* -------------------------------------------------------------------- */ -/* Is this a file or directory? */ -/* -------------------------------------------------------------------- */ - if( VSIStat( pszName, &stat ) != 0 - || (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)) ) - { - if( !bTestOpen ) - { - CPLError( CE_Failure, CPLE_OpenFailed, - "%s is not a file or directory.\n" - "Unable to open as a Mapinfo dataset.\n", - pszName ); - } - - return FALSE; - } + m_pszName = CPLStrdup( poOpenInfo->pszFilename ); /* -------------------------------------------------------------------- */ /* If it is a file, try to open as a Mapinfo file. */ /* -------------------------------------------------------------------- */ - if( VSI_ISREG(stat.st_mode) ) + if( !poOpenInfo->bIsDirectory ) { IMapInfoFile *poFile; - poFile = IMapInfoFile::SmartOpen( pszName, bTestOpen ); + poFile = IMapInfoFile::SmartOpen( m_pszName, bTestOpen ); if( poFile == NULL ) return FALSE; + poFile->SetDescription( poFile->GetName() ); + m_nLayerCount = 1; m_papoLayers = (IMapInfoFile **) CPLMalloc(sizeof(void*)); m_papoLayers[0] = poFile; - m_pszDirectory = CPLStrdup( CPLGetPath(pszName) ); + m_pszDirectory = CPLStrdup( CPLGetPath(m_pszName) ); } /* -------------------------------------------------------------------- */ @@ -262,9 +245,9 @@ int OGRTABDataSource::Open( const char * pszName, int bTestOpen ) /* -------------------------------------------------------------------- */ else { - char **papszFileList = CPLReadDir( pszName ); + char **papszFileList = CPLReadDir( m_pszName ); - m_pszDirectory = CPLStrdup( pszName ); + m_pszDirectory = CPLStrdup( m_pszName ); for( int iFile = 0; papszFileList != NULL && papszFileList[iFile] != NULL; @@ -288,6 +271,7 @@ int OGRTABDataSource::Open( const char * pszName, int bTestOpen ) CSLDestroy( papszFileList ); return FALSE; } + poFile->SetDescription( poFile->GetName() ); m_nLayerCount++; m_papoLayers = (IMapInfoFile **) @@ -338,11 +322,11 @@ OGRLayer *OGRTABDataSource::GetLayer( int iLayer ) } /************************************************************************/ -/* CreateLayer() */ +/* ICreateLayer() */ /************************************************************************/ OGRLayer * -OGRTABDataSource::CreateLayer( const char * pszLayerName, +OGRTABDataSource::ICreateLayer( const char * pszLayerName, OGRSpatialReference *poSRSIn, OGRwkbGeometryType /* eGeomTypeIn */, char ** /* papszOptions */ ) @@ -396,6 +380,8 @@ OGRTABDataSource::CreateLayer( const char * pszLayerName, delete poFile; return FALSE; } + + poFile->SetDescription( poFile->GetName() ); m_nLayerCount++; m_papoLayers = (IMapInfoFile **) @@ -443,3 +429,68 @@ int OGRTABDataSource::TestCapability( const char * pszCap ) return FALSE; } +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **OGRTABDataSource::GetFileList() +{ + VSIStatBufL sStatBuf; + CPLStringList osList; + + VSIStatL( m_pszName, &sStatBuf ); + if( VSI_ISDIR(sStatBuf.st_mode) ) + { + static const char *apszExtensions[] = + { "mif", "mid", "tab", "map", "ind", "dat", "id", NULL }; + char **papszDirEntries = CPLReadDir( m_pszName ); + int iFile; + + for( iFile = 0; + papszDirEntries != NULL && papszDirEntries[iFile] != NULL; + iFile++ ) + { + if( CSLFindString( (char **) apszExtensions, + CPLGetExtension(papszDirEntries[iFile])) != -1) + { + osList.AddString( CPLFormFilename( m_pszName, + papszDirEntries[iFile], + NULL ) ); + } + } + + CSLDestroy( papszDirEntries ); + } + else + { + static const char* apszMIFExtensions[] = { "mif", "mid", NULL }; + static const char* apszTABExtensions[] = { "tab", "map", "ind", "dat", "id", NULL }; + const char** papszExtensions; + if( EQUAL(CPLGetExtension(m_pszName), "mif") || + EQUAL(CPLGetExtension(m_pszName), "mid") ) + { + papszExtensions = apszMIFExtensions; + } + else + { + papszExtensions = apszTABExtensions; + } + const char** papszIter = papszExtensions; + while( *papszIter ) + { + const char *pszFile = CPLResetExtension(m_pszName, *papszIter ); + if( VSIStatL( pszFile, &sStatBuf ) != 0) + { + pszFile = CPLResetExtension(m_pszName, CPLString(*papszIter).toupper() ); + if( VSIStatL( pszFile, &sStatBuf ) != 0) + { + pszFile = NULL; + } + } + if( pszFile ) + osList.AddString( pszFile ); + papszIter ++; + } + } + return osList.StealList(); +} diff --git a/mitab/mitab_ogr_driver.cpp b/mitab/mitab_ogr_driver.cpp index 9b452f1..c140bb7 100644 --- a/mitab/mitab_ogr_driver.cpp +++ b/mitab/mitab_ogr_driver.cpp @@ -75,41 +75,61 @@ /************************************************************************/ -/* ~OGRTABDriver() */ +/* OGRTABDriverIdentify() */ /************************************************************************/ -OGRTABDriver::~OGRTABDriver() +static int OGRTABDriverIdentify( GDALOpenInfo* poOpenInfo ) { -} - -/************************************************************************/ -/* OGRTABDriver::GetName() */ -/************************************************************************/ - -const char *OGRTABDriver::GetName() - -{ - return "MapInfo File"; + /* Files not ending with .tab, .mif or .mid are not handled by this driver */ + if( !poOpenInfo->bStatOK ) + return FALSE; + if( poOpenInfo->bIsDirectory ) + return -1; /* unsure */ + if( poOpenInfo->fpL == NULL ) + return FALSE; + if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MIF") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MID") ) + { + return TRUE; + } + if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "TAB") ) + { + for( int i = 0; i < poOpenInfo->nHeaderBytes; i++) + { + const char* pszLine = (const char*)poOpenInfo->pabyHeader + i; + if (EQUALN(pszLine, "Fields", 6)) + return TRUE; + else if (EQUALN(pszLine, "create view", 11)) + return TRUE; + else if (EQUALN(pszLine, "\"\\IsSeamless\" = \"TRUE\"", 21)) + return TRUE; + } + } + return FALSE; } /************************************************************************/ /* OGRTABDriver::Open() */ /************************************************************************/ -OGRDataSource *OGRTABDriver::Open( const char * pszFilename, - int bUpdate ) +static GDALDataset *OGRTABDriverOpen( GDALOpenInfo* poOpenInfo ) { OGRTABDataSource *poDS; - - if( bUpdate ) + + if( OGRTABDriverIdentify(poOpenInfo) == FALSE ) + { + return NULL; + } + + if( poOpenInfo->eAccess == GA_Update ) { return NULL; } poDS = new OGRTABDataSource(); - if( poDS->Open( pszFilename, TRUE ) ) + if( poDS->Open( poOpenInfo, TRUE ) ) return poDS; else { @@ -120,11 +140,12 @@ OGRDataSource *OGRTABDriver::Open( const char * pszFilename, /************************************************************************/ -/* CreateDataSource() */ +/* Create() */ /************************************************************************/ -OGRDataSource *OGRTABDriver::CreateDataSource( const char * pszName, - char ** papszOptions ) +static GDALDataset *OGRTABDriverCreate( const char * pszName, + int nBands, int nXSize, int nYSize, GDALDataType eDT, + char **papszOptions ) { OGRTABDataSource *poDS; @@ -143,78 +164,35 @@ OGRDataSource *OGRTABDriver::CreateDataSource( const char * pszName, } /************************************************************************/ -/* TestCapability() */ -/************************************************************************/ - -int OGRTABDriver::TestCapability( const char * pszCap ) - -{ - if( EQUAL(pszCap,ODrCCreateDataSource) ) - return TRUE; - else if( EQUAL(pszCap,ODrCDeleteDataSource) ) - return TRUE; - else - return FALSE; -} - -/************************************************************************/ -/* DeleteDataSource() */ +/* Delete() */ /************************************************************************/ -OGRErr OGRTABDriver::DeleteDataSource( const char *pszDataSource ) +static CPLErr OGRTABDriverDelete( const char *pszDataSource ) { - int iExt; - VSIStatBuf sStatBuf; - static const char *apszExtensions[] = - { "mif", "mid", "tab", "map", "ind", "dat", "id", NULL }; - - if( VSIStat( pszDataSource, &sStatBuf ) != 0 ) + GDALOpenInfo oOpenInfo(pszDataSource, GA_ReadOnly); + GDALDataset* poDS = OGRTABDriverOpen(&oOpenInfo); + if( poDS == NULL ) + return CE_Failure; + char** papszFileList = poDS->GetFileList(); + delete poDS; + + char** papszIter = papszFileList; + while( papszIter && *papszIter ) { - CPLError( CE_Failure, CPLE_AppDefined, - "%s does not appear to be a file or directory.", - pszDataSource ); - - return OGRERR_FAILURE; + VSIUnlink( *papszIter ); + papszIter ++; } + CSLDestroy(papszFileList); - if( VSI_ISREG(sStatBuf.st_mode) - && (EQUAL(CPLGetExtension(pszDataSource),"mif") - || EQUAL(CPLGetExtension(pszDataSource),"mid") - || EQUAL(CPLGetExtension(pszDataSource),"tab")) ) - { - for( iExt=0; apszExtensions[iExt] != NULL; iExt++ ) - { - const char *pszFile = CPLResetExtension(pszDataSource, - apszExtensions[iExt] ); - if( VSIStat( pszFile, &sStatBuf ) == 0 ) - VSIUnlink( pszFile ); - } - } - else if( VSI_ISDIR(sStatBuf.st_mode) ) + VSIStatBufL sStatBuf; + if( VSIStatL( pszDataSource, &sStatBuf ) == 0 && + VSI_ISDIR(sStatBuf.st_mode) ) { - char **papszDirEntries = CPLReadDir( pszDataSource ); - int iFile; - - for( iFile = 0; - papszDirEntries != NULL && papszDirEntries[iFile] != NULL; - iFile++ ) - { - if( CSLFindString( (char **) apszExtensions, - CPLGetExtension(papszDirEntries[iFile])) != -1) - { - VSIUnlink( CPLFormFilename( pszDataSource, - papszDirEntries[iFile], - NULL ) ); - } - } - - CSLDestroy( papszDirEntries ); - VSIRmdir( pszDataSource ); } - return OGRERR_NONE; + return CE_None; } /************************************************************************/ @@ -227,7 +205,29 @@ extern "C" void RegisterOGRTAB() { - OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRTABDriver ); + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MapInfo File" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MapInfo File" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MapInfo File" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "tab mif mid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_mitab.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRTABDriverOpen; + poDriver->pfnIdentify = OGRTABDriverIdentify; + poDriver->pfnCreate = OGRTABDriverCreate; + poDriver->pfnDelete = OGRTABDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } } } diff --git a/mitab/mitab_ogr_driver.h b/mitab/mitab_ogr_driver.h index 4733875..d8c0e55 100644 --- a/mitab/mitab_ogr_driver.h +++ b/mitab/mitab_ogr_driver.h @@ -107,7 +107,7 @@ class OGRTABDataSource : public OGRDataSource OGRTABDataSource(); virtual ~OGRTABDataSource(); - int Open( const char *pszName, int bTestOpen ); + int Open( GDALOpenInfo* poOpenInfo, int bTestOpen ); int Create( const char *pszName, char ** papszOptions ); const char *GetName() { return m_pszName; } @@ -115,26 +115,12 @@ class OGRTABDataSource : public OGRDataSource OGRLayer *GetLayer( int ); int TestCapability( const char * ); - OGRLayer *CreateLayer(const char *, + OGRLayer *ICreateLayer(const char *, OGRSpatialReference * = NULL, OGRwkbGeometryType = wkbUnknown, char ** = NULL ); -}; - -/************************************************************************/ -/* OGRTABDriver */ -/************************************************************************/ - -class OGRTABDriver : public OGRSFDriver -{ -public: - virtual ~OGRTABDriver(); - const char *GetName(); - OGRDataSource *Open ( const char *,int ); - int TestCapability( const char * ); - virtual OGRDataSource *CreateDataSource( const char *, char ** = NULL ); - virtual OGRErr DeleteDataSource( const char * ); + char **GetFileList(); }; void CPL_DLL RegisterOGRTAB(); diff --git a/mitab/mitab_priv.h b/mitab/mitab_priv.h index 1096057..43df9e8 100644 --- a/mitab/mitab_priv.h +++ b/mitab/mitab_priv.h @@ -793,7 +793,7 @@ class TABBinBlockManager class TABRawBinBlock { protected: - FILE *m_fp; /* Associated file handle */ + VSILFILE *m_fp; /* Associated file handle */ TABAccess m_eAccess; /* Read/Write access mode */ int m_nBlockType; @@ -815,15 +815,15 @@ class TABRawBinBlock GBool bHardBlockSize = TRUE); virtual ~TABRawBinBlock(); - virtual int ReadFromFile(FILE *fpSrc, int nOffset, int nSize = 512); + virtual int ReadFromFile(VSILFILE *fpSrc, int nOffset, int nSize = 512); virtual int CommitToFile(); int CommitAsDeleted(GInt32 nNextBlockPtr); virtual int InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy = TRUE, - FILE *fpSrc = NULL, int nOffset = 0); - virtual int InitNewBlock(FILE *fpSrc, int nBlockSize, int nFileOffset=0); + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); int GetBlockType(); virtual int GetBlockClass() { return TAB_RAWBIN_BLOCK; }; @@ -889,8 +889,8 @@ class TABMAPHeaderBlock: public TABRawBinBlock virtual int InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy = TRUE, - FILE *fpSrc = NULL, int nOffset = 0); - virtual int InitNewBlock(FILE *fpSrc, int nBlockSize, int nFileOffset=0); + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); virtual int GetBlockClass() { return TABMAP_HEADER_BLOCK; }; @@ -996,8 +996,8 @@ class TABMAPIndexBlock: public TABRawBinBlock virtual int InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy = TRUE, - FILE *fpSrc = NULL, int nOffset = 0); - virtual int InitNewBlock(FILE *fpSrc, int nBlockSize, int nFileOffset=0); + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); virtual int CommitToFile(); virtual int GetBlockClass() { return TABMAP_INDEX_BLOCK; }; @@ -1097,8 +1097,8 @@ class TABMAPObjectBlock: public TABRawBinBlock virtual int InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy = TRUE, - FILE *fpSrc = NULL, int nOffset = 0); - virtual int InitNewBlock(FILE *fpSrc, int nBlockSize, int nFileOffset=0); + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); virtual int GetBlockClass() { return TABMAP_OBJECT_BLOCK; }; @@ -1173,8 +1173,8 @@ class TABMAPCoordBlock: public TABRawBinBlock virtual int InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy = TRUE, - FILE *fpSrc = NULL, int nOffset = 0); - virtual int InitNewBlock(FILE *fpSrc, int nBlockSize, int nFileOffset=0); + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); virtual int CommitToFile(); virtual int GetBlockClass() { return TABMAP_COORD_BLOCK; }; @@ -1240,8 +1240,8 @@ class TABMAPToolBlock: public TABRawBinBlock virtual int InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy = TRUE, - FILE *fpSrc = NULL, int nOffset = 0); - virtual int InitNewBlock(FILE *fpSrc, int nBlockSize, int nFileOffset=0); + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); virtual int CommitToFile(); virtual int GetBlockClass() { return TABMAP_TOOL_BLOCK; }; @@ -1279,7 +1279,7 @@ class TABIDFile { private: char *m_pszFname; - FILE *m_fp; + VSILFILE *m_fp; TABAccess m_eAccessMode; TABRawBinBlock *m_poIDBlock; @@ -1316,7 +1316,7 @@ class TABMAPFile private: int m_nMinTABVersion; char *m_pszFname; - FILE *m_fp; + VSILFILE *m_fp; TABAccess m_eAccessMode; TABBinBlockManager m_oBlockManager; @@ -1451,7 +1451,7 @@ class TABMAPFile class TABINDNode { private: - FILE *m_fp; + VSILFILE *m_fp; TABAccess m_eAccessMode; TABINDNode *m_poCurChildNode; TABINDNode *m_poParentNodeRef; @@ -1485,7 +1485,7 @@ class TABINDNode TABINDNode(TABAccess eAccessMode = TABRead); ~TABINDNode(); - int InitNode(FILE *fp, int nBlockPtr, + int InitNode(VSILFILE *fp, int nBlockPtr, int nKeyLength, int nSubTreeDepth, GBool bUnique, TABBinBlockManager *poBlockMgr=NULL, TABINDNode *poParentNode=NULL, @@ -1543,7 +1543,7 @@ class TABINDFile { private: char *m_pszFname; - FILE *m_fp; + VSILFILE *m_fp; TABAccess m_eAccessMode; TABBinBlockManager m_oBlockManager; @@ -1594,7 +1594,7 @@ class TABDATFile { private: char *m_pszFname; - FILE *m_fp; + VSILFILE *m_fp; TABAccess m_eAccessMode; TABTableType m_eTableType; @@ -1805,7 +1805,7 @@ class MIDDATAFile GBool GetEof(); private: - FILE *m_fp; + VSILFILE *m_fp; const char *m_pszDelimiter; // Set limit for the length of a line @@ -1828,7 +1828,7 @@ class MIDDATAFile Function prototypes =====================================================================*/ -TABRawBinBlock *TABCreateMAPBlockFromFile(FILE *fpSrc, int nOffset, +TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset, int nSize = 512, GBool bHardBlockSize = TRUE, TABAccess eAccessMode = TABRead); diff --git a/mitab/mitab_rawbinblock.cpp b/mitab/mitab_rawbinblock.cpp index 352288e..7db71f3 100644 --- a/mitab/mitab_rawbinblock.cpp +++ b/mitab/mitab_rawbinblock.cpp @@ -117,7 +117,7 @@ TABRawBinBlock::~TABRawBinBlock() * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABRawBinBlock::ReadFromFile(FILE *fpSrc, int nOffset, +int TABRawBinBlock::ReadFromFile(VSILFILE *fpSrc, int nOffset, int nSize /*= 512*/) { GByte *pabyBuf; @@ -142,8 +142,8 @@ int TABRawBinBlock::ReadFromFile(FILE *fpSrc, int nOffset, /*---------------------------------------------------------------- * Read from the file *---------------------------------------------------------------*/ - if (VSIFSeek(fpSrc, nOffset, SEEK_SET) != 0 || - (m_nSizeUsed = VSIFRead(pabyBuf, sizeof(GByte), nSize, fpSrc) ) == 0 || + if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 || + (m_nSizeUsed = VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc) ) == 0 || (m_bHardBlockSize && m_nSizeUsed != nSize ) ) { CPLError(CE_Failure, CPLE_FileIO, @@ -198,24 +198,24 @@ int TABRawBinBlock::CommitToFile() /*---------------------------------------------------------------- * Move the output file pointer to the right position... *---------------------------------------------------------------*/ - if (VSIFSeek(m_fp, m_nFileOffset, SEEK_SET) != 0) + if (VSIFSeekL(m_fp, m_nFileOffset, SEEK_SET) != 0) { /*------------------------------------------------------------ * Moving pointer failed... we may need to pad with zeros if * block destination is beyond current end of file. *-----------------------------------------------------------*/ int nCurPos; - nCurPos = VSIFTell(m_fp); + nCurPos = (int)VSIFTellL(m_fp); if (nCurPos < m_nFileOffset && - VSIFSeek(m_fp, 0L, SEEK_END) == 0 && - (nCurPos = VSIFTell(m_fp)) < m_nFileOffset) + VSIFSeekL(m_fp, 0L, SEEK_END) == 0 && + (nCurPos = (int)VSIFTellL(m_fp)) < m_nFileOffset) { GByte cZero = 0; while(nCurPos < m_nFileOffset && nStatus == 0) { - if (VSIFWrite(&cZero, 1, 1, m_fp) != 1) + if (VSIFWriteL(&cZero, 1, 1, m_fp) != 1) { CPLError(CE_Failure, CPLE_FileIO, "Failed writing 1 byte at offset %d.", nCurPos); @@ -240,7 +240,7 @@ int TABRawBinBlock::CommitToFile() int numBytesToWrite = m_bHardBlockSize?m_nBlockSize:m_nSizeUsed; if (nStatus != 0 || - VSIFWrite(m_pabyBuf,sizeof(GByte), + VSIFWriteL(m_pabyBuf,sizeof(GByte), numBytesToWrite, m_fp) != (size_t)numBytesToWrite ) { CPLError(CE_Failure, CPLE_FileIO, @@ -249,7 +249,7 @@ int TABRawBinBlock::CommitToFile() return -1; } - fflush(m_fp); + VSIFFlushL(m_fp); m_bModified = FALSE; @@ -317,7 +317,7 @@ int TABRawBinBlock::CommitAsDeleted(GInt32 nNextBlockPtr) int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize, int nSizeUsed, GBool bMakeCopy /* = TRUE */, - FILE *fpSrc /* = NULL */, + VSILFILE *fpSrc /* = NULL */, int nOffset /* = 0 */) { m_fp = fpSrc; @@ -372,7 +372,7 @@ int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf, * Returns 0 if succesful or -1 if an error happened, in which case * CPLError() will have been called. **********************************************************************/ -int TABRawBinBlock::InitNewBlock(FILE *fpSrc, int nBlockSize, +int TABRawBinBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset /* = 0*/) { m_fp = fpSrc; @@ -1028,7 +1028,7 @@ void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/, * Returns the new object if succesful or NULL if an error happened, in * which case CPLError() will have been called. **********************************************************************/ -TABRawBinBlock *TABCreateMAPBlockFromFile(FILE *fpSrc, int nOffset, +TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset, int nSize /*= 512*/, GBool bHardBlockSize /*= TRUE */, TABAccess eAccessMode /*= TABRead*/) @@ -1051,8 +1051,8 @@ TABRawBinBlock *TABCreateMAPBlockFromFile(FILE *fpSrc, int nOffset, /*---------------------------------------------------------------- * Read from the file *---------------------------------------------------------------*/ - if (VSIFSeek(fpSrc, nOffset, SEEK_SET) != 0 || - VSIFRead(pabyBuf, sizeof(GByte), nSize, fpSrc)!=(unsigned int)nSize ) + if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 || + VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc)!=(unsigned int)nSize ) { CPLError(CE_Failure, CPLE_FileIO, "TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.", diff --git a/mitab/mitab_tabfile.cpp b/mitab/mitab_tabfile.cpp index c186507..b6bd6aa 100644 --- a/mitab/mitab_tabfile.cpp +++ b/mitab/mitab_tabfile.cpp @@ -960,7 +960,7 @@ int TABFile::ParseTABFileFields() **********************************************************************/ int TABFile::WriteTABFile() { - FILE *fp; + VSILFILE *fp; if (m_eAccessMode != TABWrite) { @@ -969,12 +969,12 @@ int TABFile::WriteTABFile() return -1; } - if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL) + if ( (fp = VSIFOpenL(m_pszFname, "wt")) != NULL) { - fprintf(fp, "!table\n"); - fprintf(fp, "!version %d\n", m_nVersion); - fprintf(fp, "!charset %s\n", m_pszCharset); - fprintf(fp, "\n"); + VSIFPrintfL(fp, "!table\n"); + VSIFPrintfL(fp, "!version %d\n", m_nVersion); + VSIFPrintfL(fp, "!charset %s\n", m_pszCharset); + VSIFPrintfL(fp, "\n"); if (m_poDefn && m_poDefn->GetFieldCount() > 0) { @@ -982,9 +982,9 @@ int TABFile::WriteTABFile() OGRFieldDefn *poFieldDefn; const char *pszFieldType; - fprintf(fp, "Definition Table\n"); - fprintf(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); - fprintf(fp, " Fields %d\n", m_poDefn->GetFieldCount()); + VSIFPrintfL(fp, "Definition Table\n"); + VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); + VSIFPrintfL(fp, " Fields %d\n", m_poDefn->GetFieldCount()); for(iField=0; iFieldGetFieldCount(); iField++) { @@ -1025,18 +1025,18 @@ int TABFile::WriteTABFile() // Unsupported field type!!! This should never happen. CPLError(CE_Failure, CPLE_AssertionFailed, "WriteTABFile(): Unsupported field type"); - VSIFClose(fp); + VSIFCloseL(fp); return -1; } if (GetFieldIndexNumber(iField) == 0) { - fprintf(fp, " %s %s ;\n", poFieldDefn->GetNameRef(), + VSIFPrintfL(fp, " %s %s ;\n", poFieldDefn->GetNameRef(), pszFieldType ); } else { - fprintf(fp, " %s %s Index %d ;\n", + VSIFPrintfL(fp, " %s %s Index %d ;\n", poFieldDefn->GetNameRef(), pszFieldType, GetFieldIndexNumber(iField) ); } @@ -1045,13 +1045,13 @@ int TABFile::WriteTABFile() } else { - fprintf(fp, "Definition Table\n"); - fprintf(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); - fprintf(fp, " Fields 1\n"); - fprintf(fp, " FID Integer ;\n" ); + VSIFPrintfL(fp, "Definition Table\n"); + VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); + VSIFPrintfL(fp, " Fields 1\n"); + VSIFPrintfL(fp, " FID Integer ;\n" ); } - VSIFClose(fp); + VSIFCloseL(fp); } else { diff --git a/mitab/mitab_tabview.cpp b/mitab/mitab_tabview.cpp index a8ce2f7..39303b6 100644 --- a/mitab/mitab_tabview.cpp +++ b/mitab/mitab_tabview.cpp @@ -662,7 +662,7 @@ int TABView::ParseTABFile(const char *pszDatasetPath, **********************************************************************/ int TABView::WriteTABFile() { - FILE *fp; + VSILFILE *fp; CPLAssert(m_eAccessMode == TABWrite); CPLAssert(m_numTABFiles == 2); @@ -672,37 +672,37 @@ int TABView::WriteTABFile() char *pszTable1 = TABGetBasename(m_papszTABFnames[0]); char *pszTable2 = TABGetBasename(m_papszTABFnames[1]); - if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL) + if ( (fp = VSIFOpenL(m_pszFname, "wt")) != NULL) { // Version is always 100, no matter what the sub-table's version is - fprintf(fp, "!Table\n"); - fprintf(fp, "!Version 100\n"); + VSIFPrintfL(fp, "!Table\n"); + VSIFPrintfL(fp, "!Version 100\n"); - fprintf(fp, "Open Table \"%s\" Hide\n", pszTable1); - fprintf(fp, "Open Table \"%s\" Hide\n", pszTable2); - fprintf(fp, "\n"); - fprintf(fp, "Create View %s As\n", pszTable); - fprintf(fp, "Select "); + VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable1); + VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable2); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "Create View %s As\n", pszTable); + VSIFPrintfL(fp, "Select "); OGRFeatureDefn *poDefn = GetLayerDefn(); for(int iField=0; iFieldGetFieldCount(); iField++) { OGRFieldDefn *poFieldDefn = poDefn->GetFieldDefn(iField); if (iField == 0) - fprintf(fp, "%s", poFieldDefn->GetNameRef()); + VSIFPrintfL(fp, "%s", poFieldDefn->GetNameRef()); else - fprintf(fp, ",%s", poFieldDefn->GetNameRef()); + VSIFPrintfL(fp, ",%s", poFieldDefn->GetNameRef()); } - fprintf(fp, "\n"); + VSIFPrintfL(fp, "\n"); - fprintf(fp, "From %s, %s\n", pszTable2, pszTable1); - fprintf(fp, "Where %s.%s=%s.%s\n", pszTable2, + VSIFPrintfL(fp, "From %s, %s\n", pszTable2, pszTable1); + VSIFPrintfL(fp, "Where %s.%s=%s.%s\n", pszTable2, m_poRelation->GetRelFieldName(), pszTable1, m_poRelation->GetMainFieldName()); - VSIFClose(fp); + VSIFCloseL(fp); } else { diff --git a/mitab/mitab_utils.cpp b/mitab/mitab_utils.cpp index bc30dde..6793ac4 100644 --- a/mitab/mitab_utils.cpp +++ b/mitab/mitab_utils.cpp @@ -215,7 +215,7 @@ GBool TABAdjustCaseSensitiveFilename(char *pszFname) /*----------------------------------------------------------------- * Unix case. *----------------------------------------------------------------*/ - VSIStatBuf sStatBuf; + VSIStatBufL sStatBuf; char *pszTmpPath = NULL; int nTotalLen, iTmpPtr; GBool bValidPath; @@ -223,7 +223,7 @@ GBool TABAdjustCaseSensitiveFilename(char *pszFname) /*----------------------------------------------------------------- * First check if the filename is OK as is. *----------------------------------------------------------------*/ - if (VSIStat(pszFname, &sStatBuf) == 0) + if (VSIStatL(pszFname, &sStatBuf) == 0) { return TRUE; } @@ -248,7 +248,7 @@ GBool TABAdjustCaseSensitiveFilename(char *pszFname) pszTmpPath[--iTmpPtr] = '\0'; } - if (iTmpPtr > 0 && VSIStat(pszTmpPath, &sStatBuf) == 0) + if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) == 0) bValidPath = TRUE; } @@ -300,7 +300,7 @@ GBool TABAdjustCaseSensitiveFilename(char *pszFname) } } - if (iTmpPtr > 0 && VSIStat(pszTmpPath, &sStatBuf) != 0) + if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) != 0) bValidPath = FALSE; CSLDestroy(papszDir); @@ -345,13 +345,13 @@ GBool TABAdjustCaseSensitiveFilename(char *pszFname) **********************************************************************/ GBool TABAdjustFilenameExtension(char *pszFname) { - VSIStatBuf sStatBuf; + VSIStatBufL sStatBuf; int i; /*----------------------------------------------------------------- * First try using filename as provided *----------------------------------------------------------------*/ - if (VSIStat(pszFname, &sStatBuf) == 0) + if (VSIStatL(pszFname, &sStatBuf) == 0) { return TRUE; } @@ -364,7 +364,7 @@ GBool TABAdjustFilenameExtension(char *pszFname) pszFname[i] = (char)toupper(pszFname[i]); } - if (VSIStat(pszFname, &sStatBuf) == 0) + if (VSIStatL(pszFname, &sStatBuf) == 0) { return TRUE; } @@ -377,7 +377,7 @@ GBool TABAdjustFilenameExtension(char *pszFname) pszFname[i] = (char)tolower(pszFname[i]); } - if (VSIStat(pszFname, &sStatBuf) == 0) + if (VSIStatL(pszFname, &sStatBuf) == 0) { return TRUE; } @@ -442,27 +442,27 @@ char *TABGetBasename(const char *pszFname) * * Load a test file into a stringlist. * - * Lines are limited in length by the size fo the CPLReadLine() buffer. + * Lines are limited in length by the size of the CPLReadLine() buffer. **********************************************************************/ char **TAB_CSLLoad(const char *pszFname) { - FILE *fp; + VSILFILE *fp; const char *pszLine; char **papszStrList=NULL; - fp = VSIFOpen(pszFname, "rt"); + fp = VSIFOpenL(pszFname, "rt"); if (fp) { - while(!VSIFEof(fp)) + while(!VSIFEofL(fp)) { - if ( (pszLine = CPLReadLine(fp)) != NULL ) + if ( (pszLine = CPLReadLineL(fp)) != NULL ) { papszStrList = CSLAddString(papszStrList, pszLine); } } - VSIFClose(fp); + VSIFCloseL(fp); } return papszStrList; diff --git a/mitab/ogr2ogr.cpp b/mitab/ogr2ogr.cpp index 72983d5..a13e814 100644 --- a/mitab/ogr2ogr.cpp +++ b/mitab/ogr2ogr.cpp @@ -128,9 +128,9 @@ CPL_CVSID("$Id: ogr2ogr.cpp,v 1.4 2007-03-22 19:48:35 dmorissette Exp $"); static void Usage(); -static int TranslateLayer( OGRDataSource *poSrcDS, +static int TranslateLayer( GDALDataset *poSrcDS, OGRLayer * poSrcLayer, - OGRDataSource *poDstDS, + GDALDataset *poDstDS, char ** papszLSCO, const char *pszNewLayerName, int bTransform, @@ -328,9 +328,9 @@ int main( int nArgc, char ** papszArgv ) /* -------------------------------------------------------------------- */ /* Open data source. */ /* -------------------------------------------------------------------- */ - OGRDataSource *poDS; + GDALDataset *poDS; - poDS = OGRSFDriverRegistrar::Open( pszDataSource, FALSE ); + poDS = (GDALDataset*) OGROpen( pszDataSource, FALSE, NULL ); /* -------------------------------------------------------------------- */ /* Report failure */ @@ -345,7 +345,7 @@ int main( int nArgc, char ** papszArgv ) for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ ) { - printf( " -> %s\n", poR->GetDriver(iDriver)->GetName() ); + printf( " -> %s\n", poR->GetDriver(iDriver)->GetDescription() ); } exit( 1 ); @@ -354,11 +354,11 @@ int main( int nArgc, char ** papszArgv ) /* -------------------------------------------------------------------- */ /* Try opening the output datasource as an existing, writable */ /* -------------------------------------------------------------------- */ - OGRDataSource *poODS; + GDALDataset *poODS; if( bUpdate ) { - poODS = OGRSFDriverRegistrar::Open( pszDestDataSource, TRUE ); + poODS = (GDALDataset*) OGROpen( pszDestDataSource, TRUE, NULL ); if( poODS == NULL ) { printf( "FAILURE:\n" @@ -374,14 +374,14 @@ int main( int nArgc, char ** papszArgv ) else { OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar(); - OGRSFDriver *poDriver = NULL; + GDALDriver *poDriver = NULL; int iDriver; for( iDriver = 0; iDriver < poR->GetDriverCount() && poDriver == NULL; iDriver++ ) { - if( EQUAL(poR->GetDriver(iDriver)->GetName(),pszFormat) ) + if( EQUAL(poR->GetDriver(iDriver)->GetDescription(),pszFormat) ) { poDriver = poR->GetDriver(iDriver); } @@ -394,12 +394,12 @@ int main( int nArgc, char ** papszArgv ) for( iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ ) { - printf( " -> `%s'\n", poR->GetDriver(iDriver)->GetName() ); + printf( " -> `%s'\n", poR->GetDriver(iDriver)->GetDescription() ); } exit( 1 ); } - if( !poDriver->TestCapability( ODrCCreateDataSource ) ) + if( !poDriver->GetMetadataItem( GDAL_DCAP_CREATE ) ) { printf( "%s driver does not support data source creation.\n", pszFormat ); @@ -409,7 +409,7 @@ int main( int nArgc, char ** papszArgv ) /* -------------------------------------------------------------------- */ /* Create the output data source. */ /* -------------------------------------------------------------------- */ - poODS = poDriver->CreateDataSource( pszDestDataSource, papszDSCO ); + poODS = poDriver->Create( pszDestDataSource, 0, 0, 0, GDT_Unknown, papszDSCO ); if( poODS == NULL ) { printf( "%s driver failed to create %s\n", @@ -555,10 +555,10 @@ static void Usage() for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ ) { - OGRSFDriver *poDriver = poR->GetDriver(iDriver); + GDALDriver *poDriver = poR->GetDriver(iDriver); - if( poDriver->TestCapability( ODrCCreateDataSource ) ) - printf( " -f \"%s\"\n", poDriver->GetName() ); + if( poDriver->GetMetadataItem( GDAL_DCAP_CREATE ) ) + printf( " -f \"%s\"\n", poDriver->GetDescription() ); } printf( " -append: Append to existing layer instead of creating new\n" @@ -592,9 +592,9 @@ static void Usage() /* TranslateLayer() */ /************************************************************************/ -static int TranslateLayer( OGRDataSource *poSrcDS, +static int TranslateLayer( GDALDataset *poSrcDS, OGRLayer * poSrcLayer, - OGRDataSource *poDstDS, + GDALDataset *poDstDS, char **papszLCO, const char *pszNewLayerName, int bTransform, diff --git a/mitab/ogrinfo.cpp b/mitab/ogrinfo.cpp index faa6caa..2de4c4e 100644 --- a/mitab/ogrinfo.cpp +++ b/mitab/ogrinfo.cpp @@ -152,18 +152,20 @@ int main( int nArgc, char ** papszArgv ) /* Open data source. */ /* -------------------------------------------------------------------- */ OGRDataSource *poDS; - OGRSFDriver *poDriver; + OGRSFDriverH hDriver; + GDALDriver *poDriver; - poDS = OGRSFDriverRegistrar::Open( pszDataSource, !bReadOnly, &poDriver ); + poDS = (OGRDataSource*) OGROpen( pszDataSource, !bReadOnly, &hDriver ); if( poDS == NULL && !bReadOnly ) { - poDS = OGRSFDriverRegistrar::Open( pszDataSource, FALSE, &poDriver ); + poDS = (OGRDataSource*) OGROpen( pszDataSource, FALSE, &hDriver ); if( poDS != NULL && bVerbose ) { printf( "Had to open data source read-only.\n" ); bReadOnly = TRUE; } } + poDriver = (GDALDriver*) hDriver; /* -------------------------------------------------------------------- */ /* Report failure */ @@ -178,7 +180,7 @@ int main( int nArgc, char ** papszArgv ) for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ ) { - printf( " -> %s\n", poR->GetDriver(iDriver)->GetName() ); + printf( " -> %s\n", poR->GetDriver(iDriver)->GetDescription() ); } exit( 1 ); @@ -190,13 +192,13 @@ int main( int nArgc, char ** papszArgv ) if( bVerbose ) printf( "INFO: Open of `%s'\n" "using driver `%s' successful.\n", - pszDataSource, poDriver->GetName() ); + pszDataSource, poDriver->GetDescription() ); if( bVerbose && !EQUAL(pszDataSource,poDS->GetName()) ) { printf( "INFO: Internal data source name `%s'\n" " different from user name `%s'.\n", - poDS->GetName(), pszDataSource ); + poDS->GetDescription(), pszDataSource ); } /* -------------------------------------------------------------------- */ diff --git a/mitab/tabdump.cpp b/mitab/tabdump.cpp index 88b8058..070801e 100644 --- a/mitab/tabdump.cpp +++ b/mitab/tabdump.cpp @@ -313,22 +313,22 @@ int main(int argc, char *argv[]) **********************************************************************/ static int DumpMapFileBlocks(const char *pszFname) { - FILE *fp; + VSILFILE *fp; TABRawBinBlock *poBlock; int nOffset = 0; - VSIStatBuf sStatBuf; + VSIStatBufL sStatBuf; /*--------------------------------------------------------------------- * Try to open source file * Note: we use stat() to fetch the file size. *--------------------------------------------------------------------*/ - if ( VSIStat(pszFname, &sStatBuf) == -1 ) + if ( VSIStatL(pszFname, &sStatBuf) == -1 ) { printf("stat() failed for %s\n", pszFname); return -1; } - fp = fopen(pszFname, "rb"); + fp = VSIFOpenL(pszFname, "rb"); if (fp == NULL) { printf("Failed to open %s\n", pszFname); @@ -361,7 +361,7 @@ static int DumpMapFileBlocks(const char *pszFname) /*--------------------------------------------------------------------- * Cleanup and exit. *--------------------------------------------------------------------*/ - fclose(fp); + VSIFCloseL(fp); return 0; } @@ -480,14 +480,14 @@ static int DumpMapFileIndexTree2MIF(const char *pszFname, int nMaxDepth) **********************************************************************/ static int DumpMapFileBlockDetails(const char *pszFname, int nOffset) { - FILE *fp; + VSILFILE *fp; TABRawBinBlock *poBlock; /*--------------------------------------------------------------------- * Try to open source file * Note: we use stat() to fetch the file size. *--------------------------------------------------------------------*/ - fp = fopen(pszFname, "rb"); + fp = VSIFOpenL(pszFname, "rb"); if (fp == NULL) { printf("Failed to open %s\n", pszFname); @@ -517,7 +517,7 @@ static int DumpMapFileBlockDetails(const char *pszFname, int nOffset) /*--------------------------------------------------------------------- * Cleanup and exit. *--------------------------------------------------------------------*/ - fclose(fp); + VSIFCloseL(fp); return 0; } diff --git a/nmake.opt b/nmake.opt index 94f4f18..3d1fc44 100755 --- a/nmake.opt +++ b/nmake.opt @@ -1,6 +1,6 @@ # $Id: nmake.opt,v 1.5 2007-11-08 21:51:23 dmorissette Exp $ -INC = -I. -I.. -I../ogr -I../cpl +INC = -I. -I.. -I../ogr -I../cpl -DOGR_ENABLED OPTFLAGS= /Zi /nologo /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE /EHsc CFLAGS = $(OPTFLAGS) $(INC) $(EXTRAFLAGS) diff --git a/ogr/GNUmakefile b/ogr/GNUmakefile index eae3ed1..fbf6756 100755 --- a/ogr/GNUmakefile +++ b/ogr/GNUmakefile @@ -12,7 +12,16 @@ OBJ = ogr_srsnode.o ogrcurve.o ogrfeature.o ogrfeaturedefn.o \ ogrfeaturestyle.o ogr_fromepsg.o ogrfeaturequery.o swq.o \ ogrct.o ogr_gensql.o ogr_srs_xml.o ogr_srs_esri.o \ ogr_api.o gml2ogrgeometry.o ogr2gmlgeometry.o \ - ogr_miattrind.o ogr_attrind.o ogr_srs_dict.o + ogr_miattrind.o ogr_attrind.o ogr_srs_dict.o \ + swq_expr_node.o swq_op_general.o swq_op_registrar.o swq_parser.o swq_select.o \ + gdalmajorobject.o gdaldataset.o gdalrasterband.o gdalmultidomainmetadata.o \ + ogrgeomfielddefn.o gdal_misc.o gdaldriver.o gdaldrivermanager.o \ + gdalcolortable.o rasterio.o gdalrasterblock.o gdaldefaultoverviews.o \ + gdaltransformer.o gdalopeninfo.o gdalclientserver.o gdalpamdataset.o gdalpamrasterband.o \ + gdalnodatamaskband.o gdalnodatavaluesmaskband.o gdalpamproxydb.o gdal_rat.o \ + gdalvirtualmem.o gdalallvalidmaskband.o gdalproxypool.o gdalproxydataset.o gdaldefaultasync.o \ + ogrunionlayer.o ogrwarpedlayer.o ogrlayerdecorator.o overview.o ogr_srs_ozi.o \ + stub.o LIB = ogr.a diff --git a/ogr/gdal.h b/ogr/gdal.h new file mode 100644 index 0000000..8600579 --- /dev/null +++ b/ogr/gdal.h @@ -0,0 +1,900 @@ +/****************************************************************************** + * $Id: gdal.h 27487 2014-07-02 17:13:13Z rouault $ + * + * Project: GDAL Core + * Purpose: GDAL Core C/Public declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2002 Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_H_INCLUDED +#define GDAL_H_INCLUDED + +/** + * \file gdal.h + * + * Public (C callable) GDAL entry points. + */ + +#ifndef DOXYGEN_SKIP +#include "gdal_version.h" +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_progress.h" +#include "cpl_virtualmem.h" +#include "ogr_api.h" +#endif + +/* -------------------------------------------------------------------- */ +/* Significant constants. */ +/* -------------------------------------------------------------------- */ + +CPL_C_START + +/*! Pixel data types */ +typedef enum { + /*! Unknown or unspecified type */ GDT_Unknown = 0, + /*! Eight bit unsigned integer */ GDT_Byte = 1, + /*! Sixteen bit unsigned integer */ GDT_UInt16 = 2, + /*! Sixteen bit signed integer */ GDT_Int16 = 3, + /*! Thirty two bit unsigned integer */ GDT_UInt32 = 4, + /*! Thirty two bit signed integer */ GDT_Int32 = 5, + /*! Thirty two bit floating point */ GDT_Float32 = 6, + /*! Sixty four bit floating point */ GDT_Float64 = 7, + /*! Complex Int16 */ GDT_CInt16 = 8, + /*! Complex Int32 */ GDT_CInt32 = 9, + /*! Complex Float32 */ GDT_CFloat32 = 10, + /*! Complex Float64 */ GDT_CFloat64 = 11, + GDT_TypeCount = 12 /* maximum type # + 1 */ +} GDALDataType; + +int CPL_DLL CPL_STDCALL GDALGetDataTypeSize( GDALDataType ); +int CPL_DLL CPL_STDCALL GDALDataTypeIsComplex( GDALDataType ); +const char CPL_DLL * CPL_STDCALL GDALGetDataTypeName( GDALDataType ); +GDALDataType CPL_DLL CPL_STDCALL GDALGetDataTypeByName( const char * ); +GDALDataType CPL_DLL CPL_STDCALL GDALDataTypeUnion( GDALDataType, GDALDataType ); + +/** +* status of the asynchronous stream +*/ +typedef enum +{ + GARIO_PENDING = 0, + GARIO_UPDATE = 1, + GARIO_ERROR = 2, + GARIO_COMPLETE = 3, + GARIO_TypeCount = 4 +} GDALAsyncStatusType; + +const char CPL_DLL * CPL_STDCALL GDALGetAsyncStatusTypeName( GDALAsyncStatusType ); +GDALAsyncStatusType CPL_DLL CPL_STDCALL GDALGetAsyncStatusTypeByName( const char * ); + +/*! Flag indicating read/write, or read-only access to data. */ +typedef enum { + /*! Read only (no update) access */ GA_ReadOnly = 0, + /*! Read/write access. */ GA_Update = 1 +} GDALAccess; + +/*! Read/Write flag for RasterIO() method */ +typedef enum { + /*! Read data */ GF_Read = 0, + /*! Write data */ GF_Write = 1 +} GDALRWFlag; + +/*! Types of color interpretation for raster bands. */ +typedef enum +{ + GCI_Undefined=0, + /*! Greyscale */ GCI_GrayIndex=1, + /*! Paletted (see associated color table) */ GCI_PaletteIndex=2, + /*! Red band of RGBA image */ GCI_RedBand=3, + /*! Green band of RGBA image */ GCI_GreenBand=4, + /*! Blue band of RGBA image */ GCI_BlueBand=5, + /*! Alpha (0=transparent, 255=opaque) */ GCI_AlphaBand=6, + /*! Hue band of HLS image */ GCI_HueBand=7, + /*! Saturation band of HLS image */ GCI_SaturationBand=8, + /*! Lightness band of HLS image */ GCI_LightnessBand=9, + /*! Cyan band of CMYK image */ GCI_CyanBand=10, + /*! Magenta band of CMYK image */ GCI_MagentaBand=11, + /*! Yellow band of CMYK image */ GCI_YellowBand=12, + /*! Black band of CMLY image */ GCI_BlackBand=13, + /*! Y Luminance */ GCI_YCbCr_YBand=14, + /*! Cb Chroma */ GCI_YCbCr_CbBand=15, + /*! Cr Chroma */ GCI_YCbCr_CrBand=16, + /*! Max current value */ GCI_Max=16 +} GDALColorInterp; + +const char CPL_DLL *GDALGetColorInterpretationName( GDALColorInterp ); +GDALColorInterp CPL_DLL GDALGetColorInterpretationByName( const char *pszName ); + +/*! Types of color interpretations for a GDALColorTable. */ +typedef enum +{ + /*! Grayscale (in GDALColorEntry.c1) */ GPI_Gray=0, + /*! Red, Green, Blue and Alpha in (in c1, c2, c3 and c4) */ GPI_RGB=1, + /*! Cyan, Magenta, Yellow and Black (in c1, c2, c3 and c4)*/ GPI_CMYK=2, + /*! Hue, Lightness and Saturation (in c1, c2, and c3) */ GPI_HLS=3 +} GDALPaletteInterp; + +const char CPL_DLL *GDALGetPaletteInterpretationName( GDALPaletteInterp ); + +/* "well known" metadata items. */ + +#define GDALMD_AREA_OR_POINT "AREA_OR_POINT" +# define GDALMD_AOP_AREA "Area" +# define GDALMD_AOP_POINT "Point" + +/* -------------------------------------------------------------------- */ +/* GDAL Specific error codes. */ +/* */ +/* error codes 100 to 299 reserved for GDAL. */ +/* -------------------------------------------------------------------- */ +#define CPLE_WrongFormat 200 + +/* -------------------------------------------------------------------- */ +/* Define handle types related to various internal classes. */ +/* -------------------------------------------------------------------- */ + +/** Opaque type used for the C bindings of the C++ GDALMajorObject class */ +typedef void *GDALMajorObjectH; + +/** Opaque type used for the C bindings of the C++ GDALDataset class */ +typedef void *GDALDatasetH; + +/** Opaque type used for the C bindings of the C++ GDALRasterBand class */ +typedef void *GDALRasterBandH; + +/** Opaque type used for the C bindings of the C++ GDALDriver class */ +typedef void *GDALDriverH; + +/** Opaque type used for the C bindings of the C++ GDALColorTable class */ +typedef void *GDALColorTableH; + +/** Opaque type used for the C bindings of the C++ GDALRasterAttributeTable class */ +typedef void *GDALRasterAttributeTableH; + +/** Opaque type used for the C bindings of the C++ GDALAsyncReader class */ +typedef void *GDALAsyncReaderH; + +/* ==================================================================== */ +/* Registration/driver related. */ +/* ==================================================================== */ + +#define GDAL_DMD_LONGNAME "DMD_LONGNAME" +#define GDAL_DMD_HELPTOPIC "DMD_HELPTOPIC" +#define GDAL_DMD_MIMETYPE "DMD_MIMETYPE" +#define GDAL_DMD_EXTENSION "DMD_EXTENSION" +#define GDAL_DMD_EXTENSIONS "DMD_EXTENSIONS" +#define GDAL_DMD_CREATIONOPTIONLIST "DMD_CREATIONOPTIONLIST" +#define GDAL_DMD_OPENOPTIONLIST "DMD_OPENOPTIONLIST" +#define GDAL_DMD_CREATIONDATATYPES "DMD_CREATIONDATATYPES" +#define GDAL_DMD_SUBDATASETS "DMD_SUBDATASETS" + +#define GDAL_DCAP_OPEN "DCAP_OPEN" +#define GDAL_DCAP_CREATE "DCAP_CREATE" +#define GDAL_DCAP_CREATECOPY "DCAP_CREATECOPY" +#define GDAL_DCAP_VIRTUALIO "DCAP_VIRTUALIO" + +/** Capability set by a driver having raster capability. */ +#define GDAL_DCAP_RASTER "DCAP_RASTER" +/** Capability set by a driver having vector capability. */ +#define GDAL_DCAP_VECTOR "DCAP_VECTOR" + +void CPL_DLL CPL_STDCALL GDALAllRegister( void ); + +GDALDatasetH CPL_DLL CPL_STDCALL GDALCreate( GDALDriverH hDriver, + const char *, int, int, int, GDALDataType, + char ** ) CPL_WARN_UNUSED_RESULT; +GDALDatasetH CPL_DLL CPL_STDCALL +GDALCreateCopy( GDALDriverH, const char *, GDALDatasetH, + int, char **, GDALProgressFunc, void * ) CPL_WARN_UNUSED_RESULT; + +GDALDriverH CPL_DLL CPL_STDCALL GDALIdentifyDriver( const char * pszFilename, + char ** papszFileList ); +GDALDatasetH CPL_DLL CPL_STDCALL +GDALOpen( const char *pszFilename, GDALAccess eAccess ) CPL_WARN_UNUSED_RESULT; +GDALDatasetH CPL_DLL CPL_STDCALL GDALOpenShared( const char *, GDALAccess ) CPL_WARN_UNUSED_RESULT; + + +/* Note: we define GDAL_OF_READONLY and GDAL_OF_UPDATE to be on purpose */ +/* equals to GA_ReadOnly and GA_Update */ + +/** Open in read-only mode. */ +#define GDAL_OF_READONLY 0x00 +/** Open in update mode. */ +#define GDAL_OF_UPDATE 0x01 + +/** Allow raster and vector drivers. */ +#define GDAL_OF_ALL 0x00 + +/** Allow raster drivers. */ +#define GDAL_OF_RASTER 0x02 +/** Allow vector drivers. */ +#define GDAL_OF_VECTOR 0x04 +/* Some space for GDAL 3.0 new types ;-) */ +/*#define GDAL_OF_OTHER_KIND1 0x08 */ +/*#define GDAL_OF_OTHER_KIND2 0x10 */ +#ifndef DOXYGEN_SKIP +#define GDAL_OF_KIND_MASK 0x1E +#endif + +/** Open in shared mode. */ +#define GDAL_OF_SHARED 0x20 + +/** Emit error message in case of failed open. */ +#define GDAL_OF_VERBOSE_ERROR 0x40 + +GDALDatasetH CPL_DLL CPL_STDCALL GDALOpenEx( const char* pszFilename, + unsigned int nOpenFlags, + const char* const* papszAllowedDrivers, + const char* const* papszOpenOptions, + const char* const* papszSiblingFiles ) CPL_WARN_UNUSED_RESULT; + +int CPL_DLL CPL_STDCALL GDALDumpOpenDatasets( FILE * ); + +GDALDriverH CPL_DLL CPL_STDCALL GDALGetDriverByName( const char * ); +int CPL_DLL CPL_STDCALL GDALGetDriverCount( void ); +GDALDriverH CPL_DLL CPL_STDCALL GDALGetDriver( int ); +void CPL_DLL CPL_STDCALL GDALDestroyDriver( GDALDriverH ); +int CPL_DLL CPL_STDCALL GDALRegisterDriver( GDALDriverH ); +void CPL_DLL CPL_STDCALL GDALDeregisterDriver( GDALDriverH ); +void CPL_DLL CPL_STDCALL GDALDestroyDriverManager( void ); +void CPL_DLL GDALDestroy(); +CPLErr CPL_DLL CPL_STDCALL GDALDeleteDataset( GDALDriverH, const char * ); +CPLErr CPL_DLL CPL_STDCALL GDALRenameDataset( GDALDriverH, + const char * pszNewName, + const char * pszOldName ); +CPLErr CPL_DLL CPL_STDCALL GDALCopyDatasetFiles( GDALDriverH, + const char * pszNewName, + const char * pszOldName); +int CPL_DLL CPL_STDCALL GDALValidateCreationOptions( GDALDriverH, + char** papszCreationOptions); + +/* The following are deprecated */ +const char CPL_DLL * CPL_STDCALL GDALGetDriverShortName( GDALDriverH ); +const char CPL_DLL * CPL_STDCALL GDALGetDriverLongName( GDALDriverH ); +const char CPL_DLL * CPL_STDCALL GDALGetDriverHelpTopic( GDALDriverH ); +const char CPL_DLL * CPL_STDCALL GDALGetDriverCreationOptionList( GDALDriverH ); + +/* ==================================================================== */ +/* GDAL_GCP */ +/* ==================================================================== */ + +/** Ground Control Point */ +typedef struct +{ + /** Unique identifier, often numeric */ + char *pszId; + + /** Informational message or "" */ + char *pszInfo; + + /** Pixel (x) location of GCP on raster */ + double dfGCPPixel; + /** Line (y) location of GCP on raster */ + double dfGCPLine; + + /** X position of GCP in georeferenced space */ + double dfGCPX; + + /** Y position of GCP in georeferenced space */ + double dfGCPY; + + /** Elevation of GCP, or zero if not known */ + double dfGCPZ; +} GDAL_GCP; + +void CPL_DLL CPL_STDCALL GDALInitGCPs( int, GDAL_GCP * ); +void CPL_DLL CPL_STDCALL GDALDeinitGCPs( int, GDAL_GCP * ); +GDAL_GCP CPL_DLL * CPL_STDCALL GDALDuplicateGCPs( int, const GDAL_GCP * ); + +int CPL_DLL CPL_STDCALL +GDALGCPsToGeoTransform( int nGCPCount, const GDAL_GCP *pasGCPs, + double *padfGeoTransform, int bApproxOK ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPL_STDCALL +GDALInvGeoTransform( double *padfGeoTransformIn, + double *padfInvGeoTransformOut ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL CPL_STDCALL GDALApplyGeoTransform( double *, double, double, + double *, double * ); +void CPL_DLL GDALComposeGeoTransforms(const double *padfGeoTransform1, + const double *padfGeoTransform2, + double *padfGeoTransformOut); + +/* ==================================================================== */ +/* major objects (dataset, and, driver, drivermanager). */ +/* ==================================================================== */ + +char CPL_DLL ** CPL_STDCALL GDALGetMetadataDomainList( GDALMajorObjectH hObject ); +char CPL_DLL ** CPL_STDCALL GDALGetMetadata( GDALMajorObjectH, const char * ); +CPLErr CPL_DLL CPL_STDCALL GDALSetMetadata( GDALMajorObjectH, char **, + const char * ); +const char CPL_DLL * CPL_STDCALL +GDALGetMetadataItem( GDALMajorObjectH, const char *, const char * ); +CPLErr CPL_DLL CPL_STDCALL +GDALSetMetadataItem( GDALMajorObjectH, const char *, const char *, + const char * ); +const char CPL_DLL * CPL_STDCALL GDALGetDescription( GDALMajorObjectH ); +void CPL_DLL CPL_STDCALL GDALSetDescription( GDALMajorObjectH, const char * ); + +/* ==================================================================== */ +/* GDALDataset class ... normally this represents one file. */ +/* ==================================================================== */ + +#define GDAL_DS_LAYER_CREATIONOPTIONLIST "DS_LAYER_CREATIONOPTIONLIST" + +GDALDriverH CPL_DLL CPL_STDCALL GDALGetDatasetDriver( GDALDatasetH ); +char CPL_DLL ** CPL_STDCALL GDALGetFileList( GDALDatasetH ); +void CPL_DLL CPL_STDCALL GDALClose( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALGetRasterXSize( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALGetRasterYSize( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALGetRasterCount( GDALDatasetH ); +GDALRasterBandH CPL_DLL CPL_STDCALL GDALGetRasterBand( GDALDatasetH, int ); + +CPLErr CPL_DLL CPL_STDCALL GDALAddBand( GDALDatasetH hDS, GDALDataType eType, + char **papszOptions ); + +GDALAsyncReaderH CPL_DLL CPL_STDCALL +GDALBeginAsyncReader(GDALDatasetH hDS, int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace, + char **papszOptions); + +void CPL_DLL CPL_STDCALL +GDALEndAsyncReader(GDALDatasetH hDS, GDALAsyncReaderH hAsynchReaderH); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetRasterIO( + GDALDatasetH hDS, GDALRWFlag eRWFlag, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + void * pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, + int nBandCount, int *panBandCount, + int nPixelSpace, int nLineSpace, int nBandSpace); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetAdviseRead( GDALDatasetH hDS, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + int nBXSize, int nBYSize, GDALDataType eBDataType, + int nBandCount, int *panBandCount, char **papszOptions ); + +const char CPL_DLL * CPL_STDCALL GDALGetProjectionRef( GDALDatasetH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetProjection( GDALDatasetH, const char * ); +CPLErr CPL_DLL CPL_STDCALL GDALGetGeoTransform( GDALDatasetH, double * ); +CPLErr CPL_DLL CPL_STDCALL GDALSetGeoTransform( GDALDatasetH, double * ); + +int CPL_DLL CPL_STDCALL GDALGetGCPCount( GDALDatasetH ); +const char CPL_DLL * CPL_STDCALL GDALGetGCPProjection( GDALDatasetH ); +const GDAL_GCP CPL_DLL * CPL_STDCALL GDALGetGCPs( GDALDatasetH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetGCPs( GDALDatasetH, int, const GDAL_GCP *, + const char * ); + +void CPL_DLL * CPL_STDCALL GDALGetInternalHandle( GDALDatasetH, const char * ); +int CPL_DLL CPL_STDCALL GDALReferenceDataset( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALDereferenceDataset( GDALDatasetH ); + +CPLErr CPL_DLL CPL_STDCALL +GDALBuildOverviews( GDALDatasetH, const char *, int, int *, + int, int *, GDALProgressFunc, void * ); +void CPL_DLL CPL_STDCALL GDALGetOpenDatasets( GDALDatasetH **hDS, int *pnCount ); +int CPL_DLL CPL_STDCALL GDALGetAccess( GDALDatasetH hDS ); +void CPL_DLL CPL_STDCALL GDALFlushCache( GDALDatasetH hDS ); + +CPLErr CPL_DLL CPL_STDCALL + GDALCreateDatasetMaskBand( GDALDatasetH hDS, int nFlags ); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetCopyWholeRaster( + GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ); + +CPLErr CPL_DLL CPL_STDCALL GDALRasterBandCopyWholeRaster( + GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ); + +CPLErr CPL_DLL +GDALRegenerateOverviews( GDALRasterBandH hSrcBand, + int nOverviewCount, GDALRasterBandH *pahOverviewBands, + const char *pszResampling, + GDALProgressFunc pfnProgress, void *pProgressData ); + +int CPL_DLL GDALDatasetGetLayerCount( GDALDatasetH ); +OGRLayerH CPL_DLL GDALDatasetGetLayer( GDALDatasetH, int ); +OGRLayerH CPL_DLL GDALDatasetGetLayerByName( GDALDatasetH, const char * ); +OGRErr CPL_DLL GDALDatasetDeleteLayer( GDALDatasetH, int ); +OGRLayerH CPL_DLL GDALDatasetCreateLayer( GDALDatasetH, const char *, + OGRSpatialReferenceH, OGRwkbGeometryType, + char ** ); +OGRLayerH CPL_DLL GDALDatasetCopyLayer( GDALDatasetH, OGRLayerH, const char *, + char ** ); +int CPL_DLL GDALDatasetTestCapability( GDALDatasetH, const char * ); +OGRLayerH CPL_DLL GDALDatasetExecuteSQL( GDALDatasetH, const char *, + OGRGeometryH, const char * ); +void CPL_DLL GDALDatasetReleaseResultSet( GDALDatasetH, OGRLayerH ); +OGRStyleTableH CPL_DLL GDALDatasetGetStyleTable( GDALDatasetH ); +void CPL_DLL GDALDatasetSetStyleTableDirectly( GDALDatasetH, OGRStyleTableH ); +void CPL_DLL GDALDatasetSetStyleTable( GDALDatasetH, OGRStyleTableH ); + + +/* ==================================================================== */ +/* GDALRasterBand ... one band/channel in a dataset. */ +/* ==================================================================== */ + +/** + * SRCVAL - Macro which may be used by pixel functions to obtain + * a pixel from a source buffer. + */ +#define SRCVAL(papoSource, eSrcType, ii) \ + (eSrcType == GDT_Byte ? \ + ((GByte *)papoSource)[ii] : \ + (eSrcType == GDT_Float32 ? \ + ((float *)papoSource)[ii] : \ + (eSrcType == GDT_Float64 ? \ + ((double *)papoSource)[ii] : \ + (eSrcType == GDT_Int32 ? \ + ((GInt32 *)papoSource)[ii] : \ + (eSrcType == GDT_UInt16 ? \ + ((GUInt16 *)papoSource)[ii] : \ + (eSrcType == GDT_Int16 ? \ + ((GInt16 *)papoSource)[ii] : \ + (eSrcType == GDT_UInt32 ? \ + ((GUInt32 *)papoSource)[ii] : \ + (eSrcType == GDT_CInt16 ? \ + ((GInt16 *)papoSource)[ii * 2] : \ + (eSrcType == GDT_CInt32 ? \ + ((GInt32 *)papoSource)[ii * 2] : \ + (eSrcType == GDT_CFloat32 ? \ + ((float *)papoSource)[ii * 2] : \ + (eSrcType == GDT_CFloat64 ? \ + ((double *)papoSource)[ii * 2] : 0))))))))))) + +typedef CPLErr +(*GDALDerivedPixelFunc)(void **papoSources, int nSources, void *pData, + int nBufXSize, int nBufYSize, + GDALDataType eSrcType, GDALDataType eBufType, + int nPixelSpace, int nLineSpace); + +GDALDataType CPL_DLL CPL_STDCALL GDALGetRasterDataType( GDALRasterBandH ); +void CPL_DLL CPL_STDCALL +GDALGetBlockSize( GDALRasterBandH, int * pnXSize, int * pnYSize ); + +CPLErr CPL_DLL CPL_STDCALL GDALRasterAdviseRead( GDALRasterBandH hRB, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + int nBXSize, int nBYSize, GDALDataType eBDataType, char **papszOptions ); + +CPLErr CPL_DLL CPL_STDCALL +GDALRasterIO( GDALRasterBandH hRBand, GDALRWFlag eRWFlag, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + void * pBuffer, int nBXSize, int nBYSize,GDALDataType eBDataType, + int nPixelSpace, int nLineSpace ); +CPLErr CPL_DLL CPL_STDCALL GDALReadBlock( GDALRasterBandH, int, int, void * ); +CPLErr CPL_DLL CPL_STDCALL GDALWriteBlock( GDALRasterBandH, int, int, void * ); +int CPL_DLL CPL_STDCALL GDALGetRasterBandXSize( GDALRasterBandH ); +int CPL_DLL CPL_STDCALL GDALGetRasterBandYSize( GDALRasterBandH ); +GDALAccess CPL_DLL CPL_STDCALL GDALGetRasterAccess( GDALRasterBandH ); +int CPL_DLL CPL_STDCALL GDALGetBandNumber( GDALRasterBandH ); +GDALDatasetH CPL_DLL CPL_STDCALL GDALGetBandDataset( GDALRasterBandH ); + +GDALColorInterp CPL_DLL CPL_STDCALL +GDALGetRasterColorInterpretation( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL +GDALSetRasterColorInterpretation( GDALRasterBandH, GDALColorInterp ); +GDALColorTableH CPL_DLL CPL_STDCALL GDALGetRasterColorTable( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterColorTable( GDALRasterBandH, GDALColorTableH ); +int CPL_DLL CPL_STDCALL GDALHasArbitraryOverviews( GDALRasterBandH ); +int CPL_DLL CPL_STDCALL GDALGetOverviewCount( GDALRasterBandH ); +GDALRasterBandH CPL_DLL CPL_STDCALL GDALGetOverview( GDALRasterBandH, int ); +double CPL_DLL CPL_STDCALL GDALGetRasterNoDataValue( GDALRasterBandH, int * ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterNoDataValue( GDALRasterBandH, double ); +char CPL_DLL ** CPL_STDCALL GDALGetRasterCategoryNames( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterCategoryNames( GDALRasterBandH, char ** ); +double CPL_DLL CPL_STDCALL GDALGetRasterMinimum( GDALRasterBandH, int *pbSuccess ); +double CPL_DLL CPL_STDCALL GDALGetRasterMaximum( GDALRasterBandH, int *pbSuccess ); +CPLErr CPL_DLL CPL_STDCALL GDALGetRasterStatistics( + GDALRasterBandH, int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ); +CPLErr CPL_DLL CPL_STDCALL GDALComputeRasterStatistics( + GDALRasterBandH, int bApproxOK, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterStatistics( + GDALRasterBandH hBand, + double dfMin, double dfMax, double dfMean, double dfStdDev ); + +const char CPL_DLL * CPL_STDCALL GDALGetRasterUnitType( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterUnitType( GDALRasterBandH hBand, const char *pszNewValue ); +double CPL_DLL CPL_STDCALL GDALGetRasterOffset( GDALRasterBandH, int *pbSuccess ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterOffset( GDALRasterBandH hBand, double dfNewOffset); +double CPL_DLL CPL_STDCALL GDALGetRasterScale( GDALRasterBandH, int *pbSuccess ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterScale( GDALRasterBandH hBand, double dfNewOffset ); +void CPL_DLL CPL_STDCALL +GDALComputeRasterMinMax( GDALRasterBandH hBand, int bApproxOK, + double adfMinMax[2] ); +CPLErr CPL_DLL CPL_STDCALL GDALFlushRasterCache( GDALRasterBandH hBand ); +CPLErr CPL_DLL CPL_STDCALL GDALGetRasterHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void * pProgressData ); +CPLErr CPL_DLL CPL_STDCALL GDALGetDefaultHistogram( GDALRasterBandH hBand, + double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void * pProgressData ); +CPLErr CPL_DLL CPL_STDCALL GDALSetDefaultHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram ); +int CPL_DLL CPL_STDCALL +GDALGetRandomRasterSample( GDALRasterBandH, int, float * ); +GDALRasterBandH CPL_DLL CPL_STDCALL +GDALGetRasterSampleOverview( GDALRasterBandH, int ); +CPLErr CPL_DLL CPL_STDCALL GDALFillRaster( GDALRasterBandH hBand, + double dfRealValue, double dfImaginaryValue ); +CPLErr CPL_DLL CPL_STDCALL +GDALComputeBandStats( GDALRasterBandH hBand, int nSampleStep, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ); +CPLErr CPL_DLL GDALOverviewMagnitudeCorrection( GDALRasterBandH hBaseBand, + int nOverviewCount, + GDALRasterBandH *pahOverviews, + GDALProgressFunc pfnProgress, + void *pProgressData ); + +GDALRasterAttributeTableH CPL_DLL CPL_STDCALL GDALGetDefaultRAT( + GDALRasterBandH hBand ); +CPLErr CPL_DLL CPL_STDCALL GDALSetDefaultRAT( GDALRasterBandH, + GDALRasterAttributeTableH ); +CPLErr CPL_DLL CPL_STDCALL GDALAddDerivedBandPixelFunc( const char *pszName, + GDALDerivedPixelFunc pfnPixelFunc ); + +GDALRasterBandH CPL_DLL CPL_STDCALL GDALGetMaskBand( GDALRasterBandH hBand ); +int CPL_DLL CPL_STDCALL GDALGetMaskFlags( GDALRasterBandH hBand ); +CPLErr CPL_DLL CPL_STDCALL + GDALCreateMaskBand( GDALRasterBandH hBand, int nFlags ); + +#define GMF_ALL_VALID 0x01 +#define GMF_PER_DATASET 0x02 +#define GMF_ALPHA 0x04 +#define GMF_NODATA 0x08 + +/* ==================================================================== */ +/* GDALAsyncReader */ +/* ==================================================================== */ + +GDALAsyncStatusType CPL_DLL CPL_STDCALL +GDALARGetNextUpdatedRegion(GDALAsyncReaderH hARIO, double dfTimeout, + int* pnXBufOff, int* pnYBufOff, + int* pnXBufSize, int* pnYBufSize ); +int CPL_DLL CPL_STDCALL GDALARLockBuffer(GDALAsyncReaderH hARIO, + double dfTimeout); +void CPL_DLL CPL_STDCALL GDALARUnlockBuffer(GDALAsyncReaderH hARIO); + +/* -------------------------------------------------------------------- */ +/* Helper functions. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPL_STDCALL GDALGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, + int nOptions ); +void CPL_DLL CPL_STDCALL GDALSwapWords( void *pData, int nWordSize, int nWordCount, + int nWordSkip ); +void CPL_DLL CPL_STDCALL + GDALCopyWords( void * pSrcData, GDALDataType eSrcType, int nSrcPixelOffset, + void * pDstData, GDALDataType eDstType, int nDstPixelOffset, + int nWordCount ); + +void CPL_DLL +GDALCopyBits( const GByte *pabySrcData, int nSrcOffset, int nSrcStep, + GByte *pabyDstData, int nDstOffset, int nDstStep, + int nBitCount, int nStepCount ); + +int CPL_DLL CPL_STDCALL GDALLoadWorldFile( const char *, double * ); +int CPL_DLL CPL_STDCALL GDALReadWorldFile( const char *, const char *, + double * ); +int CPL_DLL CPL_STDCALL GDALWriteWorldFile( const char *, const char *, + double * ); +int CPL_DLL CPL_STDCALL GDALLoadTabFile( const char *, double *, char **, + int *, GDAL_GCP ** ); +int CPL_DLL CPL_STDCALL GDALReadTabFile( const char *, double *, char **, + int *, GDAL_GCP ** ); +int CPL_DLL CPL_STDCALL GDALLoadOziMapFile( const char *, double *, char **, + int *, GDAL_GCP ** ); +int CPL_DLL CPL_STDCALL GDALReadOziMapFile( const char * , double *, + char **, int *, GDAL_GCP ** ); +char CPL_DLL ** CPL_STDCALL GDALLoadRPBFile( const char *pszFilename, + char **papszSiblingFiles ); +char CPL_DLL ** CPL_STDCALL GDALLoadRPCFile( const char *pszFilename, + char **papszSiblingFiles ); +CPLErr CPL_DLL CPL_STDCALL GDALWriteRPBFile( const char *pszFilename, + char **papszMD ); +char CPL_DLL ** CPL_STDCALL GDALLoadIMDFile( const char *pszFilename, + char **papszSiblingFiles ); +CPLErr CPL_DLL CPL_STDCALL GDALWriteIMDFile( const char *pszFilename, + char **papszMD ); + +const char CPL_DLL * CPL_STDCALL GDALDecToDMS( double, const char *, int ); +double CPL_DLL CPL_STDCALL GDALPackedDMSToDec( double ); +double CPL_DLL CPL_STDCALL GDALDecToPackedDMS( double ); + +/* Note to developers : please keep this section in sync with ogr_core.h */ + +#ifndef GDAL_VERSION_INFO_DEFINED +#define GDAL_VERSION_INFO_DEFINED +const char CPL_DLL * CPL_STDCALL GDALVersionInfo( const char * ); +#endif + +#ifndef GDAL_CHECK_VERSION + +int CPL_DLL CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor, + const char* pszCallingComponentName); + +/** Helper macro for GDALCheckVersion() + @see GDALCheckVersion() + */ +#define GDAL_CHECK_VERSION(pszCallingComponentName) \ + GDALCheckVersion(GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, pszCallingComponentName) + +#endif + +typedef struct { + double dfLINE_OFF; + double dfSAMP_OFF; + double dfLAT_OFF; + double dfLONG_OFF; + double dfHEIGHT_OFF; + + double dfLINE_SCALE; + double dfSAMP_SCALE; + double dfLAT_SCALE; + double dfLONG_SCALE; + double dfHEIGHT_SCALE; + + double adfLINE_NUM_COEFF[20]; + double adfLINE_DEN_COEFF[20]; + double adfSAMP_NUM_COEFF[20]; + double adfSAMP_DEN_COEFF[20]; + + double dfMIN_LONG; + double dfMIN_LAT; + double dfMAX_LONG; + double dfMAX_LAT; + +} GDALRPCInfo; + +int CPL_DLL CPL_STDCALL GDALExtractRPCInfo( char **, GDALRPCInfo * ); + +/* ==================================================================== */ +/* Color tables. */ +/* ==================================================================== */ + +/** Color tuple */ +typedef struct +{ + /*! gray, red, cyan or hue */ + short c1; + + /*! green, magenta, or lightness */ + short c2; + + /*! blue, yellow, or saturation */ + short c3; + + /*! alpha or blackband */ + short c4; +} GDALColorEntry; + +GDALColorTableH CPL_DLL CPL_STDCALL GDALCreateColorTable( GDALPaletteInterp ); +void CPL_DLL CPL_STDCALL GDALDestroyColorTable( GDALColorTableH ); +GDALColorTableH CPL_DLL CPL_STDCALL GDALCloneColorTable( GDALColorTableH ); +GDALPaletteInterp CPL_DLL CPL_STDCALL GDALGetPaletteInterpretation( GDALColorTableH ); +int CPL_DLL CPL_STDCALL GDALGetColorEntryCount( GDALColorTableH ); +const GDALColorEntry CPL_DLL * CPL_STDCALL GDALGetColorEntry( GDALColorTableH, int ); +int CPL_DLL CPL_STDCALL GDALGetColorEntryAsRGB( GDALColorTableH, int, GDALColorEntry *); +void CPL_DLL CPL_STDCALL GDALSetColorEntry( GDALColorTableH, int, const GDALColorEntry * ); +void CPL_DLL CPL_STDCALL GDALCreateColorRamp( GDALColorTableH hTable, + int nStartIndex, const GDALColorEntry *psStartColor, + int nEndIndex, const GDALColorEntry *psEndColor ); + +/* ==================================================================== */ +/* Raster Attribute Table */ +/* ==================================================================== */ + +/** Field type of raster attribute table */ +typedef enum { + /*! Integer field */ GFT_Integer , + /*! Floating point (double) field */ GFT_Real, + /*! String field */ GFT_String +} GDALRATFieldType; + +/** Field usage of raster attribute table */ +typedef enum { + /*! General purpose field. */ GFU_Generic = 0, + /*! Histogram pixel count */ GFU_PixelCount = 1, + /*! Class name */ GFU_Name = 2, + /*! Class range minimum */ GFU_Min = 3, + /*! Class range maximum */ GFU_Max = 4, + /*! Class value (min=max) */ GFU_MinMax = 5, + /*! Red class color (0-255) */ GFU_Red = 6, + /*! Green class color (0-255) */ GFU_Green = 7, + /*! Blue class color (0-255) */ GFU_Blue = 8, + /*! Alpha (0=transparent,255=opaque)*/ GFU_Alpha = 9, + /*! Color Range Red Minimum */ GFU_RedMin = 10, + /*! Color Range Green Minimum */ GFU_GreenMin = 11, + /*! Color Range Blue Minimum */ GFU_BlueMin = 12, + /*! Color Range Alpha Minimum */ GFU_AlphaMin = 13, + /*! Color Range Red Maximum */ GFU_RedMax = 14, + /*! Color Range Green Maximum */ GFU_GreenMax = 15, + /*! Color Range Blue Maximum */ GFU_BlueMax = 16, + /*! Color Range Alpha Maximum */ GFU_AlphaMax = 17, + /*! Maximum GFU value */ GFU_MaxCount +} GDALRATFieldUsage; + +GDALRasterAttributeTableH CPL_DLL CPL_STDCALL + GDALCreateRasterAttributeTable(void); +void CPL_DLL CPL_STDCALL GDALDestroyRasterAttributeTable( + GDALRasterAttributeTableH ); + +int CPL_DLL CPL_STDCALL GDALRATGetColumnCount( GDALRasterAttributeTableH ); + +const char CPL_DLL * CPL_STDCALL GDALRATGetNameOfCol( + GDALRasterAttributeTableH, int ); +GDALRATFieldUsage CPL_DLL CPL_STDCALL GDALRATGetUsageOfCol( + GDALRasterAttributeTableH, int ); +GDALRATFieldType CPL_DLL CPL_STDCALL GDALRATGetTypeOfCol( + GDALRasterAttributeTableH, int ); + +int CPL_DLL CPL_STDCALL GDALRATGetColOfUsage( GDALRasterAttributeTableH, + GDALRATFieldUsage ); +int CPL_DLL CPL_STDCALL GDALRATGetRowCount( GDALRasterAttributeTableH ); + +const char CPL_DLL * CPL_STDCALL GDALRATGetValueAsString( + GDALRasterAttributeTableH, int ,int); +int CPL_DLL CPL_STDCALL GDALRATGetValueAsInt( + GDALRasterAttributeTableH, int ,int); +double CPL_DLL CPL_STDCALL GDALRATGetValueAsDouble( + GDALRasterAttributeTableH, int ,int); + +void CPL_DLL CPL_STDCALL GDALRATSetValueAsString( GDALRasterAttributeTableH, int, int, + const char * ); +void CPL_DLL CPL_STDCALL GDALRATSetValueAsInt( GDALRasterAttributeTableH, int, int, + int ); +void CPL_DLL CPL_STDCALL GDALRATSetValueAsDouble( GDALRasterAttributeTableH, int, int, + double ); + +int CPL_DLL CPL_STDCALL GDALRATChangesAreWrittenToFile( GDALRasterAttributeTableH hRAT ); + +CPLErr CPL_DLL CPL_STDCALL GDALRATValuesIOAsDouble( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, double *pdfData ); +CPLErr CPL_DLL CPL_STDCALL GDALRATValuesIOAsInteger( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, int *pnData); +CPLErr CPL_DLL CPL_STDCALL GDALRATValuesIOAsString( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, char **papszStrList); + +void CPL_DLL CPL_STDCALL GDALRATSetRowCount( GDALRasterAttributeTableH, + int ); +CPLErr CPL_DLL CPL_STDCALL GDALRATCreateColumn( GDALRasterAttributeTableH, + const char *, + GDALRATFieldType, + GDALRATFieldUsage ); +CPLErr CPL_DLL CPL_STDCALL GDALRATSetLinearBinning( GDALRasterAttributeTableH, + double, double ); +int CPL_DLL CPL_STDCALL GDALRATGetLinearBinning( GDALRasterAttributeTableH, + double *, double * ); +CPLErr CPL_DLL CPL_STDCALL GDALRATInitializeFromColorTable( + GDALRasterAttributeTableH, GDALColorTableH ); +GDALColorTableH CPL_DLL CPL_STDCALL GDALRATTranslateToColorTable( + GDALRasterAttributeTableH, int nEntryCount ); +void CPL_DLL CPL_STDCALL GDALRATDumpReadable( GDALRasterAttributeTableH, + FILE * ); +GDALRasterAttributeTableH CPL_DLL CPL_STDCALL + GDALRATClone( GDALRasterAttributeTableH ); + +int CPL_DLL CPL_STDCALL GDALRATGetRowOfValue( GDALRasterAttributeTableH , double ); + + +/* ==================================================================== */ +/* GDAL Cache Management */ +/* ==================================================================== */ + +void CPL_DLL CPL_STDCALL GDALSetCacheMax( int nBytes ); +int CPL_DLL CPL_STDCALL GDALGetCacheMax(void); +int CPL_DLL CPL_STDCALL GDALGetCacheUsed(void); +void CPL_DLL CPL_STDCALL GDALSetCacheMax64( GIntBig nBytes ); +GIntBig CPL_DLL CPL_STDCALL GDALGetCacheMax64(void); +GIntBig CPL_DLL CPL_STDCALL GDALGetCacheUsed64(void); + +int CPL_DLL CPL_STDCALL GDALFlushCacheBlock(void); + +/* ==================================================================== */ +/* GDAL virtual memory */ +/* ==================================================================== */ + +CPLVirtualMem CPL_DLL* GDALDatasetGetVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ); + +CPLVirtualMem CPL_DLL* GDALRasterBandGetVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + GIntBig nLineSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ); + +CPLVirtualMem CPL_DLL* GDALGetVirtualMemAuto( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); + +typedef enum +{ + /*! Tile Interleaved by Pixel: tile (0,0) with internal band interleaved by pixel organization, tile (1, 0), ... */ + GTO_TIP, + /*! Band Interleaved by Tile : tile (0,0) of first band, tile (0,0) of second band, ... tile (1,0) of fisrt band, tile (1,0) of second band, ... */ + GTO_BIT, + /*! Band SeQuential : all the tiles of first band, all the tiles of following band... */ + GTO_BSQ +} GDALTileOrganization; + +CPLVirtualMem CPL_DLL* GDALDatasetGetTiledVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + GDALTileOrganization eTileOrganization, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ); + +CPLVirtualMem CPL_DLL* GDALRasterBandGetTiledVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ); + + +CPL_C_END + +#endif /* ndef GDAL_H_INCLUDED */ diff --git a/ogr/gdal_alg.h b/ogr/gdal_alg.h new file mode 100644 index 0000000..a22684b --- /dev/null +++ b/ogr/gdal_alg.h @@ -0,0 +1,476 @@ +/****************************************************************************** + * $Id: gdal_alg.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Image Processing Algorithms + * Purpose: Prototypes, and definitions for various GDAL based algorithms. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_ALG_H_INCLUDED +#define GDAL_ALG_H_INCLUDED + +/** + * \file gdal_alg.h + * + * Public (C callable) GDAL algorithm entry points, and definitions. + */ + +#ifndef DOXYGEN_SKIP +#include "gdal.h" +#include "cpl_minixml.h" +#include "ogr_api.h" +#endif + +CPL_C_START + +int CPL_DLL CPL_STDCALL GDALComputeMedianCutPCT( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + int (*pfnIncludePixel)(int,int,void*), + int nColors, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +int CPL_DLL CPL_STDCALL GDALDitherRGB2PCT( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GDALRasterBandH hTarget, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +int CPL_DLL CPL_STDCALL GDALChecksumImage( GDALRasterBandH hBand, + int nXOff, int nYOff, int nXSize, int nYSize ); + +CPLErr CPL_DLL CPL_STDCALL +GDALComputeProximity( GDALRasterBandH hSrcBand, + GDALRasterBandH hProximityBand, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALFillNodata( GDALRasterBandH hTargetBand, + GDALRasterBandH hMaskBand, + double dfMaxSearchDist, + int bDeprecatedOption, + int nSmoothingIterations, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALPolygonize( GDALRasterBandH hSrcBand, + GDALRasterBandH hMaskBand, + OGRLayerH hOutLayer, int iPixValField, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALFPolygonize( GDALRasterBandH hSrcBand, + GDALRasterBandH hMaskBand, + OGRLayerH hOutLayer, int iPixValField, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALSieveFilter( GDALRasterBandH hSrcBand, GDALRasterBandH hMaskBand, + GDALRasterBandH hDstBand, + int nSizeThreshold, int nConnectedness, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +/* + * Warp Related. + */ + +typedef int +(*GDALTransformerFunc)( void *pTransformerArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +typedef struct { + char szSignature[4]; + const char *pszClassName; + GDALTransformerFunc pfnTransform; + void (*pfnCleanup)( void * ); + CPLXMLNode *(*pfnSerialize)( void * ); + /* TODO GDAL 2.0 : add a void* (*pfnClone) (void *) member */ +} GDALTransformerInfo; + +void CPL_DLL GDALDestroyTransformer( void *pTransformerArg ); +int CPL_DLL GDALUseTransformer( void *pTranformerArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, + int *panSuccess ); + +/* High level transformer for going from image coordinates on one file + to image coordiantes on another, potentially doing reprojection, + utilizing GCPs or using the geotransform. */ + +void CPL_DLL * +GDALCreateGenImgProjTransformer( GDALDatasetH hSrcDS, const char *pszSrcWKT, + GDALDatasetH hDstDS, const char *pszDstWKT, + int bGCPUseOK, double dfGCPErrorThreshold, + int nOrder ); +void CPL_DLL * +GDALCreateGenImgProjTransformer2( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, + char **papszOptions ); +void CPL_DLL * +GDALCreateGenImgProjTransformer3( const char *pszSrcWKT, + const double *padfSrcGeoTransform, + const char *pszDstWKT, + const double *padfDstGeoTransform ); +void CPL_DLL GDALSetGenImgProjTransformerDstGeoTransform( void *, + const double * ); +void CPL_DLL GDALDestroyGenImgProjTransformer( void * ); +int CPL_DLL GDALGenImgProjTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Geo to geo reprojection transformer. */ +void CPL_DLL * +GDALCreateReprojectionTransformer( const char *pszSrcWKT, + const char *pszDstWKT ); +void CPL_DLL GDALDestroyReprojectionTransformer( void * ); +int CPL_DLL GDALReprojectionTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* GCP based transformer ... forward is to georef coordinates */ +void CPL_DLL * +GDALCreateGCPTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed ); + +/* GCP based transformer with refinement of the GCPs ... forward is to georef coordinates */ +void CPL_DLL * +GDALCreateGCPRefineTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed, double tolerance, int minimumGcps); + +void CPL_DLL GDALDestroyGCPTransformer( void *pTransformArg ); +int CPL_DLL GDALGCPTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Thin Plate Spine transformer ... forward is to georef coordinates */ + +void CPL_DLL * +GDALCreateTPSTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed ); +void CPL_DLL GDALDestroyTPSTransformer( void *pTransformArg ); +int CPL_DLL GDALTPSTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +char CPL_DLL ** RPCInfoToMD( GDALRPCInfo *psRPCInfo ); + +/* RPC based transformer ... src is pixel/line/elev, dst is long/lat/elev */ + +void CPL_DLL * +GDALCreateRPCTransformer( GDALRPCInfo *psRPC, int bReversed, + double dfPixErrThreshold, + char **papszOptions ); +void CPL_DLL GDALDestroyRPCTransformer( void *pTransformArg ); +int CPL_DLL GDALRPCTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Geolocation transformer */ + +void CPL_DLL * +GDALCreateGeoLocTransformer( GDALDatasetH hBaseDS, + char **papszGeolocationInfo, + int bReversed ); +void CPL_DLL GDALDestroyGeoLocTransformer( void *pTransformArg ); +int CPL_DLL GDALGeoLocTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Approximate transformer */ +void CPL_DLL * +GDALCreateApproxTransformer( GDALTransformerFunc pfnRawTransformer, + void *pRawTransformerArg, double dfMaxError ); +void CPL_DLL GDALApproxTransformerOwnsSubtransformer( void *pCBData, + int bOwnFlag ); +void CPL_DLL GDALDestroyApproxTransformer( void *pApproxArg ); +int CPL_DLL GDALApproxTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + + + + +int CPL_DLL CPL_STDCALL +GDALSimpleImageWarp( GDALDatasetH hSrcDS, + GDALDatasetH hDstDS, + int nBandCount, int *panBandList, + GDALTransformerFunc pfnTransform, + void *pTransformArg, + GDALProgressFunc pfnProgress, + void *pProgressArg, + char **papszWarpOptions ); + +CPLErr CPL_DLL CPL_STDCALL +GDALSuggestedWarpOutput( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines ); +CPLErr CPL_DLL CPL_STDCALL +GDALSuggestedWarpOutput2( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines, + double *padfExtents, + int nOptions ); + +CPLXMLNode CPL_DLL * +GDALSerializeTransformer( GDALTransformerFunc pfnFunc, void *pTransformArg ); +CPLErr CPL_DLL GDALDeserializeTransformer( CPLXMLNode *psTree, + GDALTransformerFunc *ppfnFunc, + void **ppTransformArg ); + +CPLErr CPL_DLL +GDALTransformGeolocations( GDALRasterBandH hXBand, + GDALRasterBandH hYBand, + GDALRasterBandH hZBand, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + GDALProgressFunc pfnProgress, + void *pProgressArg, + char **papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Contour Line Generation */ +/* -------------------------------------------------------------------- */ + +typedef CPLErr (*GDALContourWriter)( double dfLevel, int nPoints, + double *padfX, double *padfY, void * ); + +typedef void *GDALContourGeneratorH; + +GDALContourGeneratorH CPL_DLL +GDAL_CG_Create( int nWidth, int nHeight, + int bNoDataSet, double dfNoDataValue, + double dfContourInterval, double dfContourBase, + GDALContourWriter pfnWriter, void *pCBData ); +CPLErr CPL_DLL GDAL_CG_FeedLine( GDALContourGeneratorH hCG, + double *padfScanline ); +void CPL_DLL GDAL_CG_Destroy( GDALContourGeneratorH hCG ); + +typedef struct +{ + void *hLayer; + + double adfGeoTransform[6]; + + int nElevField; + int nIDField; + int nNextID; +} OGRContourWriterInfo; + +CPLErr CPL_DLL +OGRContourWriter( double, int, double *, double *, void *pInfo ); + +CPLErr CPL_DLL +GDALContourGenerate( GDALRasterBandH hBand, + double dfContourInterval, double dfContourBase, + int nFixedLevelCount, double *padfFixedLevels, + int bUseNoData, double dfNoDataValue, + void *hLayer, int iIDField, int iElevField, + GDALProgressFunc pfnProgress, void *pProgressArg ); + +/************************************************************************/ +/* Rasterizer API - geometries burned into GDAL raster. */ +/************************************************************************/ + +CPLErr CPL_DLL +GDALRasterizeGeometries( GDALDatasetH hDS, + int nBandCount, int *panBandList, + int nGeomCount, OGRGeometryH *pahGeometries, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeomBurnValue, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); +CPLErr CPL_DLL +GDALRasterizeLayers( GDALDatasetH hDS, + int nBandCount, int *panBandList, + int nLayerCount, OGRLayerH *pahLayers, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfLayerBurnValues, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressArg ); + +CPLErr CPL_DLL +GDALRasterizeLayersBuf( void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, int nPixelSpace, int nLineSpace, + int nLayerCount, OGRLayerH *pahLayers, + const char *pszDstProjection, + double *padfDstGeoTransform, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, double dfBurnValue, + char **papszOptions, GDALProgressFunc pfnProgress, + void *pProgressArg ); + + +/************************************************************************/ +/* Gridding interface. */ +/************************************************************************/ + +/** Gridding Algorithms */ +typedef enum { + /*! Inverse distance to a power */ GGA_InverseDistanceToAPower = 1, + /*! Moving Average */ GGA_MovingAverage = 2, + /*! Nearest Neighbor */ GGA_NearestNeighbor = 3, + /*! Minimum Value (Data Metric) */ GGA_MetricMinimum = 4, + /*! Maximum Value (Data Metric) */ GGA_MetricMaximum = 5, + /*! Data Range (Data Metric) */ GGA_MetricRange = 6, + /*! Number of Points (Data Metric) */ GGA_MetricCount = 7, + /*! Average Distance (Data Metric) */ GGA_MetricAverageDistance = 8, + /*! Average Distance Between Data Points (Data Metric) */ + GGA_MetricAverageDistancePts = 9 +} GDALGridAlgorithm; + +/** Inverse distance to a power method control options */ +typedef struct +{ + /*! Weighting power. */ + double dfPower; + /*! Smoothing parameter. */ + double dfSmoothing; + /*! Reserved for future use. */ + double dfAnisotropyRatio; + /*! Reserved for future use. */ + double dfAnisotropyAngle; + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! Maximum number of data points to use. + * + * Do not search for more points than this number. + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMaxPoints; + /*! Minimum number of data points to use. + * + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMinPoints; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridInverseDistanceToAPowerOptions; + +/** Moving average method control options */ +typedef struct +{ + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! Minimum number of data points to average. + * + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMinPoints; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridMovingAverageOptions; + +/** Nearest neighbor method control options */ +typedef struct +{ + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridNearestNeighborOptions; + +/** Data metrics method control options */ +typedef struct +{ + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! Minimum number of data points to average. + * + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMinPoints; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridDataMetricsOptions; + +CPLErr CPL_DLL +GDALGridCreate( GDALGridAlgorithm, const void *, GUInt32, + const double *, const double *, const double *, + double, double, double, double, + GUInt32, GUInt32, GDALDataType, void *, + GDALProgressFunc, void *); + +GDAL_GCP CPL_DLL * +GDALComputeMatchingPoints( GDALDatasetH hFirstImage, + GDALDatasetH hSecondImage, + char **papszOptions, + int *pnGCPCount ); +CPL_C_END + +#endif /* ndef GDAL_ALG_H_INCLUDED */ diff --git a/ogr/gdal_alg_priv.h b/ogr/gdal_alg_priv.h new file mode 100644 index 0000000..76b8f2f --- /dev/null +++ b/ogr/gdal_alg_priv.h @@ -0,0 +1,200 @@ +/****************************************************************************** + * $Id: gdal_alg_priv.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Image Processing Algorithms + * Purpose: Prototypes and definitions for various GDAL based algorithms: + * private declarations. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2008, Andrey Kiselev + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_ALG_PRIV_H_INCLUDED +#define GDAL_ALG_PRIV_H_INCLUDED + +#include "gdal_alg.h" + +CPL_C_START + +/** Source of the burn value */ +typedef enum { + /*! Use value from padfBurnValue */ GBV_UserBurnValue = 0, + /*! Use value from the Z coordinate */ GBV_Z = 1, + /*! Use value form the M value */ GBV_M = 2 +} GDALBurnValueSrc; + +typedef enum { + GRMA_Replace = 0, + GRMA_Add = 1, +} GDALRasterMergeAlg; + +typedef struct { + unsigned char * pabyChunkBuf; + int nXSize; + int nYSize; + int nBands; + GDALDataType eType; + double *padfBurnValue; + GDALBurnValueSrc eBurnValueSource; + GDALRasterMergeAlg eMergeAlg; +} GDALRasterizeInfo; + +/************************************************************************/ +/* Low level rasterizer API. */ +/************************************************************************/ + +typedef void (*llScanlineFunc)( void *, int, int, int, double ); +typedef void (*llPointFunc)( void *, int, int, double ); + +void GDALdllImagePoint( int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ); + +void GDALdllImageLine( int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ); + +void GDALdllImageLineAllTouched(int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, + double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ); + +void GDALdllImageFilledPolygon(int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, + double *padfVariant, + llScanlineFunc pfnScanlineFunc, void *pCBData ); + +CPL_C_END + +/************************************************************************/ +/* Polygon Enumerator */ +/************************************************************************/ + +class GDALRasterPolygonEnumerator + +{ +private: + void MergePolygon( int nSrcId, int nDstId ); + int NewPolygon( GInt32 nValue ); + +public: // these are intended to be readonly. + + GInt32 *panPolyIdMap; + GInt32 *panPolyValue; + + int nNextPolygonId; + int nPolyAlloc; + + int nConnectedness; + +public: + GDALRasterPolygonEnumerator( int nConnectedness=4 ); + ~GDALRasterPolygonEnumerator(); + + void ProcessLine( GInt32 *panLastLineVal, GInt32 *panThisLineVal, + GInt32 *panLastLineId, GInt32 *panThisLineId, + int nXSize ); + + void CompleteMerges(); + + void Clear(); +}; + +#ifdef OGR_ENABLED +/************************************************************************/ +/* Polygon Enumerator */ +/* */ +/* Buffers has float values instead og GInt32 */ +/************************************************************************/ +class GDALRasterFPolygonEnumerator + +{ +private: + void MergePolygon( int nSrcId, int nDstId ); + int NewPolygon( float fValue ); + +public: // these are intended to be readonly. + + GInt32 *panPolyIdMap; + float *pafPolyValue; + + int nNextPolygonId; + int nPolyAlloc; + + int nConnectedness; + +public: + GDALRasterFPolygonEnumerator( int nConnectedness=4 ); + ~GDALRasterFPolygonEnumerator(); + + void ProcessLine( float *pafLastLineVal, float *pafThisLineVal, + GInt32 *panLastLineId, GInt32 *panThisLineId, + int nXSize ); + + void CompleteMerges(); + + void Clear(); +}; +#endif + +typedef void* (*GDALTransformDeserializeFunc)( CPLXMLNode *psTree ); + +void* GDALRegisterTransformDeserializer(const char* pszTransformName, + GDALTransformerFunc pfnTransformerFunc, + GDALTransformDeserializeFunc pfnDeserializeFunc); +void GDALUnregisterTransformDeserializer(void* pData); + +void GDALCleanupTransformDeserializerMutex(); + +/* Transformer cloning */ + +void* GDALCloneTPSTransformer( void *pTransformArg ); +void* GDALCloneGeoLocTransformer( void *pTransformArg ); +void* GDALCloneGenImgProjTransformer( void *pTransformArg ); +void* GDALCloneApproxTransformer( void *pTransformArg ); +/* TODO : GDALCloneGeoLocTransformer? , GDALCloneRPCTransformer? */ + +void* GDALCreateTPSTransformerInt( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed, char** papszOptions ); + +void CPL_DLL * GDALCloneTransformer( void *pTranformerArg ); + +/************************************************************************/ +/* Float comparison function. */ +/************************************************************************/ + +/** + * Units in the Last Place. This specifies how big an error we are willing to + * accept in terms of the value of the least significant digit of the floating + * point number’s representation. MAX_ULPS can also be interpreted in terms of + * how many representable floats we are willing to accept between A and B. + */ +#define MAX_ULPS 10 + +GBool GDALFloatEquals(float A, float B); + +#endif /* ndef GDAL_ALG_PRIV_H_INCLUDED */ diff --git a/ogr/gdal_frmts.h b/ogr/gdal_frmts.h new file mode 100644 index 0000000..2a3526b --- /dev/null +++ b/ogr/gdal_frmts.h @@ -0,0 +1,183 @@ +/****************************************************************************** + * $Id: gdal_frmts.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL + * Purpose: Prototypes for all format specific driver initializations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_FRMTS_H_INCLUDED +#define GDAL_FRMTS_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START +void CPL_DLL GDALRegister_GDB(void); +void CPL_DLL GDALRegister_GTiff(void); +void CPL_DLL GDALRegister_GXF(void); +void CPL_DLL GDALRegister_OGDI(void); +void CPL_DLL GDALRegister_HFA(void); +void CPL_DLL GDALRegister_AAIGrid(void); +void CPL_DLL GDALRegister_GRASSASCIIGrid(void); +void CPL_DLL GDALRegister_AIGrid(void); +void CPL_DLL GDALRegister_AIGrid2(void); +void CPL_DLL GDALRegister_CEOS(void); +void CPL_DLL GDALRegister_SAR_CEOS(void); +void CPL_DLL GDALRegister_SDTS(void); +void CPL_DLL GDALRegister_ELAS(void); +void CPL_DLL GDALRegister_EHdr(void); +void CPL_DLL GDALRegister_GenBin(void); +void CPL_DLL GDALRegister_PAux(void); +void CPL_DLL GDALRegister_ENVI(void); +void CPL_DLL GDALRegister_DOQ1(void); +void CPL_DLL GDALRegister_DOQ2(void); +void CPL_DLL GDALRegister_DTED(void); +void CPL_DLL GDALRegister_MFF(void); +void CPL_DLL GDALRegister_HKV(void); +void CPL_DLL GDALRegister_PNG(void); +void CPL_DLL GDALRegister_DDS(void); +void CPL_DLL GDALRegister_GTA(void); +void CPL_DLL GDALRegister_JPEG(void); +void CPL_DLL GDALRegister_JPEG2000(void); +void CPL_DLL GDALRegister_JP2KAK(void); +void CPL_DLL GDALRegister_JPIPKAK(void); +void CPL_DLL GDALRegister_MEM(void); +void CPL_DLL GDALRegister_JDEM(void); +void CPL_DLL GDALRegister_RASDAMAN(void); +void CPL_DLL GDALRegister_GRASS(void); +void CPL_DLL GDALRegister_PNM(void); +void CPL_DLL GDALRegister_GIF(void); +void CPL_DLL GDALRegister_BIGGIF(void); +void CPL_DLL GDALRegister_Envisat(void); +void CPL_DLL GDALRegister_FITS(void); +void CPL_DLL GDALRegister_ECW(void); +void CPL_DLL GDALRegister_JP2ECW(void); +void CPL_DLL GDALRegister_ECW_JP2ECW(); +void CPL_DLL GDALRegister_FujiBAS(void); +void CPL_DLL GDALRegister_FIT(void); +void CPL_DLL GDALRegister_VRT(void); +void CPL_DLL GDALRegister_USGSDEM(void); +void CPL_DLL GDALRegister_FAST(void); +void CPL_DLL GDALRegister_HDF4(void); +void CPL_DLL GDALRegister_HDF4Image(void); +void CPL_DLL GDALRegister_L1B(void); +void CPL_DLL GDALRegister_LDF(void); +void CPL_DLL GDALRegister_BSB(void); +void CPL_DLL GDALRegister_XPM(void); +void CPL_DLL GDALRegister_BMP(void); +void CPL_DLL GDALRegister_GSC(void); +void CPL_DLL GDALRegister_NITF(void); +void CPL_DLL GDALRegister_RPFTOC(void); +void CPL_DLL GDALRegister_ECRGTOC(void); +void CPL_DLL GDALRegister_MrSID(void); +void CPL_DLL GDALRegister_MG4Lidar(void); +void CPL_DLL GDALRegister_PCIDSK(void); +void CPL_DLL GDALRegister_BT(void); +void CPL_DLL GDALRegister_DODS(void); +void CPL_DLL GDALRegister_GMT(void); +void CPL_DLL GDALRegister_netCDF(void); +void CPL_DLL GDALRegister_LAN(void); +void CPL_DLL GDALRegister_CPG(void); +void CPL_DLL GDALRegister_AirSAR(void); +void CPL_DLL GDALRegister_RS2(void); +void CPL_DLL GDALRegister_ILWIS(void); +void CPL_DLL GDALRegister_PCRaster(void); +void CPL_DLL GDALRegister_IDA(void); +void CPL_DLL GDALRegister_NDF(void); +void CPL_DLL GDALRegister_RMF(void); +void CPL_DLL GDALRegister_BAG(void); +void CPL_DLL GDALRegister_HDF5(void); +void CPL_DLL GDALRegister_HDF5Image(void); +void CPL_DLL GDALRegister_MSGN(void); +void CPL_DLL GDALRegister_MSG(void); +void CPL_DLL GDALRegister_RIK(void); +void CPL_DLL GDALRegister_Leveller(void); +void CPL_DLL GDALRegister_SGI(void); +void CPL_DLL GDALRegister_SRTMHGT(void); +void CPL_DLL GDALRegister_DIPEx(void); +void CPL_DLL GDALRegister_ISIS3(void); +void CPL_DLL GDALRegister_ISIS2(void); +void CPL_DLL GDALRegister_PDS(void); +void CPL_DLL GDALRegister_IDRISI(void); +void CPL_DLL GDALRegister_Terragen(void); +void CPL_DLL GDALRegister_WCS(void); +void CPL_DLL GDALRegister_WMS(void); +void CPL_DLL GDALRegister_HTTP(void); +void CPL_DLL GDALRegister_SDE(void); +void CPL_DLL GDALRegister_GSAG(void); +void CPL_DLL GDALRegister_GSBG(void); +void CPL_DLL GDALRegister_GS7BG(void); +void CPL_DLL GDALRegister_GRIB(void); +void CPL_DLL GDALRegister_INGR(void); +void CPL_DLL GDALRegister_ERS(void); +void CPL_DLL GDALRegister_PALSARJaxa(void); +void CPL_DLL GDALRegister_DIMAP(); +void CPL_DLL GDALRegister_GFF(void); +void CPL_DLL GDALRegister_COSAR(void); +void CPL_DLL GDALRegister_TSX(void); +void CPL_DLL GDALRegister_ADRG(void); +void CPL_DLL GDALRegister_SRP(void); +void CPL_DLL GDALRegister_COASP(void); +void CPL_DLL GDALRegister_BLX(void); +void CPL_DLL GDALRegister_LCP(void); +void CPL_DLL GDALRegister_PGCHIP(void); +void CPL_DLL GDALRegister_TMS(void); +void CPL_DLL GDALRegister_EIR(void); +void CPL_DLL GDALRegister_GEOR(void); +void CPL_DLL GDALRegister_TIL(void); +void CPL_DLL GDALRegister_R(void); +void CPL_DLL GDALRegister_Rasterlite(void); +void CPL_DLL GDALRegister_EPSILON(void); +void CPL_DLL GDALRegister_PostGISRaster(void); +void CPL_DLL GDALRegister_NWT_GRD(void); +void CPL_DLL GDALRegister_NWT_GRC(void); +void CPL_DLL GDALRegister_SAGA(void); +void CPL_DLL GDALRegister_KMLSUPEROVERLAY(void); +void CPL_DLL GDALRegister_GTX(void); +void CPL_DLL GDALRegister_LOSLAS(void); +void CPL_DLL GDALRegister_Istar(void); +void CPL_DLL GDALRegister_NTv2(void); +void CPL_DLL GDALRegister_CTable2(void); +void CPL_DLL GDALRegister_JP2OpenJPEG(void); +void CPL_DLL GDALRegister_XYZ(void); +void CPL_DLL GDALRegister_HF2(void); +void CPL_DLL GDALRegister_PDF(void); +void CPL_DLL GDALRegister_JPEGLS(void); +void CPL_DLL GDALRegister_MAP(void); +void CPL_DLL GDALRegister_OZI(void); +void CPL_DLL GDALRegister_ACE2(void); +void CPL_DLL GDALRegister_CTG(void); +void CPL_DLL GDALRegister_E00GRID(void); +void CPL_DLL GDALRegister_SNODAS(void); +void CPL_DLL GDALRegister_WEBP(void); +void CPL_DLL GDALRegister_ZMap(void); +void CPL_DLL GDALRegister_NGSGEOID(void); +void CPL_DLL GDALRegister_MBTiles(void); +void CPL_DLL GDALRegister_ARG(void); +void CPL_DLL GDALRegister_IRIS(void); +void CPL_DLL GDALRegister_KRO(void); +CPL_C_END + +#endif /* ndef GDAL_FRMTS_H_INCLUDED */ diff --git a/ogr/gdal_misc.cpp b/ogr/gdal_misc.cpp new file mode 100644 index 0000000..9ad5c71 --- /dev/null +++ b/ogr/gdal_misc.cpp @@ -0,0 +1,3229 @@ +/****************************************************************************** + * $Id: gdal_misc.cpp 27487 2014-07-02 17:13:13Z rouault $ + * + * Project: GDAL Core + * Purpose: Free standing functions for GDAL. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "cpl_multiproc.h" +#include +#include + +CPL_CVSID("$Id: gdal_misc.cpp 27487 2014-07-02 17:13:13Z rouault $"); + +#include "ogr_spatialref.h" + +/************************************************************************/ +/* __pure_virtual() */ +/* */ +/* The following is a gross hack to remove the last remaining */ +/* dependency on the GNU C++ standard library. */ +/************************************************************************/ + +#ifdef __GNUC__ + +extern "C" +void __pure_virtual() + +{ +} + +#endif + +/************************************************************************/ +/* GDALDataTypeUnion() */ +/************************************************************************/ + +/** + * \brief Return the smallest data type that can fully express both input data + * types. + * + * @param eType1 first data type. + * @param eType2 second data type. + * + * @return a data type able to express eType1 and eType2. + */ + +GDALDataType CPL_STDCALL +GDALDataTypeUnion( GDALDataType eType1, GDALDataType eType2 ) + +{ + int bFloating, bComplex, nBits, bSigned; + + bComplex = GDALDataTypeIsComplex(eType1) | GDALDataTypeIsComplex(eType2); + + switch( eType1 ) + { + case GDT_Byte: + nBits = 8; + bSigned = FALSE; + bFloating = FALSE; + break; + + case GDT_Int16: + case GDT_CInt16: + nBits = 16; + bSigned = TRUE; + bFloating = FALSE; + break; + + case GDT_UInt16: + nBits = 16; + bSigned = FALSE; + bFloating = FALSE; + break; + + case GDT_Int32: + case GDT_CInt32: + nBits = 32; + bSigned = TRUE; + bFloating = FALSE; + break; + + case GDT_UInt32: + nBits = 32; + bSigned = FALSE; + bFloating = FALSE; + break; + + case GDT_Float32: + case GDT_CFloat32: + nBits = 32; + bSigned = TRUE; + bFloating = TRUE; + break; + + case GDT_Float64: + case GDT_CFloat64: + nBits = 64; + bSigned = TRUE; + bFloating = TRUE; + break; + + default: + CPLAssert( FALSE ); + return GDT_Unknown; + } + + switch( eType2 ) + { + case GDT_Byte: + break; + + case GDT_Int16: + case GDT_CInt16: + nBits = MAX(nBits,16); + bSigned = TRUE; + break; + + case GDT_UInt16: + nBits = MAX(nBits,16); + break; + + case GDT_Int32: + case GDT_CInt32: + nBits = MAX(nBits,32); + bSigned = TRUE; + break; + + case GDT_UInt32: + nBits = MAX(nBits,32); + break; + + case GDT_Float32: + case GDT_CFloat32: + nBits = MAX(nBits,32); + bSigned = TRUE; + bFloating = TRUE; + break; + + case GDT_Float64: + case GDT_CFloat64: + nBits = MAX(nBits,64); + bSigned = TRUE; + bFloating = TRUE; + break; + + default: + CPLAssert( FALSE ); + return GDT_Unknown; + } + + if( nBits == 8 ) + return GDT_Byte; + else if( nBits == 16 && bComplex ) + return GDT_CInt16; + else if( nBits == 16 && bSigned ) + return GDT_Int16; + else if( nBits == 16 && !bSigned ) + return GDT_UInt16; + else if( nBits == 32 && bFloating && bComplex ) + return GDT_CFloat32; + else if( nBits == 32 && bFloating ) + return GDT_Float32; + else if( nBits == 32 && bComplex ) + return GDT_CInt32; + else if( nBits == 32 && bSigned ) + return GDT_Int32; + else if( nBits == 32 && !bSigned ) + return GDT_UInt32; + else if( nBits == 64 && bComplex ) + return GDT_CFloat64; + else + return GDT_Float64; +} + + +/************************************************************************/ +/* GDALGetDataTypeSize() */ +/************************************************************************/ + +/** + * \brief Get data type size in bits. + * + * Returns the size of a a GDT_* type in bits, not bytes! + * + * @param eDataType type, such as GDT_Byte. + * @return the number of bits or zero if it is not recognised. + */ + +int CPL_STDCALL GDALGetDataTypeSize( GDALDataType eDataType ) + +{ + switch( eDataType ) + { + case GDT_Byte: + return 8; + + case GDT_UInt16: + case GDT_Int16: + return 16; + + case GDT_UInt32: + case GDT_Int32: + case GDT_Float32: + case GDT_CInt16: + return 32; + + case GDT_Float64: + case GDT_CInt32: + case GDT_CFloat32: + return 64; + + case GDT_CFloat64: + return 128; + + default: + return 0; + } +} + +/************************************************************************/ +/* GDALDataTypeIsComplex() */ +/************************************************************************/ + +/** + * \brief Is data type complex? + * + * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32, + * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary + * component. + */ + +int CPL_STDCALL GDALDataTypeIsComplex( GDALDataType eDataType ) + +{ + switch( eDataType ) + { + case GDT_CInt16: + case GDT_CInt32: + case GDT_CFloat32: + case GDT_CFloat64: + return TRUE; + + default: + return FALSE; + } +} + +/************************************************************************/ +/* GDALGetDataTypeName() */ +/************************************************************************/ + +/** + * \brief Get name of data type. + * + * Returns a symbolic name for the data type. This is essentially the + * the enumerated item name with the GDT_ prefix removed. So GDT_Byte returns + * "Byte". The returned strings are static strings and should not be modified + * or freed by the application. These strings are useful for reporting + * datatypes in debug statements, errors and other user output. + * + * @param eDataType type to get name of. + * @return string corresponding to existing data type + * or NULL pointer if invalid type given. + */ + +const char * CPL_STDCALL GDALGetDataTypeName( GDALDataType eDataType ) + +{ + switch( eDataType ) + { + case GDT_Unknown: + return "Unknown"; + + case GDT_Byte: + return "Byte"; + + case GDT_UInt16: + return "UInt16"; + + case GDT_Int16: + return "Int16"; + + case GDT_UInt32: + return "UInt32"; + + case GDT_Int32: + return "Int32"; + + case GDT_Float32: + return "Float32"; + + case GDT_Float64: + return "Float64"; + + case GDT_CInt16: + return "CInt16"; + + case GDT_CInt32: + return "CInt32"; + + case GDT_CFloat32: + return "CFloat32"; + + case GDT_CFloat64: + return "CFloat64"; + + default: + return NULL; + } +} + +/************************************************************************/ +/* GDALGetDataTypeByName() */ +/************************************************************************/ + +/** + * \brief Get data type by symbolic name. + * + * Returns a data type corresponding to the given symbolic name. This + * function is opposite to the GDALGetDataTypeName(). + * + * @param pszName string containing the symbolic name of the type. + * + * @return GDAL data type. + */ + +GDALDataType CPL_STDCALL GDALGetDataTypeByName( const char *pszName ) + +{ + VALIDATE_POINTER1( pszName, "GDALGetDataTypeByName", GDT_Unknown ); + + int iType; + + for( iType = 1; iType < GDT_TypeCount; iType++ ) + { + if( GDALGetDataTypeName((GDALDataType)iType) != NULL + && EQUAL(GDALGetDataTypeName((GDALDataType)iType), pszName) ) + { + return (GDALDataType)iType; + } + } + + return GDT_Unknown; +} + +/************************************************************************/ +/* GDALGetAsyncStatusTypeByName() */ +/************************************************************************/ +/** + * Get AsyncStatusType by symbolic name. + * + * Returns a data type corresponding to the given symbolic name. This + * function is opposite to the GDALGetAsyncStatusTypeName(). + * + * @param pszName string containing the symbolic name of the type. + * + * @return GDAL AsyncStatus type. + */ +GDALAsyncStatusType CPL_DLL CPL_STDCALL GDALGetAsyncStatusTypeByName( const char *pszName ) +{ + VALIDATE_POINTER1( pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR); + + int iType; + + for( iType = 1; iType < GARIO_TypeCount; iType++ ) + { + if( GDALGetAsyncStatusTypeName((GDALAsyncStatusType)iType) != NULL + && EQUAL(GDALGetAsyncStatusTypeName((GDALAsyncStatusType)iType), pszName) ) + { + return (GDALAsyncStatusType)iType; + } + } + + return GARIO_ERROR; +} + + +/************************************************************************/ +/* GDALGetAsyncStatusTypeName() */ +/************************************************************************/ + +/** + * Get name of AsyncStatus data type. + * + * Returns a symbolic name for the AsyncStatus data type. This is essentially the + * the enumerated item name with the GARIO_ prefix removed. So GARIO_COMPLETE returns + * "COMPLETE". The returned strings are static strings and should not be modified + * or freed by the application. These strings are useful for reporting + * datatypes in debug statements, errors and other user output. + * + * @param eAsyncStatusType type to get name of. + * @return string corresponding to type. + */ + + const char * CPL_STDCALL GDALGetAsyncStatusTypeName( GDALAsyncStatusType eAsyncStatusType ) + +{ + switch( eAsyncStatusType ) + { + case GARIO_PENDING: + return "PENDING"; + + case GARIO_UPDATE: + return "UPDATE"; + + case GARIO_ERROR: + return "ERROR"; + + case GARIO_COMPLETE: + return "COMPLETE"; + default: + return NULL; + } +} + +/************************************************************************/ +/* GDALGetPaletteInterpretationName() */ +/************************************************************************/ + +/** + * \brief Get name of palette interpretation + * + * Returns a symbolic name for the palette interpretation. This is the + * the enumerated item name with the GPI_ prefix removed. So GPI_Gray returns + * "Gray". The returned strings are static strings and should not be modified + * or freed by the application. + * + * @param eInterp palette interpretation to get name of. + * @return string corresponding to palette interpretation. + */ + +const char *GDALGetPaletteInterpretationName( GDALPaletteInterp eInterp ) + +{ + switch( eInterp ) + { + case GPI_Gray: + return "Gray"; + + case GPI_RGB: + return "RGB"; + + case GPI_CMYK: + return "CMYK"; + + case GPI_HLS: + return "HLS"; + + default: + return "Unknown"; + } +} + +/************************************************************************/ +/* GDALGetColorInterpretationName() */ +/************************************************************************/ + +/** + * \brief Get name of color interpretation + * + * Returns a symbolic name for the color interpretation. This is derived from + * the enumerated item name with the GCI_ prefix removed, but there are some + * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red". + * The returned strings are static strings and should not be modified + * or freed by the application. + * + * @param eInterp color interpretation to get name of. + * @return string corresponding to color interpretation + * or NULL pointer if invalid enumerator given. + */ + +const char *GDALGetColorInterpretationName( GDALColorInterp eInterp ) + +{ + switch( eInterp ) + { + case GCI_Undefined: + return "Undefined"; + + case GCI_GrayIndex: + return "Gray"; + + case GCI_PaletteIndex: + return "Palette"; + + case GCI_RedBand: + return "Red"; + + case GCI_GreenBand: + return "Green"; + + case GCI_BlueBand: + return "Blue"; + + case GCI_AlphaBand: + return "Alpha"; + + case GCI_HueBand: + return "Hue"; + + case GCI_SaturationBand: + return "Saturation"; + + case GCI_LightnessBand: + return "Lightness"; + + case GCI_CyanBand: + return "Cyan"; + + case GCI_MagentaBand: + return "Magenta"; + + case GCI_YellowBand: + return "Yellow"; + + case GCI_BlackBand: + return "Black"; + + case GCI_YCbCr_YBand: + return "YCbCr_Y"; + + case GCI_YCbCr_CbBand: + return "YCbCr_Cb"; + + case GCI_YCbCr_CrBand: + return "YCbCr_Cr"; + + default: + return "Unknown"; + } +} + +/************************************************************************/ +/* GDALGetColorInterpretationByName() */ +/************************************************************************/ + +/** + * \brief Get color interpreation by symbolic name. + * + * Returns a color interpreation corresponding to the given symbolic name. This + * function is opposite to the GDALGetColorInterpretationName(). + * + * @param pszName string containing the symbolic name of the color interpretation. + * + * @return GDAL color interpretation. + * + * @since GDAL 1.7.0 + */ + +GDALColorInterp GDALGetColorInterpretationByName( const char *pszName ) + +{ + VALIDATE_POINTER1( pszName, "GDALGetColorInterpretationByName", GCI_Undefined ); + + int iType; + + for( iType = 0; iType <= GCI_Max; iType++ ) + { + if( EQUAL(GDALGetColorInterpretationName((GDALColorInterp)iType), pszName) ) + { + return (GDALColorInterp)iType; + } + } + + return GCI_Undefined; +} + +/************************************************************************/ +/* GDALGetRandomRasterSample() */ +/************************************************************************/ + +int CPL_STDCALL +GDALGetRandomRasterSample( GDALRasterBandH hBand, int nSamples, + float *pafSampleBuf ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRandomRasterSample", 0 ); + + GDALRasterBand *poBand; + + poBand = (GDALRasterBand *) GDALGetRasterSampleOverview( hBand, nSamples ); + CPLAssert( NULL != poBand ); + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + int nBlockXSize, nBlockYSize; + int nBlocksPerRow, nBlocksPerColumn; + int nSampleRate; + int bGotNoDataValue; + double dfNoDataValue; + int nActualSamples = 0; + int nBlockSampleRate; + int nBlockPixels, nBlockCount; + + dfNoDataValue = poBand->GetNoDataValue( &bGotNoDataValue ); + + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nBlocksPerRow = (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize; + nBlocksPerColumn = (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize; + + nBlockPixels = nBlockXSize * nBlockYSize; + nBlockCount = nBlocksPerRow * nBlocksPerColumn; + + if( nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 + || nBlockCount == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALGetRandomRasterSample(): returning because band" + " appears degenerate." ); + + return FALSE; + } + + nSampleRate = (int) MAX(1,sqrt((double) nBlockCount)-2.0); + + if( nSampleRate == nBlocksPerRow && nSampleRate > 1 ) + nSampleRate--; + + while( nSampleRate > 1 + && ((nBlockCount-1) / nSampleRate + 1) * nBlockPixels < nSamples ) + nSampleRate--; + + if ((nSamples / ((nBlockCount-1) / nSampleRate + 1)) == 0) + nBlockSampleRate = 1; + else + nBlockSampleRate = + MAX(1,nBlockPixels / (nSamples / ((nBlockCount-1) / nSampleRate + 1))); + + for( int iSampleBlock = 0; + iSampleBlock < nBlockCount; + iSampleBlock += nSampleRate ) + { + double dfValue = 0.0, dfReal, dfImag; + int iXBlock, iYBlock, iX, iY, iXValid, iYValid, iRemainder = 0; + GDALRasterBlock *poBlock; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = poBand->GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + continue; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + continue; + } + + if( (iXBlock + 1) * nBlockXSize > poBand->GetXSize() ) + iXValid = poBand->GetXSize() - iXBlock * nBlockXSize; + else + iXValid = nBlockXSize; + + if( (iYBlock + 1) * nBlockYSize > poBand->GetYSize() ) + iYValid = poBand->GetYSize() - iYBlock * nBlockYSize; + else + iYValid = nBlockYSize; + + for( iY = 0; iY < iYValid; iY++ ) + { + for( iX = iRemainder; iX < iXValid; iX += nBlockSampleRate ) + { + int iOffset; + + iOffset = iX + iY * nBlockXSize; + switch( poBlock->GetDataType() ) + { + case GDT_Byte: + dfValue = ((GByte *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_UInt16: + dfValue = ((GUInt16 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Float64: + dfValue = ((double *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_CInt16: + dfReal = ((GInt16 *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((GInt16 *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + case GDT_CInt32: + dfReal = ((GInt32 *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((GInt32 *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + case GDT_CFloat32: + dfReal = ((float *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((float *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + case GDT_CFloat64: + dfReal = ((double *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((double *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && dfValue == dfNoDataValue ) + continue; + + if( nActualSamples < nSamples ) + pafSampleBuf[nActualSamples++] = (float) dfValue; + } + + iRemainder = iX - iXValid; + } + + poBlock->DropLock(); + } + + return nActualSamples; +} + +/************************************************************************/ +/* GDALInitGCPs() */ +/************************************************************************/ + +void CPL_STDCALL GDALInitGCPs( int nCount, GDAL_GCP * psGCP ) + +{ + if( nCount > 0 ) + { + VALIDATE_POINTER0( psGCP, "GDALInitGCPs" ); + } + + for( int iGCP = 0; iGCP < nCount; iGCP++ ) + { + memset( psGCP, 0, sizeof(GDAL_GCP) ); + psGCP->pszId = CPLStrdup(""); + psGCP->pszInfo = CPLStrdup(""); + psGCP++; + } +} + +/************************************************************************/ +/* GDALDeinitGCPs() */ +/************************************************************************/ + +void CPL_STDCALL GDALDeinitGCPs( int nCount, GDAL_GCP * psGCP ) + +{ + if ( nCount > 0 ) + { + VALIDATE_POINTER0( psGCP, "GDALDeinitGCPs" ); + } + + for( int iGCP = 0; iGCP < nCount; iGCP++ ) + { + CPLFree( psGCP->pszId ); + CPLFree( psGCP->pszInfo ); + psGCP++; + } +} + +/************************************************************************/ +/* GDALDuplicateGCPs() */ +/************************************************************************/ + +GDAL_GCP * CPL_STDCALL +GDALDuplicateGCPs( int nCount, const GDAL_GCP *pasGCPList ) + +{ + GDAL_GCP *pasReturn; + + pasReturn = (GDAL_GCP *) CPLMalloc(sizeof(GDAL_GCP) * nCount); + GDALInitGCPs( nCount, pasReturn ); + + for( int iGCP = 0; iGCP < nCount; iGCP++ ) + { + CPLFree( pasReturn[iGCP].pszId ); + pasReturn[iGCP].pszId = CPLStrdup( pasGCPList[iGCP].pszId ); + + CPLFree( pasReturn[iGCP].pszInfo ); + pasReturn[iGCP].pszInfo = CPLStrdup( pasGCPList[iGCP].pszInfo ); + + pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel; + pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine; + pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX; + pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY; + pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ; + } + + return pasReturn; +} + +/************************************************************************/ +/* GDALFindAssociatedFile() */ +/************************************************************************/ + +/** + * Find file with alternate extension. + * + * Finds the file with the indicated extension, substituting it in place + * of the extension of the base filename. Generally used to search for + * associated files like world files .RPB files, etc. If necessary, the + * extension will be tried in both upper and lower case. If a sibling file + * list is available it will be used instead of doing VSIStatExL() calls to + * probe the file system. + * + * Note that the result is a dynamic CPLString so this method should not + * be used in a situation where there could be cross heap issues. It is + * generally imprudent for application built on GDAL to use this function + * unless they are sure they will always use the same runtime heap as GDAL. + * + * @param pszBaseFilename the filename relative to which to search. + * @param pszExt the target extension in either upper or lower case. + * @param papszSiblingFiles the list of files in the same directory as + * pszBaseFilename or NULL if they are not known. + * @param nFlags special options controlling search. None defined yet, just + * pass 0. + * + * @return an empty string if the target is not found, otherwise the target + * file with similar path style as the pszBaseFilename. + */ + +CPLString GDALFindAssociatedFile( const char *pszBaseFilename, + const char *pszExt, + char **papszSiblingFiles, + int nFlags ) + +{ + (void) nFlags; + + CPLString osTarget = CPLResetExtension( pszBaseFilename, pszExt ); + + if( papszSiblingFiles == NULL ) + { + VSIStatBufL sStatBuf; + + if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + CPLString osAltExt = pszExt; + + if( islower( pszExt[0] ) ) + osAltExt.toupper(); + else + osAltExt.tolower(); + + osTarget = CPLResetExtension( pszBaseFilename, osAltExt ); + + if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + return ""; + } + } + else + { + int iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(osTarget) ); + if( iSibling < 0 ) + return ""; + + osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling])); + osTarget += papszSiblingFiles[iSibling]; + } + + return osTarget; +} + +/************************************************************************/ +/* GDALLoadOziMapFile() */ +/************************************************************************/ + +#define MAX_GCP 30 + +int CPL_STDCALL GDALLoadOziMapFile( const char *pszFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + char **papszLines; + int iLine, nLines=0; + int nCoordinateCount = 0; + GDAL_GCP asGCPs[MAX_GCP]; + + VALIDATE_POINTER1( pszFilename, "GDALLoadOziMapFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALLoadOziMapFile", FALSE ); + VALIDATE_POINTER1( pnGCPCount, "GDALLoadOziMapFile", FALSE ); + VALIDATE_POINTER1( ppasGCPs, "GDALLoadOziMapFile", FALSE ); + + papszLines = CSLLoad2( pszFilename, 1000, 200, NULL ); + + if ( !papszLines ) + return FALSE; + + nLines = CSLCount( papszLines ); + + // Check the OziExplorer Map file signature + if ( nLines < 5 + || !EQUALN(papszLines[0], "OziExplorer Map Data File Version ", 34) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map format.", + pszFilename ); + CSLDestroy( papszLines ); + return FALSE; + } + + OGRSpatialReference oSRS; + OGRErr eErr = OGRERR_NONE; + + /* The Map Scale Factor has been introduced recently on the 6th line */ + /* and is a trick that is used to just change that line without changing */ + /* the rest of the MAP file but providing an imagery that is smaller or larger */ + /* so we have to correct the pixel/line values read in the .MAP file so they */ + /* match the actual imagery dimension. Well, this is a bad summary of what */ + /* is explained at http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */ + double dfMSF = 1; + + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MSF,", 4) ) + { + dfMSF = atof(papszLines[iLine] + 4); + if (dfMSF <= 0.01) /* Suspicious values */ + { + CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]); + dfMSF = 1; + } + } + } + + eErr = oSRS.importFromOzi( papszLines ); + if ( eErr == OGRERR_NONE ) + { + if ( ppszWKT != NULL ) + oSRS.exportToWkt( ppszWKT ); + } + + // Iterate all lines in the MAP-file + for ( iLine = 5; iLine < nLines; iLine++ ) + { + char **papszTok = NULL; + + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + + if ( CSLCount(papszTok) < 12 ) + { + CSLDestroy(papszTok); + continue; + } + + if ( CSLCount(papszTok) >= 17 + && EQUALN(papszTok[0], "Point", 5) + && !EQUAL(papszTok[2], "") + && !EQUAL(papszTok[3], "") + && nCoordinateCount < MAX_GCP ) + { + int bReadOk = FALSE; + double dfLon = 0., dfLat = 0.; + + if ( !EQUAL(papszTok[6], "") + && !EQUAL(papszTok[7], "") + && !EQUAL(papszTok[9], "") + && !EQUAL(papszTok[10], "") ) + { + // Set geographical coordinates of the pixels + dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0; + dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0; + if ( EQUAL(papszTok[11], "W") ) + dfLon = -dfLon; + if ( EQUAL(papszTok[8], "S") ) + dfLat = -dfLat; + + // Transform from the geographical coordinates into projected + // coordinates. + if ( eErr == OGRERR_NONE ) + { + OGRSpatialReference *poLatLong = NULL; + OGRCoordinateTransformation *poTransform = NULL; + + poLatLong = oSRS.CloneGeogCS(); + if ( poLatLong ) + { + poTransform = OGRCreateCoordinateTransformation( poLatLong, &oSRS ); + if ( poTransform ) + { + bReadOk = poTransform->Transform( 1, &dfLon, &dfLat ); + delete poTransform; + } + delete poLatLong; + } + } + } + else if ( !EQUAL(papszTok[14], "") + && !EQUAL(papszTok[15], "") ) + { + // Set cartesian coordinates of the pixels. + dfLon = CPLAtofM(papszTok[14]); + dfLat = CPLAtofM(papszTok[15]); + bReadOk = TRUE; + + //if ( EQUAL(papszTok[16], "S") ) + // dfLat = -dfLat; + } + + if ( bReadOk ) + { + GDALInitGCPs( 1, asGCPs + nCoordinateCount ); + + // Set pixel/line part + asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]) / dfMSF; + asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]) / dfMSF; + + asGCPs[nCoordinateCount].dfGCPX = dfLon; + asGCPs[nCoordinateCount].dfGCPY = dfLat; + + nCoordinateCount++; + } + } + + CSLDestroy( papszTok ); + } + + CSLDestroy( papszLines ); + + if ( nCoordinateCount == 0 ) + { + CPLDebug( "GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to convert the GCPs into a geotransform definition, if */ +/* possible. Otherwise we will need to use them as GCPs. */ +/* -------------------------------------------------------------------- */ + if( !GDALGCPsToGeoTransform( nCoordinateCount, asGCPs, padfGeoTransform, + CSLTestBoolean(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO")) ) ) + { + if ( pnGCPCount && ppasGCPs ) + { + CPLDebug( "GDAL", + "GDALLoadOziMapFile(%s) found file, wasn't able to derive a\n" + "first order geotransform. Using points as GCPs.", + pszFilename ); + + *ppasGCPs = (GDAL_GCP *) + CPLCalloc( sizeof(GDAL_GCP),nCoordinateCount ); + memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount ); + *pnGCPCount = nCoordinateCount; + } + } + else + { + GDALDeinitGCPs( nCoordinateCount, asGCPs ); + } + + return TRUE; +} + +#undef MAX_GCP + +/************************************************************************/ +/* GDALReadOziMapFile() */ +/************************************************************************/ + +int CPL_STDCALL GDALReadOziMapFile( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + const char *pszOzi; + FILE *fpOzi; + +/* -------------------------------------------------------------------- */ +/* Try lower case, then upper case. */ +/* -------------------------------------------------------------------- */ + pszOzi = CPLResetExtension( pszBaseFilename, "map" ); + + fpOzi = VSIFOpen( pszOzi, "rt" ); + + if ( fpOzi == NULL && VSIIsCaseSensitiveFS(pszOzi) ) + { + pszOzi = CPLResetExtension( pszBaseFilename, "MAP" ); + fpOzi = VSIFOpen( pszOzi, "rt" ); + } + + if ( fpOzi == NULL ) + return FALSE; + + VSIFClose( fpOzi ); + +/* -------------------------------------------------------------------- */ +/* We found the file, now load and parse it. */ +/* -------------------------------------------------------------------- */ + return GDALLoadOziMapFile( pszOzi, padfGeoTransform, ppszWKT, + pnGCPCount, ppasGCPs ); +} + +/************************************************************************/ +/* GDALLoadTabFile() */ +/* */ +/* Helper function for translator implementators wanting */ +/* support for MapInfo .tab-files. */ +/************************************************************************/ + +#define MAX_GCP 256 + +int CPL_STDCALL GDALLoadTabFile( const char *pszFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + char **papszLines; + char **papszTok=NULL; + int bTypeRasterFound = FALSE; + int bInsideTableDef = FALSE; + int iLine, numLines=0; + int nCoordinateCount = 0; + GDAL_GCP asGCPs[MAX_GCP]; + + papszLines = CSLLoad2( pszFilename, 1000, 200, NULL ); + + if ( !papszLines ) + return FALSE; + + numLines = CSLCount(papszLines); + + // Iterate all lines in the TAB-file + for(iLine=0; iLine 4 + && EQUAL(papszTok[4], "Label") + && nCoordinateCount < MAX_GCP ) + { + GDALInitGCPs( 1, asGCPs + nCoordinateCount ); + + asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]); + asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]); + asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]); + asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]); + if( papszTok[5] != NULL ) + { + CPLFree( asGCPs[nCoordinateCount].pszId ); + asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]); + } + + nCoordinateCount++; + } + else if( bTypeRasterFound && bInsideTableDef + && EQUAL(papszTok[0],"CoordSys") + && ppszWKT != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.importFromMICoordSys( papszLines[iLine] ) == OGRERR_NONE ) + oSRS.exportToWkt( ppszWKT ); + } + else if( EQUAL(papszTok[0],"Units") + && CSLCount(papszTok) > 1 + && EQUAL(papszTok[1],"degree") ) + { + /* + ** If we have units of "degree", but a projected coordinate + ** system we need to convert it to geographic. See to01_02.TAB. + */ + if( ppszWKT != NULL && *ppszWKT != NULL + && EQUALN(*ppszWKT,"PROJCS",6) ) + { + OGRSpatialReference oSRS, oSRSGeogCS; + char *pszSrcWKT = *ppszWKT; + + oSRS.importFromWkt( &pszSrcWKT ); + oSRSGeogCS.CopyGeogCSFrom( &oSRS ); + CPLFree( *ppszWKT ); + oSRSGeogCS.exportToWkt( ppszWKT ); + } + } + + } + + CSLDestroy(papszTok); + CSLDestroy(papszLines); + + if( nCoordinateCount == 0 ) + { + CPLDebug( "GDAL", "GDALLoadTabFile(%s) did not get any GCPs.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to convert the GCPs into a geotransform definition, if */ +/* possible. Otherwise we will need to use them as GCPs. */ +/* -------------------------------------------------------------------- */ + if( !GDALGCPsToGeoTransform( nCoordinateCount, asGCPs, padfGeoTransform, + FALSE ) ) + { + if (pnGCPCount && ppasGCPs) + { + CPLDebug( "GDAL", + "GDALLoadTabFile(%s) found file, wasn't able to derive a\n" + "first order geotransform. Using points as GCPs.", + pszFilename ); + + *ppasGCPs = (GDAL_GCP *) + CPLCalloc( sizeof(GDAL_GCP),nCoordinateCount ); + memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount ); + *pnGCPCount = nCoordinateCount; + } + } + else + { + GDALDeinitGCPs( nCoordinateCount, asGCPs ); + } + + return TRUE; +} + +#undef MAX_GCP + +/************************************************************************/ +/* GDALReadTabFile() */ +/* */ +/* Helper function for translator implementators wanting */ +/* support for MapInfo .tab-files. */ +/************************************************************************/ + +int CPL_STDCALL GDALReadTabFile( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, + ppszWKT, pnGCPCount, ppasGCPs, + NULL, NULL); +} + + +int GDALReadTabFile2( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs, + char** papszSiblingFiles, char** ppszTabFileNameOut ) +{ + const char *pszTAB; + VSILFILE *fpTAB; + + if (ppszTabFileNameOut) + *ppszTabFileNameOut = NULL; + + pszTAB = CPLResetExtension( pszBaseFilename, "tab" ); + + if (papszSiblingFiles) + { + int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB)); + if (iSibling >= 0) + { + CPLString osTabFilename = pszBaseFilename; + osTabFilename.resize(strlen(pszBaseFilename) - + strlen(CPLGetFilename(pszBaseFilename))); + osTabFilename += papszSiblingFiles[iSibling]; + if ( GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT, + pnGCPCount, ppasGCPs ) ) + { + if (ppszTabFileNameOut) + *ppszTabFileNameOut = CPLStrdup(osTabFilename); + return TRUE; + } + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try lower case, then upper case. */ +/* -------------------------------------------------------------------- */ + + fpTAB = VSIFOpenL( pszTAB, "rt" ); + + if( fpTAB == NULL && VSIIsCaseSensitiveFS(pszTAB) ) + { + pszTAB = CPLResetExtension( pszBaseFilename, "TAB" ); + fpTAB = VSIFOpenL( pszTAB, "rt" ); + } + + if( fpTAB == NULL ) + return FALSE; + + VSIFCloseL( fpTAB ); + +/* -------------------------------------------------------------------- */ +/* We found the file, now load and parse it. */ +/* -------------------------------------------------------------------- */ + if (GDALLoadTabFile( pszTAB, padfGeoTransform, ppszWKT, + pnGCPCount, ppasGCPs ) ) + { + if (ppszTabFileNameOut) + *ppszTabFileNameOut = CPLStrdup(pszTAB); + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALLoadWorldFile() */ +/************************************************************************/ + +/** + * \brief Read ESRI world file. + * + * This function reads an ESRI style world file, and formats a geotransform + * from its contents. + * + * The world file contains an affine transformation with the parameters + * in a different order than in a geotransform array. + * + *
      + *
    • geotransform[1] : width of pixel + *
    • geotransform[4] : rotational coefficient, zero for north up images. + *
    • geotransform[2] : rotational coefficient, zero for north up images. + *
    • geotransform[5] : height of pixel (but negative) + *
    • geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel. + *
    • geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel. + *
    + * + * @param pszFilename the world file name. + * @param padfGeoTransform the six double array into which the + * geotransformation should be placed. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPL_STDCALL +GDALLoadWorldFile( const char *pszFilename, double *padfGeoTransform ) + +{ + char **papszLines; + + VALIDATE_POINTER1( pszFilename, "GDALLoadWorldFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALLoadWorldFile", FALSE ); + + papszLines = CSLLoad2( pszFilename, 100, 100, NULL ); + + if ( !papszLines ) + return FALSE; + + double world[6]; + // reads the first 6 non-empty lines + int nLines = 0; + int nLinesCount = CSLCount(papszLines); + for( int i = 0; i < nLinesCount && nLines < 6; ++i ) + { + CPLString line(papszLines[i]); + if( line.Trim().empty() ) + continue; + + world[nLines] = CPLAtofM(line); + ++nLines; + } + + if( nLines == 6 + && (world[0] != 0.0 || world[2] != 0.0) + && (world[3] != 0.0 || world[1] != 0.0) ) + { + padfGeoTransform[0] = world[4]; + padfGeoTransform[1] = world[0]; + padfGeoTransform[2] = world[2]; + padfGeoTransform[3] = world[5]; + padfGeoTransform[4] = world[1]; + padfGeoTransform[5] = world[3]; + + // correct for center of pixel vs. top left of pixel + padfGeoTransform[0] -= 0.5 * padfGeoTransform[1]; + padfGeoTransform[0] -= 0.5 * padfGeoTransform[2]; + padfGeoTransform[3] -= 0.5 * padfGeoTransform[4]; + padfGeoTransform[3] -= 0.5 * padfGeoTransform[5]; + + CSLDestroy(papszLines); + + return TRUE; + } + else + { + CPLDebug( "GDAL", + "GDALLoadWorldFile(%s) found file, but it was corrupt.", + pszFilename ); + CSLDestroy(papszLines); + return FALSE; + } +} + +/************************************************************************/ +/* GDALReadWorldFile() */ +/************************************************************************/ + +/** + * \brief Read ESRI world file. + * + * This function reads an ESRI style world file, and formats a geotransform + * from its contents. It does the same as GDALLoadWorldFile() function, but + * it will form the filename for the worldfile from the filename of the raster + * file referred and the suggested extension. If no extension is provided, + * the code will internally try the unix style and windows style world file + * extensions (eg. for .tif these would be .tfw and .tifw). + * + * The world file contains an affine transformation with the parameters + * in a different order than in a geotransform array. + * + *
      + *
    • geotransform[1] : width of pixel + *
    • geotransform[4] : rotational coefficient, zero for north up images. + *
    • geotransform[2] : rotational coefficient, zero for north up images. + *
    • geotransform[5] : height of pixel (but negative) + *
    • geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel. + *
    • geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel. + *
    + * + * @param pszBaseFilename the target raster file. + * @param pszExtension the extension to use (ie. ".wld") or NULL to derive it + * from the pszBaseFilename + * @param padfGeoTransform the six double array into which the + * geotransformation should be placed. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPL_STDCALL +GDALReadWorldFile( const char *pszBaseFilename, const char *pszExtension, + double *padfGeoTransform ) + +{ + return GDALReadWorldFile2(pszBaseFilename, pszExtension, + padfGeoTransform, NULL, NULL); +} + +int GDALReadWorldFile2( const char *pszBaseFilename, const char *pszExtension, + double *padfGeoTransform, char** papszSiblingFiles, + char** ppszWorldFileNameOut ) +{ + const char *pszTFW; + char szExtUpper[32], szExtLower[32]; + int i; + + VALIDATE_POINTER1( pszBaseFilename, "GDALReadWorldFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALReadWorldFile", FALSE ); + + if (ppszWorldFileNameOut) + *ppszWorldFileNameOut = NULL; + +/* -------------------------------------------------------------------- */ +/* If we aren't given an extension, try both the unix and */ +/* windows style extensions. */ +/* -------------------------------------------------------------------- */ + if( pszExtension == NULL ) + { + char szDerivedExtension[100]; + std::string oBaseExt = CPLGetExtension( pszBaseFilename ); + + if( oBaseExt.length() < 2 ) + return FALSE; + + // windows version - first + last + 'w' + szDerivedExtension[0] = oBaseExt[0]; + szDerivedExtension[1] = oBaseExt[oBaseExt.length()-1]; + szDerivedExtension[2] = 'w'; + szDerivedExtension[3] = '\0'; + + if( GDALReadWorldFile2( pszBaseFilename, szDerivedExtension, + padfGeoTransform, papszSiblingFiles, + ppszWorldFileNameOut ) ) + return TRUE; + + // unix version - extension + 'w' + if( oBaseExt.length() > sizeof(szDerivedExtension)-2 ) + return FALSE; + + strcpy( szDerivedExtension, oBaseExt.c_str() ); + strcat( szDerivedExtension, "w" ); + return GDALReadWorldFile2( pszBaseFilename, szDerivedExtension, + padfGeoTransform, papszSiblingFiles, + ppszWorldFileNameOut ); + } + +/* -------------------------------------------------------------------- */ +/* Skip the leading period in the extension if there is one. */ +/* -------------------------------------------------------------------- */ + if( *pszExtension == '.' ) + pszExtension++; + +/* -------------------------------------------------------------------- */ +/* Generate upper and lower case versions of the extension. */ +/* -------------------------------------------------------------------- */ + CPLStrlcpy( szExtUpper, pszExtension, sizeof(szExtUpper) ); + CPLStrlcpy( szExtLower, pszExtension, sizeof(szExtLower) ); + + for( i = 0; szExtUpper[i] != '\0'; i++ ) + { + szExtUpper[i] = (char) toupper(szExtUpper[i]); + szExtLower[i] = (char) tolower(szExtLower[i]); + } + + VSIStatBufL sStatBuf; + int bGotTFW; + + pszTFW = CPLResetExtension( pszBaseFilename, szExtLower ); + + if (papszSiblingFiles) + { + int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW)); + if (iSibling >= 0) + { + CPLString osTFWFilename = pszBaseFilename; + osTFWFilename.resize(strlen(pszBaseFilename) - + strlen(CPLGetFilename(pszBaseFilename))); + osTFWFilename += papszSiblingFiles[iSibling]; + if (GDALLoadWorldFile( osTFWFilename, padfGeoTransform )) + { + if (ppszWorldFileNameOut) + *ppszWorldFileNameOut = CPLStrdup(osTFWFilename); + return TRUE; + } + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try lower case, then upper case. */ +/* -------------------------------------------------------------------- */ + + bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0; + + if( !bGotTFW && VSIIsCaseSensitiveFS(pszTFW) ) + { + pszTFW = CPLResetExtension( pszBaseFilename, szExtUpper ); + bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0; + } + + if( !bGotTFW ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We found the file, now load and parse it. */ +/* -------------------------------------------------------------------- */ + if (GDALLoadWorldFile( pszTFW, padfGeoTransform )) + { + if (ppszWorldFileNameOut) + *ppszWorldFileNameOut = CPLStrdup(pszTFW); + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALWriteWorldFile() */ +/* */ +/* Helper function for translator implementators wanting */ +/* support for ESRI world files. */ +/************************************************************************/ + +/** + * \brief Write ESRI world file. + * + * This function writes an ESRI style world file from the passed geotransform. + * + * The world file contains an affine transformation with the parameters + * in a different order than in a geotransform array. + * + *
      + *
    • geotransform[1] : width of pixel + *
    • geotransform[4] : rotational coefficient, zero for north up images. + *
    • geotransform[2] : rotational coefficient, zero for north up images. + *
    • geotransform[5] : height of pixel (but negative) + *
    • geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel. + *
    • geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel. + *
    + * + * @param pszBaseFilename the target raster file. + * @param pszExtension the extension to use (ie. ".wld"). Must not be NULL + * @param padfGeoTransform the six double array from which the + * geotransformation should be read. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPL_STDCALL +GDALWriteWorldFile( const char * pszBaseFilename, const char *pszExtension, + double *padfGeoTransform ) + +{ + VALIDATE_POINTER1( pszBaseFilename, "GDALWriteWorldFile", FALSE ); + VALIDATE_POINTER1( pszExtension, "GDALWriteWorldFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALWriteWorldFile", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Prepare the text to write to the file. */ +/* -------------------------------------------------------------------- */ + CPLString osTFWText; + + osTFWText.Printf( "%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n", + padfGeoTransform[1], + padfGeoTransform[4], + padfGeoTransform[2], + padfGeoTransform[5], + padfGeoTransform[0] + + 0.5 * padfGeoTransform[1] + + 0.5 * padfGeoTransform[2], + padfGeoTransform[3] + + 0.5 * padfGeoTransform[4] + + 0.5 * padfGeoTransform[5] ); + +/* -------------------------------------------------------------------- */ +/* Update extention, and write to disk. */ +/* -------------------------------------------------------------------- */ + const char *pszTFW; + VSILFILE *fpTFW; + + pszTFW = CPLResetExtension( pszBaseFilename, pszExtension ); + fpTFW = VSIFOpenL( pszTFW, "wt" ); + if( fpTFW == NULL ) + return FALSE; + + VSIFWriteL( (void *) osTFWText.c_str(), 1, osTFWText.size(), fpTFW ); + VSIFCloseL( fpTFW ); + + return TRUE; +} + +/************************************************************************/ +/* GDALVersionInfo() */ +/************************************************************************/ + +/** + * \brief Get runtime version information. + * + * Available pszRequest values: + *
      + *
    • "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. ie. "1170" + * Note: starting with GDAL 1.10, this string will be longer than 4 characters. + *
    • "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a string. + * ie. "20020416". + *
    • "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "1.1.7" + *
    • "--version": Returns one line version message suitable for use in + * response to --version requests. ie. "GDAL 1.1.7, released 2002/04/16" + *
    • "LICENSE": Returns the content of the LICENSE.TXT file from the GDAL_DATA directory. + * Before GDAL 1.7.0, the returned string was leaking memory but this is now resolved. + * So the result should not been freed by the caller. + *
    • "BUILD_INFO": List of NAME=VALUE pairs separated by newlines with + * information on build time options. + *
    + * + * @param pszRequest the type of version info desired, as listed above. + * + * @return an internal string containing the requested information. + */ + +const char * CPL_STDCALL GDALVersionInfo( const char *pszRequest ) + +{ +/* -------------------------------------------------------------------- */ +/* Try to capture as much build information as practical. */ +/* -------------------------------------------------------------------- */ + if( pszRequest != NULL && EQUAL(pszRequest,"BUILD_INFO") ) + { + CPLString osBuildInfo; + +#ifdef ESRI_BUILD + osBuildInfo += "ESRI_BUILD=YES\n"; +#endif +#ifdef PAM_ENABLED + osBuildInfo += "PAM_ENABLED=YES\n"; +#endif +#ifdef OGR_ENABLED + osBuildInfo += "OGR_ENABLED=YES\n"; +#endif + + CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); + CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE ); + return (char *) CPLGetTLS(CTLS_VERSIONINFO); + } + +/* -------------------------------------------------------------------- */ +/* LICENSE is a special case. We try to find and read the */ +/* LICENSE.TXT file from the GDAL_DATA directory and return it */ +/* -------------------------------------------------------------------- */ + if( pszRequest != NULL && EQUAL(pszRequest,"LICENSE") ) + { + char* pszResultLicence = (char*) CPLGetTLS( CTLS_VERSIONINFO_LICENCE ); + if( pszResultLicence != NULL ) + { + return pszResultLicence; + } + + const char *pszFilename = CPLFindFile( "etc", "LICENSE.TXT" ); + VSILFILE *fp = NULL; + int nLength; + + if( pszFilename != NULL ) + fp = VSIFOpenL( pszFilename, "r" ); + + if( fp != NULL ) + { + VSIFSeekL( fp, 0, SEEK_END ); + nLength = (int) VSIFTellL( fp ) + 1; + VSIFSeekL( fp, SEEK_SET, 0 ); + + pszResultLicence = (char *) VSICalloc(1,nLength); + if (pszResultLicence) + VSIFReadL( pszResultLicence, 1, nLength-1, fp ); + + VSIFCloseL( fp ); + } + + if (!pszResultLicence) + { + pszResultLicence = CPLStrdup( + "GDAL/OGR is released under the MIT/X license.\n" + "The LICENSE.TXT distributed with GDAL/OGR should\n" + "contain additional details.\n" ); + } + + CPLSetTLS( CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE ); + return pszResultLicence; + } + +/* -------------------------------------------------------------------- */ +/* All other strings are fairly small. */ +/* -------------------------------------------------------------------- */ + CPLString osVersionInfo; + + if( pszRequest == NULL || EQUAL(pszRequest,"VERSION_NUM") ) + osVersionInfo.Printf( "%d", GDAL_VERSION_NUM ); + else if( EQUAL(pszRequest,"RELEASE_DATE") ) + osVersionInfo.Printf( "%d", GDAL_RELEASE_DATE ); + else if( EQUAL(pszRequest,"RELEASE_NAME") ) + osVersionInfo.Printf( GDAL_RELEASE_NAME ); + else // --version + osVersionInfo.Printf( "GDAL %s, released %d/%02d/%02d", + GDAL_RELEASE_NAME, + GDAL_RELEASE_DATE / 10000, + (GDAL_RELEASE_DATE % 10000) / 100, + GDAL_RELEASE_DATE % 100 ); + + CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); // clear old value. + CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE ); + return (char *) CPLGetTLS(CTLS_VERSIONINFO); +} + +/************************************************************************/ +/* GDALCheckVersion() */ +/************************************************************************/ + +/** Return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor. + + The purpose of this method is to ensure that calling code will run with the GDAL + version it is compiled for. It is primarly intented for external plugins. + + @param nVersionMajor Major version to be tested against + @param nVersionMinor Minor version to be tested against + @param pszCallingComponentName If not NULL, in case of version mismatch, the method + will issue a failure mentionning the name of + the calling component. + + @return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor, FALSE otherwise. + */ +int CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor, + const char* pszCallingComponentName) +{ + if (nVersionMajor == GDAL_VERSION_MAJOR && + nVersionMinor == GDAL_VERSION_MINOR) + return TRUE; + + if (pszCallingComponentName) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s was compiled against GDAL %d.%d but current library version is %d.%d\n", + pszCallingComponentName, nVersionMajor, nVersionMinor, + GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR); + } + return FALSE; +} + +/************************************************************************/ +/* GDALDecToDMS() */ +/* */ +/* Translate a decimal degrees value to a DMS string with */ +/* hemisphere. */ +/************************************************************************/ + +const char * CPL_STDCALL GDALDecToDMS( double dfAngle, const char * pszAxis, + int nPrecision ) + +{ + return CPLDecToDMS( dfAngle, pszAxis, nPrecision ); +} + +/************************************************************************/ +/* GDALPackedDMSToDec() */ +/************************************************************************/ + +/** + * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees. + * + * See CPLPackedDMSToDec(). + */ + +double CPL_STDCALL GDALPackedDMSToDec( double dfPacked ) + +{ + return CPLPackedDMSToDec( dfPacked ); +} + +/************************************************************************/ +/* GDALDecToPackedDMS() */ +/************************************************************************/ + +/** + * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS). + * + * See CPLDecToPackedDMS(). + */ + +double CPL_STDCALL GDALDecToPackedDMS( double dfDec ) + +{ + return CPLDecToPackedDMS( dfDec ); +} + +/************************************************************************/ +/* GDALGCPsToGeoTransform() */ +/************************************************************************/ + +/** + * \brief Generate Geotransform from GCPs. + * + * Given a set of GCPs perform first order fit as a geotransform. + * + * Due to imprecision in the calculations the fit algorithm will often + * return non-zero rotational coefficients even if given perfectly non-rotated + * inputs. A special case has been implemented for corner corner coordinates + * given in TL, TR, BR, BL order. So when using this to get a geotransform + * from 4 corner coordinates, pass them in this order. + * + * @param nGCPCount the number of GCPs being passed in. + * @param pasGCPs the list of GCP structures. + * @param padfGeoTransform the six double array in which the affine + * geotransformation will be returned. + * @param bApproxOK If FALSE the function will fail if the geotransform is not + * essentially an exact fit (within 0.25 pixel) for all GCPs. + * + * @return TRUE on success or FALSE if there aren't enough points to prepare a + * geotransform, the pointers are ill-determined or if bApproxOK is FALSE + * and the fit is poor. + */ + +int CPL_STDCALL +GDALGCPsToGeoTransform( int nGCPCount, const GDAL_GCP *pasGCPs, + double *padfGeoTransform, int bApproxOK ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Recognise a few special cases. */ +/* -------------------------------------------------------------------- */ + if( nGCPCount < 2 ) + return FALSE; + + if( nGCPCount == 2 ) + { + if( pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel + || pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine ) + return FALSE; + + padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) + / (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel); + padfGeoTransform[2] = 0.0; + + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) + / (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine); + + padfGeoTransform[0] = pasGCPs[0].dfGCPX + - pasGCPs[0].dfGCPPixel * padfGeoTransform[1] + - pasGCPs[0].dfGCPLine * padfGeoTransform[2]; + + padfGeoTransform[3] = pasGCPs[0].dfGCPY + - pasGCPs[0].dfGCPPixel * padfGeoTransform[4] + - pasGCPs[0].dfGCPLine * padfGeoTransform[5]; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Special case of 4 corner coordinates of a non-rotated */ +/* image. The points must be in TL-TR-BR-BL order for now. */ +/* This case helps avoid some imprecision in the general */ +/* calcuations. */ +/* -------------------------------------------------------------------- */ + if( nGCPCount == 4 + && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine + && pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine + && pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel + && pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel + && pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine + && pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel + && pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY + && pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY + && pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX + && pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX + && pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY + && pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX ) + { + padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) + / (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel); + padfGeoTransform[2] = 0.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) + / (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine); + + padfGeoTransform[0] = + pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1]; + padfGeoTransform[3] = + pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5]; + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute source and destination ranges so we can normalize */ +/* the values to make the least squares computation more stable. */ +/* -------------------------------------------------------------------- */ + double min_pixel = pasGCPs[0].dfGCPPixel; + double max_pixel = pasGCPs[0].dfGCPPixel; + double min_line = pasGCPs[0].dfGCPLine; + double max_line = pasGCPs[0].dfGCPLine; + double min_geox = pasGCPs[0].dfGCPX; + double max_geox = pasGCPs[0].dfGCPX; + double min_geoy = pasGCPs[0].dfGCPY; + double max_geoy = pasGCPs[0].dfGCPY; + + for (i = 1; i < nGCPCount; ++i) { + min_pixel = MIN(min_pixel, pasGCPs[i].dfGCPPixel); + max_pixel = MAX(max_pixel, pasGCPs[i].dfGCPPixel); + min_line = MIN(min_line, pasGCPs[i].dfGCPLine); + max_line = MAX(max_line, pasGCPs[i].dfGCPLine); + min_geox = MIN(min_geox, pasGCPs[i].dfGCPX); + max_geox = MAX(max_geox, pasGCPs[i].dfGCPX); + min_geoy = MIN(min_geoy, pasGCPs[i].dfGCPY); + max_geoy = MAX(max_geoy, pasGCPs[i].dfGCPY); + } + + double EPS = 1.0e-12; + + if( ABS(max_pixel - min_pixel) < EPS + || ABS(max_line - min_line) < EPS + || ABS(max_geox - min_geox) < EPS + || ABS(max_geoy - min_geoy) < EPS) + { + return FALSE; // degenerate in at least one dimension. + } + + double pl_normalize[6], geo_normalize[6]; + + pl_normalize[0] = -min_pixel / (max_pixel - min_pixel); + pl_normalize[1] = 1.0 / (max_pixel - min_pixel); + pl_normalize[2] = 0.0; + pl_normalize[3] = -min_line / (max_line - min_line); + pl_normalize[4] = 0.0; + pl_normalize[5] = 1.0 / (max_line - min_line); + + geo_normalize[0] = -min_geox / (max_geox - min_geox); + geo_normalize[1] = 1.0 / (max_geox - min_geox); + geo_normalize[2] = 0.0; + geo_normalize[3] = -min_geoy / (max_geoy - min_geoy); + geo_normalize[4] = 0.0; + geo_normalize[5] = 1.0 / (max_geoy - min_geoy); + +/* -------------------------------------------------------------------- */ +/* In the general case, do a least squares error approximation by */ +/* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum */ +/* -------------------------------------------------------------------- */ + + double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_xx = 0.0, sum_yy = 0.0; + double sum_Lon = 0.0, sum_Lonx = 0.0, sum_Lony = 0.0; + double sum_Lat = 0.0, sum_Latx = 0.0, sum_Laty = 0.0; + double divisor; + + for (i = 0; i < nGCPCount; ++i) { + double pixel, line, geox, geoy; + + GDALApplyGeoTransform(pl_normalize, + pasGCPs[i].dfGCPPixel, + pasGCPs[i].dfGCPLine, + &pixel, &line); + GDALApplyGeoTransform(geo_normalize, + pasGCPs[i].dfGCPX, + pasGCPs[i].dfGCPY, + &geox, &geoy); + + sum_x += pixel; + sum_y += line; + sum_xy += pixel * line; + sum_xx += pixel * pixel; + sum_yy += line * line; + sum_Lon += geox; + sum_Lonx += geox * pixel; + sum_Lony += geox * line; + sum_Lat += geoy; + sum_Latx += geoy * pixel; + sum_Laty += geoy * line; + } + + divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) + + 2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx + - sum_x * sum_x * sum_yy; + +/* -------------------------------------------------------------------- */ +/* If the divisor is zero, there is no valid solution. */ +/* -------------------------------------------------------------------- */ + if (divisor == 0.0) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Compute top/left origin. */ +/* -------------------------------------------------------------------- */ + double gt_normalized[6]; + gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) + + sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) + / divisor; + + gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) + + sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) + / divisor; + +/* -------------------------------------------------------------------- */ +/* Compute X related coefficients. */ +/* -------------------------------------------------------------------- */ + gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) + + sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) + / divisor; + + gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) + + sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) + + sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) + / divisor; + +/* -------------------------------------------------------------------- */ +/* Compute Y related coefficients. */ +/* -------------------------------------------------------------------- */ + gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) + + sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) + / divisor; + + gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) + + sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) + + sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) + / divisor; + +/* -------------------------------------------------------------------- */ +/* Compose the resulting transformation with the normalization */ +/* geotransformations. */ +/* -------------------------------------------------------------------- */ + double gt1p2[6], inv_geo_normalize[6]; + if( !GDALInvGeoTransform(geo_normalize, inv_geo_normalize)) + return FALSE; + + GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2); + GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform); + +/* -------------------------------------------------------------------- */ +/* Now check if any of the input points fit this poorly. */ +/* -------------------------------------------------------------------- */ + if( !bApproxOK ) + { + double dfPixelSize = ABS(padfGeoTransform[1]) + + ABS(padfGeoTransform[2]) + + ABS(padfGeoTransform[4]) + + ABS(padfGeoTransform[5]); + + for( i = 0; i < nGCPCount; i++ ) + { + double dfErrorX, dfErrorY; + + dfErrorX = + (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] + + pasGCPs[i].dfGCPLine * padfGeoTransform[2] + + padfGeoTransform[0]) + - pasGCPs[i].dfGCPX; + dfErrorY = + (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] + + pasGCPs[i].dfGCPLine * padfGeoTransform[5] + + padfGeoTransform[3]) + - pasGCPs[i].dfGCPY; + + if( ABS(dfErrorX) > 0.25 * dfPixelSize + || ABS(dfErrorY) > 0.25 * dfPixelSize ) + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* GDALComposeGeoTransforms() */ +/************************************************************************/ + +/** + * \brief Compose two geotransforms. + * + * The resulting geotransform is the equivelent to padfGT1 and then padfGT2 + * being applied to a point. + * + * @param padfGT1 the first geotransform, six values. + * @param padfGT2 the second geotransform, six values. + * @param padfGTOut the output geotransform, six values, may safely be the same + * array as padfGT1 or padfGT2. + */ + +void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2, + double *padfGTOut) + +{ + double gtwrk[6]; + // We need to think of the geotransform in a more normal form to do + // the matrix multiple: + // + // __ __ + // | gt[1] gt[2] gt[0] | + // | gt[4] gt[5] gt[3] | + // | 0.0 0.0 1.0 | + // -- -- + // + // Then we can use normal matrix multiplication to produce the + // composed transformation. I don't actually reform the matrix + // explicitly which is why the following may seem kind of spagettish. + + gtwrk[1] = + padfGT2[1] * padfGT1[1] + + padfGT2[2] * padfGT1[4]; + gtwrk[2] = + padfGT2[1] * padfGT1[2] + + padfGT2[2] * padfGT1[5]; + gtwrk[0] = + padfGT2[1] * padfGT1[0] + + padfGT2[2] * padfGT1[3] + + padfGT2[0] * 1.0; + + gtwrk[4] = + padfGT2[4] * padfGT1[1] + + padfGT2[5] * padfGT1[4]; + gtwrk[5] = + padfGT2[4] * padfGT1[2] + + padfGT2[5] * padfGT1[5]; + gtwrk[3] = + padfGT2[4] * padfGT1[0] + + padfGT2[5] * padfGT1[3] + + padfGT2[3] * 1.0; + memcpy(padfGTOut, gtwrk, sizeof(double) * 6); +} + +/************************************************************************/ +/* GDALGeneralCmdLineProcessor() */ +/************************************************************************/ + +/** + * \brief General utility option processing. + * + * This function is intended to provide a variety of generic commandline + * options for all GDAL commandline utilities. It takes care of the following + * commandline options: + * + * --version: report version of GDAL in use. + * --build: report build info about GDAL in use. + * --license: report GDAL license info. + * --formats: report all format drivers configured. + * --format [format]: report details of one format driver. + * --optfile filename: expand an option file into the argument list. + * --config key value: set system configuration option. + * --debug [on/off/value]: set debug level. + * --mempreload dir: preload directory contents into /vsimem + * --pause: Pause for user input (allows time to attach debugger) + * --locale [locale]: Install a locale using setlocale() (debugging) + * --help-general: report detailed help on general options. + * + * The argument array is replaced "in place" and should be freed with + * CSLDestroy() when no longer needed. The typical usage looks something + * like the following. Note that the formats should be registered so that + * the --formats and --format options will work properly. + * + * int main( int argc, char ** argv ) + * { + * GDALAllRegister(); + * + * argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 ); + * if( argc < 1 ) + * exit( -argc ); + * + * @param nArgc number of values in the argument list. + * @param ppapszArgv pointer to the argument list array (will be updated in place). + * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR + * to determine which drivers should be displayed by --formats. + * If set to 0, GDAL_OF_RASTER is assumed. + * + * @return updated nArgc argument count. Return of 0 requests terminate + * without error, return of -1 requests exit with error code. + */ + +int CPL_STDCALL +GDALGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions ) + +{ + char **papszReturn = NULL; + int iArg; + char **papszArgv = *ppapszArgv; + +/* -------------------------------------------------------------------- */ +/* Preserve the program name. */ +/* -------------------------------------------------------------------- */ + papszReturn = CSLAddString( papszReturn, papszArgv[0] ); + +/* ==================================================================== */ +/* Loop over all arguments. */ +/* ==================================================================== */ + for( iArg = 1; iArg < nArgc; iArg++ ) + { +/* -------------------------------------------------------------------- */ +/* --version */ +/* -------------------------------------------------------------------- */ + if( EQUAL(papszArgv[iArg],"--version") ) + { + printf( "%s\n", GDALVersionInfo( "--version" ) ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --build */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--build") ) + { + printf( "%s", GDALVersionInfo( "BUILD_INFO" ) ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --license */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--license") ) + { + printf( "%s\n", GDALVersionInfo( "LICENSE" ) ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --config */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--config") ) + { + if( iArg + 2 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--config option given without a key and value argument." ); + CSLDestroy( papszReturn ); + return -1; + } + + CPLSetConfigOption( papszArgv[iArg+1], papszArgv[iArg+2] ); + + iArg += 2; + } + +/* -------------------------------------------------------------------- */ +/* --mempreload */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--mempreload") ) + { + int i; + + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--mempreload option given without directory path."); + CSLDestroy( papszReturn ); + return -1; + } + + char **papszFiles = CPLReadDir( papszArgv[iArg+1] ); + if( CSLCount(papszFiles) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--mempreload given invalid or empty directory."); + CSLDestroy( papszReturn ); + return -1; + } + + for( i = 0; papszFiles[i] != NULL; i++ ) + { + CPLString osOldPath, osNewPath; + VSIStatBufL sStatBuf; + + if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") ) + continue; + + osOldPath = CPLFormFilename( papszArgv[iArg+1], + papszFiles[i], NULL ); + osNewPath.Printf( "/vsimem/%s", papszFiles[i] ); + + if( VSIStatL( osOldPath, &sStatBuf ) != 0 + || VSI_ISDIR( sStatBuf.st_mode ) ) + { + CPLDebug( "VSI", "Skipping preload of %s.", + osOldPath.c_str() ); + continue; + } + + CPLDebug( "VSI", "Preloading %s to %s.", + osOldPath.c_str(), osNewPath.c_str() ); + + if( CPLCopyFile( osNewPath, osOldPath ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to copy %s to /vsimem", + osOldPath.c_str() ); + return -1; + } + } + + CSLDestroy( papszFiles ); + iArg += 1; + } + +/* -------------------------------------------------------------------- */ +/* --debug */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--debug") ) + { + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--debug option given without debug level." ); + CSLDestroy( papszReturn ); + return -1; + } + + CPLSetConfigOption( "CPL_DEBUG", papszArgv[iArg+1] ); + iArg += 1; + } + +/* -------------------------------------------------------------------- */ +/* --optfile */ +/* */ +/* Annoyingly the options inserted by --optfile will *not* be */ +/* processed properly if they are general options. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--optfile") ) + { + const char *pszLine; + FILE *fpOptFile; + + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--optfile option given without filename." ); + CSLDestroy( papszReturn ); + return -1; + } + + fpOptFile = VSIFOpen( papszArgv[iArg+1], "rb" ); + + if( fpOptFile == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open optfile '%s'.\n%s", + papszArgv[iArg+1], VSIStrerror( errno ) ); + CSLDestroy( papszReturn ); + return -1; + } + + while( (pszLine = CPLReadLine( fpOptFile )) != NULL ) + { + char **papszTokens; + int i; + + if( pszLine[0] == '#' || strlen(pszLine) == 0 ) + continue; + + papszTokens = CSLTokenizeString( pszLine ); + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++) + papszReturn = CSLAddString( papszReturn, papszTokens[i] ); + CSLDestroy( papszTokens ); + } + + VSIFClose( fpOptFile ); + + iArg += 1; + } + +/* -------------------------------------------------------------------- */ +/* --formats */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg], "--formats") ) + { + int iDr; + + if( nOptions == 0 ) + nOptions = GDAL_OF_RASTER; + + printf( "Supported Formats:\n" ); + for( iDr = 0; iDr < GDALGetDriverCount(); iDr++ ) + { + GDALDriverH hDriver = GDALGetDriver(iDr); + + const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets, *pszKind; + char** papszMD = GDALGetMetadata( hDriver, NULL ); + + if( nOptions == GDAL_OF_RASTER && + !CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) ) + continue; + if( nOptions == GDAL_OF_VECTOR && + !CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) ) + continue; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_OPEN, FALSE ) ) + pszRFlag = "r"; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATE, FALSE ) ) + pszWFlag = "w+"; + else if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATECOPY, FALSE ) ) + pszWFlag = "w"; + else + pszWFlag = "o"; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_VIRTUALIO, FALSE ) ) + pszVirtualIO = "v"; + else + pszVirtualIO = ""; + + if( CSLFetchBoolean( papszMD, GDAL_DMD_SUBDATASETS, FALSE ) ) + pszSubdatasets = "s"; + else + pszSubdatasets = ""; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) && + CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE )) + pszKind = "raster,vector"; + else if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) ) + pszKind = "raster"; + else if( CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) ) + pszKind = "vector"; + else + pszKind = "unknown kind"; + + printf( " %s -%s- (%s%s%s%s): %s\n", + GDALGetDriverShortName( hDriver ), + pszKind, + pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets, + GDALGetDriverLongName( hDriver ) ); + } + + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --format */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg], "--format") ) + { + GDALDriverH hDriver; + char **papszMD; + + CSLDestroy( papszReturn ); + + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--format option given without a format code." ); + return -1; + } + + hDriver = GDALGetDriverByName( papszArgv[iArg+1] ); + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--format option given with format '%s', but that format not\n" + "recognised. Use the --formats option to get a list of available formats,\n" + "and use the short code (ie. GTiff or HFA) as the format identifier.\n", + papszArgv[iArg+1] ); + return -1; + } + + printf( "Format Details:\n" ); + printf( " Short Name: %s\n", GDALGetDriverShortName( hDriver ) ); + printf( " Long Name: %s\n", GDALGetDriverLongName( hDriver ) ); + + papszMD = GDALGetMetadata( hDriver, NULL ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) ) + printf( " Supports: Raster\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) ) + printf( " Supports: Vector\n" ); + + const char* pszExt = CSLFetchNameValue( papszMD, GDAL_DMD_EXTENSIONS ); + if( pszExt != NULL ) + printf( " Extension%s: %s\n", (strchr(pszExt, ' ') ? "s" : ""), + pszExt ); + + if( CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) ) + printf( " Mime Type: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) ) + printf( " Help Topic: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) ); + + if( CSLFetchBoolean( papszMD, GDAL_DMD_SUBDATASETS, FALSE ) ) + printf( " Supports: Subdatasets\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_OPEN, FALSE ) ) + printf( " Supports: Open() - Open existing dataset.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATE, FALSE ) ) + printf( " Supports: Create() - Create writeable dataset.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATECOPY, FALSE ) ) + printf( " Supports: CreateCopy() - Create dataset by copying another.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_VIRTUALIO, FALSE ) ) + printf( " Supports: Virtual IO - eg. /vsimem/\n" ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) ) + printf( " Creation Datatypes: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONOPTIONLIST ) ) + { + CPLXMLNode *psCOL = + CPLParseXMLString( + CSLFetchNameValue( papszMD, + GDAL_DMD_CREATIONOPTIONLIST ) ); + char *pszFormattedXML = + CPLSerializeXMLTree( psCOL ); + + CPLDestroyXMLNode( psCOL ); + + printf( "\n%s\n", pszFormattedXML ); + CPLFree( pszFormattedXML ); + } + if( CSLFetchNameValue( papszMD, GDAL_DS_LAYER_CREATIONOPTIONLIST ) ) + { + CPLXMLNode *psCOL = + CPLParseXMLString( + CSLFetchNameValue( papszMD, + GDAL_DS_LAYER_CREATIONOPTIONLIST ) ); + char *pszFormattedXML = + CPLSerializeXMLTree( psCOL ); + + CPLDestroyXMLNode( psCOL ); + + printf( "\n%s\n", pszFormattedXML ); + CPLFree( pszFormattedXML ); + } + if( CSLFetchNameValue( papszMD, GDAL_DMD_OPENOPTIONLIST ) ) + { + CPLXMLNode *psCOL = + CPLParseXMLString( + CSLFetchNameValue( papszMD, + GDAL_DMD_OPENOPTIONLIST ) ); + char *pszFormattedXML = + CPLSerializeXMLTree( psCOL ); + + CPLDestroyXMLNode( psCOL ); + + printf( "%s\n", pszFormattedXML ); + CPLFree( pszFormattedXML ); + } + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --help-general */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--help-general") ) + { + printf( "Generic GDAL utility command options:\n" ); + printf( " --version: report version of GDAL in use.\n" ); + printf( " --license: report GDAL license info.\n" ); + printf( " --formats: report all configured format drivers.\n" ); + printf( " --format [format]: details of one format.\n" ); + printf( " --optfile filename: expand an option file into the argument list.\n" ); + printf( " --config key value: set system configuration option.\n" ); + printf( " --debug [on/off/value]: set debug level.\n" ); + printf( " --pause: wait for user input, time to attach debugger\n" ); + printf( " --locale [locale]: install locale for debugging (ie. en_US.UTF-8)\n" ); + printf( " --help-general: report detailed help on general options.\n" ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --locale */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--locale") && iArg < nArgc-1 ) + { + CPLsetlocale( LC_ALL, papszArgv[++iArg] ); + } + +/* -------------------------------------------------------------------- */ +/* --pause */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--pause") ) + { + printf( "Hit to Continue.\n" ); + CPLReadLine( stdin ); + } + +/* -------------------------------------------------------------------- */ +/* carry through unrecognised options. */ +/* -------------------------------------------------------------------- */ + else + { + papszReturn = CSLAddString( papszReturn, papszArgv[iArg] ); + } + } + + *ppapszArgv = papszReturn; + + return CSLCount( *ppapszArgv ); +} + + +/************************************************************************/ +/* _FetchDblFromMD() */ +/************************************************************************/ + +static int _FetchDblFromMD( char **papszMD, const char *pszKey, + double *padfTarget, int nCount, double dfDefault ) + +{ + char szFullKey[200]; + + sprintf( szFullKey, "%s", pszKey ); + + const char *pszValue = CSLFetchNameValue( papszMD, szFullKey ); + int i; + + for( i = 0; i < nCount; i++ ) + padfTarget[i] = dfDefault; + + if( pszValue == NULL ) + return FALSE; + + if( nCount == 1 ) + { + *padfTarget = CPLAtofM( pszValue ); + return TRUE; + } + + char **papszTokens = CSLTokenizeStringComplex( pszValue, " ,", + FALSE, FALSE ); + + if( CSLCount( papszTokens ) != nCount ) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + for( i = 0; i < nCount; i++ ) + padfTarget[i] = CPLAtofM(papszTokens[i]); + + CSLDestroy( papszTokens ); + + return TRUE; +} + +/************************************************************************/ +/* GDALExtractRPCInfo() */ +/* */ +/* Extract RPC info from metadata, and apply to an RPCInfo */ +/* structure. The inverse of this function is RPCInfoToMD() in */ +/* alg/gdal_rpc.cpp (should it be needed). */ +/************************************************************************/ + +int CPL_STDCALL GDALExtractRPCInfo( char **papszMD, GDALRPCInfo *psRPC ) + +{ + if( CSLFetchNameValue( papszMD, "LINE_NUM_COEFF" ) == NULL ) + return FALSE; + + if( CSLFetchNameValue( papszMD, "LINE_NUM_COEFF" ) == NULL + || CSLFetchNameValue( papszMD, "LINE_DEN_COEFF" ) == NULL + || CSLFetchNameValue( papszMD, "SAMP_NUM_COEFF" ) == NULL + || CSLFetchNameValue( papszMD, "SAMP_DEN_COEFF" ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Some required RPC metadata missing in GDALExtractRPCInfo()"); + return FALSE; + } + + _FetchDblFromMD( papszMD, "LINE_OFF", &(psRPC->dfLINE_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, "LINE_SCALE", &(psRPC->dfLINE_SCALE), 1, 1.0 ); + _FetchDblFromMD( papszMD, "SAMP_OFF", &(psRPC->dfSAMP_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, "SAMP_SCALE", &(psRPC->dfSAMP_SCALE), 1, 1.0 ); + _FetchDblFromMD( papszMD, "HEIGHT_OFF", &(psRPC->dfHEIGHT_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, "HEIGHT_SCALE", &(psRPC->dfHEIGHT_SCALE),1, 1.0); + _FetchDblFromMD( papszMD, "LAT_OFF", &(psRPC->dfLAT_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, "LAT_SCALE", &(psRPC->dfLAT_SCALE), 1, 1.0 ); + _FetchDblFromMD( papszMD, "LONG_OFF", &(psRPC->dfLONG_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, "LONG_SCALE", &(psRPC->dfLONG_SCALE), 1, 1.0 ); + + _FetchDblFromMD( papszMD, "LINE_NUM_COEFF", psRPC->adfLINE_NUM_COEFF, + 20, 0.0 ); + _FetchDblFromMD( papszMD, "LINE_DEN_COEFF", psRPC->adfLINE_DEN_COEFF, + 20, 0.0 ); + _FetchDblFromMD( papszMD, "SAMP_NUM_COEFF", psRPC->adfSAMP_NUM_COEFF, + 20, 0.0 ); + _FetchDblFromMD( papszMD, "SAMP_DEN_COEFF", psRPC->adfSAMP_DEN_COEFF, + 20, 0.0 ); + + _FetchDblFromMD( papszMD, "MIN_LONG", &(psRPC->dfMIN_LONG), 1, -180.0 ); + _FetchDblFromMD( papszMD, "MIN_LAT", &(psRPC->dfMIN_LAT), 1, -90.0 ); + _FetchDblFromMD( papszMD, "MAX_LONG", &(psRPC->dfMAX_LONG), 1, 180.0 ); + _FetchDblFromMD( papszMD, "MAX_LAT", &(psRPC->dfMAX_LAT), 1, 90.0 ); + + return TRUE; +} + +/************************************************************************/ +/* GDALFindAssociatedAuxFile() */ +/************************************************************************/ + +GDALDataset *GDALFindAssociatedAuxFile( const char *pszBasename, + GDALAccess eAccess, + GDALDataset *poDependentDS ) + +{ + const char *pszAuxSuffixLC = "aux"; + const char *pszAuxSuffixUC = "AUX"; + + if( EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Don't even try to look for an .aux file if we don't have a */ +/* path of any kind. */ +/* -------------------------------------------------------------------- */ + if( strlen(pszBasename) == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* We didn't find that, so try and find a corresponding aux */ +/* file. Check that we are the dependent file of the aux */ +/* file, or if we aren't verify that the dependent file does */ +/* not exist, likely mean it is us but some sort of renaming */ +/* has occured. */ +/* -------------------------------------------------------------------- */ + CPLString osJustFile = CPLGetFilename(pszBasename); // without dir + CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC); + GDALDataset *poODS = NULL; + GByte abyHeader[32]; + VSILFILE *fp; + + fp = VSIFOpenL( osAuxFilename, "rb" ); + + + if ( fp == NULL && VSIIsCaseSensitiveFS(osAuxFilename)) + { + // Can't found file with lower case suffix. Try the upper case one. + osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC); + fp = VSIFOpenL( osAuxFilename, "rb" ); + } + + if( fp != NULL ) + { + if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 && + EQUALN((char *) abyHeader,"EHFA_HEADER_TAG",15) ) + { + /* Avoid causing failure in opening of main file from SWIG bindings */ + /* when auxiliary file cannot be opened (#3269) */ + CPLTurnFailureIntoWarning(TRUE); + poODS = (GDALDataset *) GDALOpenShared( osAuxFilename, eAccess ); + CPLTurnFailureIntoWarning(FALSE); + } + VSIFCloseL( fp ); + } + +/* -------------------------------------------------------------------- */ +/* Try replacing extension with .aux */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL ) + { + const char *pszDep + = poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" ); + if( pszDep == NULL ) + { + CPLDebug( "AUX", + "Found %s but it has no dependent file, ignoring.", + osAuxFilename.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else if( !EQUAL(pszDep,osJustFile) ) + { + VSIStatBufL sStatBuf; + + if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + { + CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else + { + CPLDebug( "AUX", "%s is for file %s, not %s, but since\n" + "%s does not exist, we will use .aux file as our own.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str(), + pszDep ); + } + } + +/* -------------------------------------------------------------------- */ +/* Confirm that the aux file matches the configuration of the */ +/* dependent dataset. */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL && poDependentDS != NULL + && (poODS->GetRasterCount() != poDependentDS->GetRasterCount() + || poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() + || poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) ) + { + CPLDebug( "AUX", + "Ignoring aux file %s as its raster configuration\n" + "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)", + osAuxFilename.c_str(), + poODS->GetRasterXSize(), + poODS->GetRasterYSize(), + poODS->GetRasterCount(), + poDependentDS->GetRasterXSize(), + poDependentDS->GetRasterYSize(), + poDependentDS->GetRasterCount() ); + + GDALClose( poODS ); + poODS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Try appending .aux to the end of the filename. */ +/* -------------------------------------------------------------------- */ + if( poODS == NULL ) + { + osAuxFilename = pszBasename; + osAuxFilename += "."; + osAuxFilename += pszAuxSuffixLC; + fp = VSIFOpenL( osAuxFilename, "rb" ); + if ( fp == NULL && VSIIsCaseSensitiveFS(osAuxFilename) ) + { + // Can't found file with lower case suffix. Try the upper case one. + osAuxFilename = pszBasename; + osAuxFilename += "."; + osAuxFilename += pszAuxSuffixUC; + fp = VSIFOpenL( osAuxFilename, "rb" ); + } + + if( fp != NULL ) + { + if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 && + EQUALN((char *) abyHeader,"EHFA_HEADER_TAG",15) ) + { + /* Avoid causing failure in opening of main file from SWIG bindings */ + /* when auxiliary file cannot be opened (#3269) */ + CPLTurnFailureIntoWarning(TRUE); + poODS = (GDALDataset *) GDALOpenShared( osAuxFilename, eAccess ); + CPLTurnFailureIntoWarning(FALSE); + } + VSIFCloseL( fp ); + } + + if( poODS != NULL ) + { + const char *pszDep + = poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" ); + if( pszDep == NULL ) + { + CPLDebug( "AUX", + "Found %s but it has no dependent file, ignoring.", + osAuxFilename.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else if( !EQUAL(pszDep,osJustFile) ) + { + VSIStatBufL sStatBuf; + + if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + { + CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else + { + CPLDebug( "AUX", "%s is for file %s, not %s, but since\n" + "%s does not exist, we will use .aux file as our own.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str(), + pszDep ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Confirm that the aux file matches the configuration of the */ +/* dependent dataset. */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL && poDependentDS != NULL + && (poODS->GetRasterCount() != poDependentDS->GetRasterCount() + || poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() + || poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) ) + { + CPLDebug( "AUX", + "Ignoring aux file %s as its raster configuration\n" + "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)", + osAuxFilename.c_str(), + poODS->GetRasterXSize(), + poODS->GetRasterYSize(), + poODS->GetRasterCount(), + poDependentDS->GetRasterXSize(), + poDependentDS->GetRasterYSize(), + poDependentDS->GetRasterCount() ); + + GDALClose( poODS ); + poODS = NULL; + } + + return poODS; +} + +/************************************************************************/ +/* -------------------------------------------------------------------- */ +/* The following stubs are present to ensure that older GDAL */ +/* bridges don't fail with newer libraries. */ +/* -------------------------------------------------------------------- */ +/************************************************************************/ + +CPL_C_START + +void * CPL_STDCALL GDALCreateProjDef( const char * ) +{ + CPLDebug( "GDAL", "GDALCreateProjDef no longer supported." ); + return NULL; +} + +CPLErr CPL_STDCALL GDALReprojectToLongLat( void *, double *, double * ) +{ + CPLDebug( "GDAL", "GDALReprojectToLatLong no longer supported." ); + return CE_Failure; +} + +CPLErr CPL_STDCALL GDALReprojectFromLongLat( void *, double *, double * ) +{ + CPLDebug( "GDAL", "GDALReprojectFromLatLong no longer supported." ); + return CE_Failure; +} + +void CPL_STDCALL GDALDestroyProjDef( void * ) + +{ + CPLDebug( "GDAL", "GDALDestroyProjDef no longer supported." ); +} + +CPL_C_END + +/************************************************************************/ +/* Infrastructure to check that dataset characteristics are valid */ +/************************************************************************/ + +CPL_C_START + +/** + * \brief Return TRUE if the dataset dimensions are valid. + * + * @param nXSize raster width + * @param nYSize raster height + * + * @since GDAL 1.7.0 + */ +int GDALCheckDatasetDimensions( int nXSize, int nYSize ) +{ + if (nXSize <= 0 || nYSize <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid dataset dimensions : %d x %d", nXSize, nYSize); + return FALSE; + } + return TRUE; +} + +/** + * \brief Return TRUE if the band count is valid. + * + * If the configuration option GDAL_MAX_BAND_COUNT is defined, + * the band count will be compared to the maximum number of band allowed. + * + * @param nBands the band count + * @param bIsZeroAllowed TRUE if band count == 0 is allowed + * + * @since GDAL 1.7.0 + */ + +int GDALCheckBandCount( int nBands, int bIsZeroAllowed ) +{ + int nMaxBands = -1; + const char* pszMaxBandCount = CPLGetConfigOption("GDAL_MAX_BAND_COUNT", NULL); + if (pszMaxBandCount != NULL) + { + nMaxBands = atoi(pszMaxBandCount); + } + if (nBands < 0 || (!bIsZeroAllowed && nBands == 0) || + (nMaxBands >= 0 && nBands > nMaxBands) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid band count : %d", nBands); + return FALSE; + } + return TRUE; +} + +CPL_C_END + + +/************************************************************************/ +/* GDALSerializeGCPListToXML() */ +/************************************************************************/ + +void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode, + GDAL_GCP* pasGCPList, + int nGCPCount, + const char* pszGCPProjection ) +{ + CPLString oFmt; + + CPLXMLNode *psPamGCPList = CPLCreateXMLNode( psParentNode, CXT_Element, + "GCPList" ); + + CPLXMLNode* psLastChild = NULL; + + if( pszGCPProjection != NULL + && strlen(pszGCPProjection) > 0 ) + { + CPLSetXMLValue( psPamGCPList, "#Projection", + pszGCPProjection ); + psLastChild = psPamGCPList->psChild; + } + + for( int iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + CPLXMLNode *psXMLGCP; + GDAL_GCP *psGCP = pasGCPList + iGCP; + + psXMLGCP = CPLCreateXMLNode( NULL, CXT_Element, "GCP" ); + + if( psLastChild == NULL ) + psPamGCPList->psChild = psXMLGCP; + else + psLastChild->psNext = psXMLGCP; + psLastChild = psXMLGCP; + + CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId ); + + if( psGCP->pszInfo != NULL && strlen(psGCP->pszInfo) > 0 ) + CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo ); + + CPLSetXMLValue( psXMLGCP, "#Pixel", + oFmt.Printf( "%.4f", psGCP->dfGCPPixel ) ); + + CPLSetXMLValue( psXMLGCP, "#Line", + oFmt.Printf( "%.4f", psGCP->dfGCPLine ) ); + + CPLSetXMLValue( psXMLGCP, "#X", + oFmt.Printf( "%.12E", psGCP->dfGCPX ) ); + + CPLSetXMLValue( psXMLGCP, "#Y", + oFmt.Printf( "%.12E", psGCP->dfGCPY ) ); + + /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */ + if( psGCP->dfGCPZ != 0.0 ) + CPLSetXMLValue( psXMLGCP, "#Z", + oFmt.Printf( "%.12E", psGCP->dfGCPZ ) ); + } +} + +/************************************************************************/ +/* GDALDeserializeGCPListFromXML() */ +/************************************************************************/ + +void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList, + GDAL_GCP** ppasGCPList, + int* pnGCPCount, + char** ppszGCPProjection ) +{ + CPLXMLNode *psXMLGCP; + OGRSpatialReference oSRS; + + if( ppszGCPProjection ) + { + const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", ""); + + if( strlen(pszRawProj) > 0 + && oSRS.SetFromUserInput( pszRawProj ) == OGRERR_NONE ) + oSRS.exportToWkt( ppszGCPProjection ); + else + *ppszGCPProjection = CPLStrdup(""); + } + + // Count GCPs. + int nGCPMax = 0; + + for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL; + psXMLGCP = psXMLGCP->psNext ) + nGCPMax++; + + *ppasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax); + *pnGCPCount = 0; + + for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL; + psXMLGCP = psXMLGCP->psNext ) + { + GDAL_GCP *psGCP = *ppasGCPList + *pnGCPCount; + + if( !EQUAL(psXMLGCP->pszValue,"GCP") || + psXMLGCP->eType != CXT_Element ) + continue; + + GDALInitGCPs( 1, psGCP ); + + CPLFree( psGCP->pszId ); + psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id","")); + + CPLFree( psGCP->pszInfo ); + psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info","")); + + psGCP->dfGCPPixel = atof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0")); + psGCP->dfGCPLine = atof(CPLGetXMLValue(psXMLGCP,"Line","0.0")); + + psGCP->dfGCPX = atof(CPLGetXMLValue(psXMLGCP,"X","0.0")); + psGCP->dfGCPY = atof(CPLGetXMLValue(psXMLGCP,"Y","0.0")); + const char* pszZ = CPLGetXMLValue(psXMLGCP,"Z",NULL); + if( pszZ == NULL ) + { + /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */ + pszZ = CPLGetXMLValue(psXMLGCP,"GCPZ","0.0"); + } + psGCP->dfGCPZ = atof(pszZ); + + (*pnGCPCount) ++; + } +} diff --git a/ogr/gdal_pam.h b/ogr/gdal_pam.h new file mode 100644 index 0000000..3f0580e --- /dev/null +++ b/ogr/gdal_pam.h @@ -0,0 +1,314 @@ +/****************************************************************************** + * $Id: gdal_pam.h 26117 2013-06-29 20:22:34Z rouault $ + * + * Project: GDAL Core + * Purpose: Declaration for Peristable Auxilary Metadata classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_PAM_H_INCLUDED +#define GDAL_PAM_H_INCLUDED + +#include "gdal_priv.h" + +class GDALPamRasterBand; + +/* Clone Info Flags */ + +#define GCIF_GEOTRANSFORM 0x01 +#define GCIF_PROJECTION 0x02 +#define GCIF_METADATA 0x04 +#define GCIF_GCPS 0x08 + +#define GCIF_NODATA 0x001000 +#define GCIF_CATEGORYNAMES 0x002000 +#define GCIF_MINMAX 0x004000 +#define GCIF_SCALEOFFSET 0x008000 +#define GCIF_UNITTYPE 0x010000 +#define GCIF_COLORTABLE 0x020000 +#define GCIF_COLORINTERP 0x020000 +#define GCIF_BAND_METADATA 0x040000 +#define GCIF_RAT 0x080000 +#define GCIF_MASK 0x100000 +#define GCIF_BAND_DESCRIPTION 0x200000 + +#define GCIF_ONLY_IF_MISSING 0x10000000 +#define GCIF_PROCESS_BANDS 0x20000000 + +#define GCIF_PAM_DEFAULT (GCIF_GEOTRANSFORM | GCIF_PROJECTION | \ + GCIF_METADATA | GCIF_GCPS | \ + GCIF_NODATA | GCIF_CATEGORYNAMES | \ + GCIF_MINMAX | GCIF_SCALEOFFSET | \ + GCIF_UNITTYPE | GCIF_COLORTABLE | \ + GCIF_COLORINTERP | GCIF_BAND_METADATA | \ + GCIF_RAT | GCIF_MASK | \ + GCIF_ONLY_IF_MISSING | GCIF_PROCESS_BANDS|\ + GCIF_BAND_DESCRIPTION) + +/* GDAL PAM Flags */ +/* ERO 2011/04/13 : GPF_AUXMODE seems to be unimplemented */ +#define GPF_DIRTY 0x01 // .pam file needs to be written on close +#define GPF_TRIED_READ_FAILED 0x02 // no need to keep trying to read .pam. +#define GPF_DISABLED 0x04 // do not try any PAM stuff. +#define GPF_AUXMODE 0x08 // store info in .aux (HFA) file. +#define GPF_NOSAVE 0x10 // do not try to save pam info. + +/* ==================================================================== */ +/* GDALDatasetPamInfo */ +/* */ +/* We make these things a seperate structure of information */ +/* primarily so we can modify it without altering the size of */ +/* the GDALPamDataset. It is an effort to reduce ABI churn for */ +/* driver plugins. */ +/* ==================================================================== */ +class GDALDatasetPamInfo +{ +public: + char *pszPamFilename; + + char *pszProjection; + + int bHaveGeoTransform; + double adfGeoTransform[6]; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + CPLString osPhysicalFilename; + CPLString osSubdatasetName; + CPLString osAuxFilename; +}; + +/* ******************************************************************** */ +/* GDALPamDataset */ +/* ******************************************************************** */ + +class CPL_DLL GDALPamDataset : public GDALDataset +{ + friend class GDALPamRasterBand; + + private: + int IsPamFilenameAPotentialSiblingFile(); + + protected: + GDALPamDataset(void); + + int nPamFlags; + GDALDatasetPamInfo *psPam; + + virtual CPLXMLNode *SerializeToXML( const char *); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + virtual CPLErr TryLoadXML(char **papszSiblingFiles = NULL); + virtual CPLErr TrySaveXML(); + + CPLErr TryLoadAux(char **papszSiblingFiles = NULL); + CPLErr TrySaveAux(); + + virtual const char *BuildPamFilename(); + + void PamInitialize(); + void PamClear(); + + void SetPhysicalFilename( const char * ); + const char *GetPhysicalFilename(); + void SetSubdatasetName( const char *); + const char *GetSubdatasetName(); + + public: + virtual ~GDALPamDataset(); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + + virtual char **GetFileList(void); + + virtual CPLErr CloneInfo( GDALDataset *poSrcDS, int nCloneInfoFlags ); + + virtual CPLErr IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + + // "semi private" methods. + void MarkPamDirty() { nPamFlags |= GPF_DIRTY; } + GDALDatasetPamInfo *GetPamInfo() { return psPam; } + int GetPamFlags() { return nPamFlags; } + void SetPamFlags(int nValue ) { nPamFlags = nValue; } +}; + +/* ==================================================================== */ +/* GDALRasterBandPamInfo */ +/* */ +/* We make these things a seperate structure of information */ +/* primarily so we can modify it without altering the size of */ +/* the GDALPamDataset. It is an effort to reduce ABI churn for */ +/* driver plugins. */ +/* ==================================================================== */ +typedef struct { + GDALPamDataset *poParentDS; + + int bNoDataValueSet; + double dfNoDataValue; + + GDALColorTable *poColorTable; + + GDALColorInterp eColorInterp; + + char *pszUnitType; + char **papszCategoryNames; + + double dfOffset; + double dfScale; + + int bHaveMinMax; + double dfMin; + double dfMax; + + int bHaveStats; + double dfMean; + double dfStdDev; + + CPLXMLNode *psSavedHistograms; + + GDALRasterAttributeTable *poDefaultRAT; + +} GDALRasterBandPamInfo; + +/* ******************************************************************** */ +/* GDALPamRasterBand */ +/* ******************************************************************** */ +class CPL_DLL GDALPamRasterBand : public GDALRasterBand +{ + friend class GDALPamDataset; + + protected: + + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + void PamInitialize(); + void PamClear(); + + GDALRasterBandPamInfo *psPam; + + public: + GDALPamRasterBand(); + virtual ~GDALPamRasterBand(); + + virtual void SetDescription( const char * ); + + virtual CPLErr SetNoDataValue( double ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual GDALColorTable *GetColorTable(); + + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual GDALColorInterp GetColorInterpretation(); + + virtual const char *GetUnitType(); + CPLErr SetUnitType( const char * ); + + virtual char **GetCategoryNames(); + virtual CPLErr SetCategoryNames( char ** ); + + virtual double GetOffset( int *pbSuccess = NULL ); + CPLErr SetOffset( double ); + virtual double GetScale( int *pbSuccess = NULL ); + CPLErr SetScale( double ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, int ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram ); + + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + // new in GDALPamRasterBand. + virtual CPLErr CloneInfo( GDALRasterBand *poSrcBand, int nCloneInfoFlags ); + + // "semi private" methods. + GDALRasterBandPamInfo *GetPamInfo() { return psPam; } +}; + +// These are mainly helper functions for internal use. +int CPL_DLL PamParseHistogram( CPLXMLNode *psHistItem, + double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int *pbIncludeOutOfRange, int *pbApproxOK ); +CPLXMLNode CPL_DLL * +PamFindMatchingHistogram( CPLXMLNode *psSavedHistograms, + double dfMin, double dfMax, int nBuckets, + int bIncludeOutOfRange, int bApproxOK ); +CPLXMLNode CPL_DLL * +PamHistogramToXMLTree( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApprox ); + +// For managing the proxy file database. +const char CPL_DLL * PamGetProxy( const char * ); +const char CPL_DLL * PamAllocateProxy( const char * ); +const char CPL_DLL * PamDeallocateProxy( const char * ); +void CPL_DLL PamCleanProxyDB( void ); + +#endif /* ndef GDAL_PAM_H_INCLUDED */ diff --git a/ogr/gdal_priv.h b/ogr/gdal_priv.h new file mode 100644 index 0000000..17f0341 --- /dev/null +++ b/ogr/gdal_priv.h @@ -0,0 +1,1074 @@ +/****************************************************************************** + * $Id: gdal_priv.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Name: gdal_priv.h + * Project: GDAL Core + * Purpose: GDAL Core C++/Private declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_PRIV_H_INCLUDED +#define GDAL_PRIV_H_INCLUDED + +/* -------------------------------------------------------------------- */ +/* Predeclare various classes before pulling in gdal.h, the */ +/* public declarations. */ +/* -------------------------------------------------------------------- */ +class GDALMajorObject; +class GDALDataset; +class GDALRasterBand; +class GDALDriver; +class GDALRasterAttributeTable; +class GDALProxyDataset; +class GDALProxyRasterBand; +class GDALAsyncReader; + +/* -------------------------------------------------------------------- */ +/* Pull in the public declarations. This gets the C apis, and */ +/* also various constants. However, we will still get to */ +/* provide the real class definitions for the GDAL classes. */ +/* -------------------------------------------------------------------- */ + +#include "gdal.h" +#include "gdal_frmts.h" +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include +#include +#include "ogr_core.h" + +#define GMO_VALID 0x0001 +#define GMO_IGNORE_UNIMPLEMENTED 0x0002 +#define GMO_SUPPORT_MD 0x0004 +#define GMO_SUPPORT_MDMD 0x0008 +#define GMO_MD_DIRTY 0x0010 +#define GMO_PAM_CLASS 0x0020 + +/************************************************************************/ +/* GDALMultiDomainMetadata */ +/************************************************************************/ + +class CPL_DLL GDALMultiDomainMetadata +{ +private: + char **papszDomainList; + CPLStringList **papoMetadataLists; + +public: + GDALMultiDomainMetadata(); + ~GDALMultiDomainMetadata(); + + int XMLInit( CPLXMLNode *psMetadata, int bMerge ); + CPLXMLNode *Serialize(); + + char **GetDomainList() { return papszDomainList; } + + char **GetMetadata( const char * pszDomain = "" ); + CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + void Clear(); +}; + +/* ******************************************************************** */ +/* GDALMajorObject */ +/* */ +/* Base class providing metadata, description and other */ +/* services shared by major objects. */ +/* ******************************************************************** */ + +//! Object with metadata. + +class CPL_DLL GDALMajorObject +{ + protected: + int nFlags; // GMO_* flags. + CPLString sDescription; + GDALMultiDomainMetadata oMDMD; + + char **BuildMetadataDomainList(char** papszList, int bCheckNonEmpty, ...) CPL_NULL_TERMINATED; + + public: + GDALMajorObject(); + virtual ~GDALMajorObject(); + + int GetMOFlags(); + void SetMOFlags(int nFlags); + + virtual const char *GetDescription() const; + virtual void SetDescription( const char * ); + + virtual char **GetMetadataDomainList(); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); +}; + +/* ******************************************************************** */ +/* GDALDefaultOverviews */ +/* ******************************************************************** */ +class CPL_DLL GDALDefaultOverviews +{ + friend class GDALDataset; + + GDALDataset *poDS; + GDALDataset *poODS; + + CPLString osOvrFilename; + + int bOvrIsAux; + + int bCheckedForMask; + int bOwnMaskDS; + GDALDataset *poMaskDS; + + // for "overview datasets" we record base level info so we can + // find our way back to get overview masks. + GDALDataset *poBaseDS; + + // Stuff for deferred initialize/overviewscans... + bool bCheckedForOverviews; + void OverviewScan(); + char *pszInitName; + int bInitNameIsOVR; + char **papszInitSiblingFiles; + + public: + GDALDefaultOverviews(); + ~GDALDefaultOverviews(); + + void Initialize( GDALDataset *poDS, const char *pszName = NULL, + char **papszSiblingFiles = NULL, + int bNameIsOVR = FALSE ); + + int IsInitialized(); + + int CloseDependentDatasets(); + + // Overview Related + + int GetOverviewCount(int); + GDALRasterBand *GetOverview(int,int); + + CPLErr BuildOverviews( const char * pszBasename, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr BuildOverviewsSubDataset( const char * pszPhysicalFile, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr CleanOverviews(); + + // Mask Related + + CPLErr CreateMaskBand( int nFlags, int nBand = -1 ); + GDALRasterBand *GetMaskBand( int nBand ); + int GetMaskFlags( int nBand ); + + int HaveMaskFile( char **papszSiblings = NULL, + const char *pszBasename = NULL ); + + char** GetSiblingFiles() { return papszInitSiblingFiles; } +}; + +/* ******************************************************************** */ +/* GDALOpenInfo */ +/* */ +/* Structure of data about dataset for open functions. */ +/* ******************************************************************** */ + +class CPL_DLL GDALOpenInfo +{ + int bHasGotSiblingFiles; + char **papszSiblingFiles; + int nHeaderBytesTried; + + public: + GDALOpenInfo( const char * pszFile, int nOpenFlagsIn, + char **papszSiblingFiles = NULL ); + ~GDALOpenInfo( void ); + + char *pszFilename; + char** papszOpenOptions; + + GDALAccess eAccess; + int nOpenFlags; + + int bStatOK; + int bIsDirectory; + + VSILFILE *fpL; + + int nHeaderBytes; + GByte *pabyHeader; + + int TryToIngest(int nBytes); + char **GetSiblingFiles(); +}; + +/* ******************************************************************** */ +/* GDALDataset */ +/* ******************************************************************** */ + +class OGRLayer; +class OGRGeometry; +class OGRSpatialReference; +class OGRStyleTable; + +//! A set of associated raster bands, usually from one file. + +class CPL_DLL GDALDataset : public GDALMajorObject +{ + friend GDALDatasetH CPL_STDCALL GDALOpenEx( const char* pszFilename, + unsigned int nOpenFlags, + const char* const* papszAllowedDrivers, + const char* const* papszOpenOptions, + const char* const* papszSiblingFiles ); + + friend class GDALDriver; + friend class GDALDefaultOverviews; + friend class GDALProxyDataset; + friend class GDALDriverManager; + + protected: + GDALDriver *poDriver; + GDALAccess eAccess; + + // Stored raster information. + int nRasterXSize; + int nRasterYSize; + int nBands; + GDALRasterBand **papoBands; + + int bForceCachedIO; + + int nRefCount; + int bShared; + + GDALDataset(void); + void RasterInitialize( int, int ); + void SetBand( int, GDALRasterBand * ); + + GDALDefaultOverviews oOvManager; + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, int, int, int ); + + CPLErr BlockBasedRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, int, int, int ); + void BlockBasedFlushCache(); + + CPLErr ValidateRasterIOOrAdviseReadParameters( + const char* pszCallingFunc, + int* pbStopProcessingOnCENone, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandMap); + + virtual int CloseDependentDatasets(); + + int ValidateLayerCreationOptions( const char* const* papszLCO ); + + friend class GDALRasterBand; + + public: + virtual ~GDALDataset(); + + int GetRasterXSize( void ); + int GetRasterYSize( void ); + int GetRasterCount( void ); + GDALRasterBand *GetRasterBand( int ); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + + virtual void *GetInternalHandle( const char * ); + virtual GDALDriver *GetDriver(void); + virtual char **GetFileList(void); + + virtual const char* GetDriverName(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual GDALAsyncReader* + BeginAsyncReader(int nXOff, int nYOff, int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace, + char **papszOptions); + virtual void EndAsyncReader(GDALAsyncReader *); + + CPLErr RasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, int, int, int ); + + int Reference(); + int Dereference(); + GDALAccess GetAccess() { return eAccess; } + + int GetShared(); + void MarkAsShared(); + + static GDALDataset **GetOpenDatasets( int *pnDatasetCount ); + + CPLErr BuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + void ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (4, 5); + +private: + void *m_hMutex; + + OGRLayer* BuildLayerFromSelectInfo(void* psSelectInfo, + OGRGeometry *poSpatialFilter, + const char *pszDialect); + + public: + + virtual int GetLayerCount(); + virtual OGRLayer *GetLayer(int); + virtual OGRLayer *GetLayerByName(const char *); + virtual OGRErr DeleteLayer(int); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *CreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRLayer *CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions = NULL ); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRLayer * ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + int GetRefCount() const; + int GetSummaryRefCount() const; + OGRErr Release(); + + + static int IsGenericSQLDialect(const char* pszDialect); + + protected: + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + + OGRErr ProcessSQLCreateIndex( const char * ); + OGRErr ProcessSQLDropIndex( const char * ); + OGRErr ProcessSQLDropTable( const char * ); + OGRErr ProcessSQLAlterTableAddColumn( const char * ); + OGRErr ProcessSQLAlterTableDropColumn( const char * ); + OGRErr ProcessSQLAlterTableAlterColumn( const char * ); + OGRErr ProcessSQLAlterTableRenameColumn( const char * ); + + OGRStyleTable *m_poStyleTable; +}; + +/* ******************************************************************** */ +/* GDALRasterBlock */ +/* ******************************************************************** */ + +//! A single raster block in the block cache. + +class CPL_DLL GDALRasterBlock +{ + GDALDataType eType; + + int bDirty; + int nLockCount; + + int nXOff; + int nYOff; + + int nXSize; + int nYSize; + + void *pData; + + GDALRasterBand *poBand; + + GDALRasterBlock *poNext; + GDALRasterBlock *poPrevious; + + public: + GDALRasterBlock( GDALRasterBand *, int, int ); + virtual ~GDALRasterBlock(); + + CPLErr Internalize( void ); + void Touch( void ); + void MarkDirty( void ); + void MarkClean( void ); + void AddLock( void ) { nLockCount++; } + void DropLock( void ) { nLockCount--; } + void Detach(); + + CPLErr Write(); + + GDALDataType GetDataType() { return eType; } + int GetXOff() { return nXOff; } + int GetYOff() { return nYOff; } + int GetXSize() { return nXSize; } + int GetYSize() { return nYSize; } + int GetDirty() { return bDirty; } + int GetLockCount() { return nLockCount; } + + void *GetDataRef( void ) { return pData; } + + /// @brief Accessor to source GDALRasterBand object. + /// @return source raster band of the raster block. + GDALRasterBand *GetBand() { return poBand; } + + static int FlushCacheBlock(); + static void Verify(); + + static int SafeLockBlock( GDALRasterBlock ** ); + + /* Should only be called by GDALDestroyDriverManager() */ + static void DestroyRBMutex(); +}; + +/* ******************************************************************** */ +/* GDALColorTable */ +/* ******************************************************************** */ + +/*! A color table / palette. */ + +class CPL_DLL GDALColorTable +{ + GDALPaletteInterp eInterp; + + std::vector aoEntries; + +public: + GDALColorTable( GDALPaletteInterp = GPI_RGB ); + ~GDALColorTable(); + + GDALColorTable *Clone() const; + + GDALPaletteInterp GetPaletteInterpretation() const; + + int GetColorEntryCount() const; + const GDALColorEntry *GetColorEntry( int ) const; + int GetColorEntryAsRGB( int, GDALColorEntry * ) const; + void SetColorEntry( int, const GDALColorEntry * ); + int CreateColorRamp( int, const GDALColorEntry * , + int, const GDALColorEntry * ); +}; + +/* ******************************************************************** */ +/* GDALRasterBand */ +/* ******************************************************************** */ + +//! A single raster band (or channel). + +class CPL_DLL GDALRasterBand : public GDALMajorObject +{ + private: + CPLErr eFlushBlockErr; + + void SetFlushBlockErr( CPLErr eErr ); + + friend class GDALRasterBlock; + + protected: + GDALDataset *poDS; + int nBand; /* 1 based */ + + int nRasterXSize; + int nRasterYSize; + + GDALDataType eDataType; + GDALAccess eAccess; + + /* stuff related to blocking, and raster cache */ + int nBlockXSize; + int nBlockYSize; + int nBlocksPerRow; + int nBlocksPerColumn; + + int bSubBlockingActive; + int nSubBlocksPerRow; + int nSubBlocksPerColumn; + GDALRasterBlock **papoBlocks; + + int nBlockReads; + int bForceCachedIO; + + GDALRasterBand *poMask; + bool bOwnMask; + int nMaskFlags; + + friend class GDALDataset; + friend class GDALProxyRasterBand; + + protected: + virtual CPLErr IReadBlock( int, int, void * ) = 0; + virtual CPLErr IWriteBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int ); + CPLErr OverviewRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int ); + + int InitBlockInfo(); + + CPLErr AdoptBlock( int, int, GDALRasterBlock * ); + GDALRasterBlock *TryGetLockedBlockRef( int nXBlockOff, int nYBlockYOff ); + + public: + GDALRasterBand(); + + virtual ~GDALRasterBand(); + + int GetXSize(); + int GetYSize(); + int GetBand(); + GDALDataset*GetDataset(); + + GDALDataType GetRasterDataType( void ); + void GetBlockSize( int *, int * ); + GDALAccess GetAccess(); + + CPLErr RasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int ); + CPLErr ReadBlock( int, int, void * ); + + CPLErr WriteBlock( int, int, void * ); + + GDALRasterBlock *GetLockedBlockRef( int nXBlockOff, int nYBlockOff, + int bJustInitialize = FALSE ); + CPLErr FlushBlock( int = -1, int = -1, int bWriteDirtyBlock = TRUE ); + + unsigned char* GetIndexColorTranslationTo(/* const */ GDALRasterBand* poReferenceBand, + unsigned char* pTranslationTable = NULL, + int* pApproximateMatching = NULL); + + // New OpengIS CV_SampleDimension stuff. + + virtual CPLErr FlushCache(); + virtual char **GetCategoryNames(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual double GetScale( int *pbSuccess = NULL ); + virtual const char *GetUnitType(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr Fill(double dfRealValue, double dfImaginaryValue = 0); + + virtual CPLErr SetCategoryNames( char ** ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual CPLErr SetOffset( double ); + virtual CPLErr SetScale( double ); + virtual CPLErr SetUnitType( const char * ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc, void *pProgressData ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr ComputeRasterMinMax( int, double* ); + + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + virtual GDALRasterBand *GetRasterSampleOverview( int ); + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, int ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual CPLVirtualMem *GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); + + void ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (4, 5); +}; + +/* ******************************************************************** */ +/* GDALAllValidMaskBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALAllValidMaskBand : public GDALRasterBand +{ + protected: + virtual CPLErr IReadBlock( int, int, void * ); + + public: + GDALAllValidMaskBand( GDALRasterBand * ); + virtual ~GDALAllValidMaskBand(); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); +}; + +/* ******************************************************************** */ +/* GDALNoDataMaskBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALNoDataMaskBand : public GDALRasterBand +{ + double dfNoDataValue; + GDALRasterBand *poParent; + + protected: + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int ); + + public: + GDALNoDataMaskBand( GDALRasterBand * ); + virtual ~GDALNoDataMaskBand(); +}; + +/* ******************************************************************** */ +/* GDALNoDataValuesMaskBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALNoDataValuesMaskBand : public GDALRasterBand +{ + double *padfNodataValues; + + protected: + virtual CPLErr IReadBlock( int, int, void * ); + + public: + GDALNoDataValuesMaskBand( GDALDataset * ); + virtual ~GDALNoDataValuesMaskBand(); +}; + +/* ******************************************************************** */ +/* GDALDriver */ +/* ******************************************************************** */ + + +/** + * \brief Format specific driver. + * + * An instance of this class is created for each supported format, and + * manages information about the format. + * + * This roughly corresponds to a file format, though some + * drivers may be gateways to many formats through a secondary + * multi-library. + */ + +class CPL_DLL GDALDriver : public GDALMajorObject +{ + public: + GDALDriver(); + ~GDALDriver(); + + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + +/* -------------------------------------------------------------------- */ +/* Public C++ methods. */ +/* -------------------------------------------------------------------- */ + GDALDataset *Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ) CPL_WARN_UNUSED_RESULT; + + CPLErr Delete( const char * pszName ); + CPLErr Rename( const char * pszNewName, + const char * pszOldName ); + CPLErr CopyFiles( const char * pszNewName, + const char * pszOldName ); + + GDALDataset *CreateCopy( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ) CPL_WARN_UNUSED_RESULT; + +/* -------------------------------------------------------------------- */ +/* The following are semiprivate, not intended to be accessed */ +/* by anyone but the formats instantiating and populating the */ +/* drivers. */ +/* -------------------------------------------------------------------- */ + GDALDataset *(*pfnOpen)( GDALOpenInfo * ); + + GDALDataset *(*pfnCreate)( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + + CPLErr (*pfnDelete)( const char * pszName ); + + GDALDataset *(*pfnCreateCopy)( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + void *pDriverData; + + void (*pfnUnloadDriver)(GDALDriver *); + + /* Return 1 if the passed file is certainly recognized by the driver */ + /* Return 0 if the passed file is certainly NOT recognized by the driver */ + /* Return -1 if the passed file may be or may not be recognized by the driver, + and that a potentially costly test must be done with pfnOpen */ + int (*pfnIdentify)( GDALOpenInfo * ); + + CPLErr (*pfnRename)( const char * pszNewName, + const char * pszOldName ); + CPLErr (*pfnCopyFiles)( const char * pszNewName, + const char * pszOldName ); + + /* For legacy OGR drivers */ + GDALDataset *(*pfnOpenWithDriverArg)( GDALDriver*, GDALOpenInfo * ); + GDALDataset *(*pfnCreateVectorOnly)( GDALDriver*, + const char * pszName, + char ** papszOptions ); + CPLErr (*pfnDeleteDataSource)( GDALDriver*, + const char * pszName ); + +/* -------------------------------------------------------------------- */ +/* Helper methods. */ +/* -------------------------------------------------------------------- */ + GDALDataset *DefaultCreateCopy( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ) CPL_WARN_UNUSED_RESULT; + static CPLErr DefaultCopyMasks( GDALDataset *poSrcDS, + GDALDataset *poDstDS, + int bStrict ); + static CPLErr QuietDelete( const char * pszName ); + + CPLErr DefaultRename( const char * pszNewName, + const char * pszOldName ); + CPLErr DefaultCopyFiles( const char * pszNewName, + const char * pszOldName ); +}; + +/* ******************************************************************** */ +/* GDALDriverManager */ +/* ******************************************************************** */ + +/** + * Class for managing the registration of file format drivers. + * + * Use GetGDALDriverManager() to fetch the global singleton instance of + * this class. + */ + +class CPL_DLL GDALDriverManager : public GDALMajorObject +{ + int nDrivers; + GDALDriver **papoDrivers; + std::map oMapNameToDrivers; + + public: + GDALDriverManager(); + ~GDALDriverManager(); + + int GetDriverCount( void ); + GDALDriver *GetDriver( int ); + GDALDriver *GetDriverByName( const char * ); + + int RegisterDriver( GDALDriver * ); + void DeregisterDriver( GDALDriver * ); + + void AutoLoadDrivers(); + void AutoSkipDrivers(); +}; + +CPL_C_START +GDALDriverManager CPL_DLL * GetGDALDriverManager( void ); +CPL_C_END + +/* ******************************************************************** */ +/* GDALAsyncReader */ +/* ******************************************************************** */ + +/** + * Class used as a session object for asynchronous requests. They are + * created with GDALDataset::BeginAsyncReader(), and destroyed with + * GDALDataset::EndAsyncReader(). + */ +class CPL_DLL GDALAsyncReader +{ + protected: + GDALDataset* poDS; + int nXOff; + int nYOff; + int nXSize; + int nYSize; + void * pBuf; + int nBufXSize; + int nBufYSize; + GDALDataType eBufType; + int nBandCount; + int* panBandMap; + int nPixelSpace; + int nLineSpace; + int nBandSpace; + + public: + GDALAsyncReader(); + virtual ~GDALAsyncReader(); + + GDALDataset* GetGDALDataset() {return poDS;} + int GetXOffset() {return nXOff;} + int GetYOffset() {return nYOff;} + int GetXSize() {return nXSize;} + int GetYSize() {return nYSize;} + void * GetBuffer() {return pBuf;} + int GetBufferXSize() {return nBufXSize;} + int GetBufferYSize() {return nBufYSize;} + GDALDataType GetBufferType() {return eBufType;} + int GetBandCount() {return nBandCount;} + int* GetBandMap() {return panBandMap;} + int GetPixelSpace() {return nPixelSpace;} + int GetLineSpace() {return nLineSpace;} + int GetBandSpace() {return nBandSpace;} + + virtual GDALAsyncStatusType + GetNextUpdatedRegion(double dfTimeout, + int* pnBufXOff, int* pnBufYOff, + int* pnBufXSize, int* pnBufYSize) = 0; + virtual int LockBuffer( double dfTimeout = -1.0 ); + virtual void UnlockBuffer(); +}; + +/* ==================================================================== */ +/* An assortment of overview related stuff. */ +/* ==================================================================== */ + +/* Not a public symbol for the moment */ +CPLErr +GDALRegenerateOverviewsMultiBand(int nBands, GDALRasterBand** papoSrcBands, + int nOverviews, + GDALRasterBand*** papapoOverviewBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ); + +CPL_C_START + +#ifndef WIN32CE + +CPLErr CPL_DLL +HFAAuxBuildOverviews( const char *pszOvrFilename, GDALDataset *poParentDS, + GDALDataset **ppoDS, + int nBands, int *panBandList, + int nNewOverviews, int *panNewOverviewList, + const char *pszResampling, + GDALProgressFunc pfnProgress, + void *pProgressData ); + +#endif /* WIN32CE */ + +CPLErr CPL_DLL +GTIFFBuildOverviews( const char * pszFilename, + int nBands, GDALRasterBand **papoBandList, + int nOverviews, int * panOverviewList, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ); + +CPLErr CPL_DLL +GDALDefaultBuildOverviews( GDALDataset *hSrcDS, const char * pszBasename, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData); + +int CPL_DLL GDALBandGetBestOverviewLevel(GDALRasterBand* poBand, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize); + +int CPL_DLL GDALOvLevelAdjust( int nOvLevel, int nXSize ); + +GDALDataset CPL_DLL * +GDALFindAssociatedAuxFile( const char *pszBasefile, GDALAccess eAccess, + GDALDataset *poDependentDS ); + +/* ==================================================================== */ +/* Misc functions. */ +/* ==================================================================== */ + +CPLErr CPL_DLL GDALParseGMLCoverage( CPLXMLNode *psTree, + int *pnXSize, int *pnYSize, + double *padfGeoTransform, + char **ppszProjection ); + +/* ==================================================================== */ +/* Infrastructure to check that dataset characteristics are valid */ +/* ==================================================================== */ + +int CPL_DLL GDALCheckDatasetDimensions( int nXSize, int nYSize ); +int CPL_DLL GDALCheckBandCount( int nBands, int bIsZeroAllowed ); + + +// Test if 2 floating point values match. Usefull when comparing values +// stored as a string at some point. See #3573, #4183, #4506 +#define ARE_REAL_EQUAL(dfVal1, dfVal2) \ + (dfVal1 == dfVal2 || fabs(dfVal1 - dfVal2) < 1e-10 || (dfVal2 != 0 && fabs(1 - dfVal1 / dfVal2) < 1e-10 )) + +/* Internal use only */ + +/* CPL_DLL exported, but only for in-tree drivers that can be built as plugins */ +int CPL_DLL GDALReadWorldFile2( const char *pszBaseFilename, const char *pszExtension, + double *padfGeoTransform, char** papszSiblingFiles, + char** ppszWorldFileNameOut); +int GDALReadTabFile2( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs, + char** papszSiblingFiles, char** ppszTabFileNameOut ); + +CPL_C_END + +void GDALNullifyOpenDatasetsList(); +void** GDALGetphDMMutex(); +void** GDALGetphDLMutex(); +void GDALNullifyProxyPoolSingleton(); +GDALDriver* GDALGetAPIPROXYDriver(); +void GDALSetResponsiblePIDForCurrentThread(GIntBig responsiblePID); +GIntBig GDALGetResponsiblePIDForCurrentThread(); + +CPLString GDALFindAssociatedFile( const char *pszBasename, const char *pszExt, + char **papszSiblingFiles, int nFlags ); + +CPLErr EXIFExtractMetadata(char**& papszMetadata, + void *fpL, int nOffset, + int bSwabflag, int nTIFFHEADER, + int& nExifOffset, int& nInterOffset, int& nGPSOffset); + +int GDALValidateOpenOptions( GDALDriverH hDriver, + const char* const* papszOptionOptions); +int GDALValidateOptions( const char* pszOptionList, + const char* const* papszOptionsToValidate, + const char* pszErrorMessageOptionType, + const char* pszErrorMessageContainerName); + +#define DIV_ROUND_UP(a, b) ( ((a) % (b)) == 0 ? ((a) / (b)) : (((a) / (b)) + 1) ) + +// Number of data samples that will be used to compute approximate statistics +// (minimum value, maximum value, etc.) +#define GDALSTAT_APPROX_NUMSAMPLES 2500 + +CPL_C_START +/* Caution: for technical reason this declaration is duplicated in gdal_crs.c */ +/* so any signature change should be reflected there too */ +void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode, + GDAL_GCP* pasGCPList, + int nGCPCount, + const char* pszGCPProjection ); +void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList, + GDAL_GCP** ppasGCPList, + int* pnGCPCount, + char** ppszGCPProjection ); +CPL_C_END + +#endif /* ndef GDAL_PRIV_H_INCLUDED */ diff --git a/ogr/gdal_proxy.h b/ogr/gdal_proxy.h new file mode 100644 index 0000000..cb0351f --- /dev/null +++ b/ogr/gdal_proxy.h @@ -0,0 +1,382 @@ +/****************************************************************************** + * $Id: gdal_proxy.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: GDAL Core C++/Private declarations + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_PROXY_H_INCLUDED +#define GDAL_PROXY_H_INCLUDED + +#include "gdal.h" + +#ifdef __cplusplus + +#include "gdal_priv.h" +#include "cpl_hash_set.h" + +/* ******************************************************************** */ +/* GDALProxyDataset */ +/* ******************************************************************** */ + +class CPL_DLL GDALProxyDataset : public GDALDataset +{ + protected: + virtual GDALDataset *RefUnderlyingDataset() = 0; + virtual void UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, int, int, int ); + public: + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual void *GetInternalHandle( const char * ); + virtual GDALDriver *GetDriver(void); + virtual char **GetFileList(void); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + virtual CPLErr CreateMaskBand( int nFlags ); + +}; + +/* ******************************************************************** */ +/* GDALProxyRasterBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALProxyRasterBand : public GDALRasterBand +{ + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand() = 0; + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int ); + + public: + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ); + virtual CPLErr FlushCache(); + virtual char **GetCategoryNames(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual double GetScale( int *pbSuccess = NULL ); + virtual const char *GetUnitType(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr Fill(double dfRealValue, double dfImaginaryValue = 0); + + virtual CPLErr SetCategoryNames( char ** ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual CPLErr SetOffset( double ); + virtual CPLErr SetScale( double ); + virtual CPLErr SetUnitType( const char * ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc, void *pProgressData ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr ComputeRasterMinMax( int, double* ); + + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + virtual GDALRasterBand *GetRasterSampleOverview( int ); + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, int ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual CPLVirtualMem *GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); +}; + + +/* ******************************************************************** */ +/* GDALProxyPoolDataset */ +/* ******************************************************************** */ + +typedef struct _GDALProxyPoolCacheEntry GDALProxyPoolCacheEntry; +class GDALProxyPoolRasterBand; + +class CPL_DLL GDALProxyPoolDataset : public GDALProxyDataset +{ + private: + GIntBig responsiblePID; + + char *pszProjectionRef; + double adfGeoTransform[6]; + int bHasSrcProjection; + int bHasSrcGeoTransform; + char *pszGCPProjection; + int nGCPCount; + GDAL_GCP *pasGCPList; + CPLHashSet *metadataSet; + CPLHashSet *metadataItemSet; + + GDALProxyPoolCacheEntry* cacheEntry; + + protected: + virtual GDALDataset *RefUnderlyingDataset(); + virtual void UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset); + + friend class GDALProxyPoolRasterBand; + + public: + GDALProxyPoolDataset(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess = GA_ReadOnly, + int bShared = FALSE, + const char * pszProjectionRef = NULL, + double * padfGeoTransform = NULL); + ~GDALProxyPoolDataset(); + + void AddSrcBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + /* Special behaviour for the following methods : they return a pointer */ + /* data type, that must be cached by the proxy, so it doesn't become invalid */ + /* when the underlying object get closed */ + virtual char **GetMetadata( const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + + virtual void *GetInternalHandle( const char * pszRequest ); + + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); +}; + +/* ******************************************************************** */ +/* GDALProxyPoolRasterBand */ +/* ******************************************************************** */ + +class GDALProxyPoolOverviewRasterBand; +class GDALProxyPoolMaskBand; + +class CPL_DLL GDALProxyPoolRasterBand : public GDALProxyRasterBand +{ + private: + CPLHashSet *metadataSet; + CPLHashSet *metadataItemSet; + char *pszUnitType; + char **papszCategoryNames; + GDALColorTable *poColorTable; + + int nSizeProxyOverviewRasterBand; + GDALProxyPoolOverviewRasterBand **papoProxyOverviewRasterBand; + GDALProxyPoolMaskBand *poProxyMaskBand; + + void Init(); + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand(); + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + friend class GDALProxyPoolOverviewRasterBand; + friend class GDALProxyPoolMaskBand; + + public: + GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingRasterBand); + ~GDALProxyPoolRasterBand(); + + void AddSrcMaskBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize); + + /* Special behaviour for the following methods : they return a pointer */ + /* data type, that must be cached by the proxy, so it doesn't become invalid */ + /* when the underlying object get closed */ + virtual char **GetMetadata( const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + virtual char **GetCategoryNames(); + virtual const char *GetUnitType(); + virtual GDALColorTable *GetColorTable(); + virtual GDALRasterBand *GetOverview(int); + virtual GDALRasterBand *GetRasterSampleOverview( int nDesiredSamples); // TODO + virtual GDALRasterBand *GetMaskBand(); + +}; + +/* ******************************************************************** */ +/* GDALProxyPoolOverviewRasterBand */ +/* ******************************************************************** */ + +class GDALProxyPoolOverviewRasterBand : public GDALProxyPoolRasterBand +{ + private: + GDALProxyPoolRasterBand *poMainBand; + int nOverviewBand; + + GDALRasterBand *poUnderlyingMainRasterBand; + int nRefCountUnderlyingMainRasterBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand(); + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + public: + GDALProxyPoolOverviewRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingOverviewBand, + GDALProxyPoolRasterBand* poMainBand, + int nOverviewBand); + ~GDALProxyPoolOverviewRasterBand(); +}; + +/* ******************************************************************** */ +/* GDALProxyPoolMaskBand */ +/* ******************************************************************** */ + +class GDALProxyPoolMaskBand : public GDALProxyPoolRasterBand +{ + private: + GDALProxyPoolRasterBand *poMainBand; + + GDALRasterBand *poUnderlyingMainRasterBand; + int nRefCountUnderlyingMainRasterBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand(); + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + public: + GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingMaskBand, + GDALProxyPoolRasterBand* poMainBand); + GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALProxyPoolRasterBand* poMainBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + ~GDALProxyPoolMaskBand(); +}; + +#endif + + +/* ******************************************************************** */ +/* C types and methods declarations */ +/* ******************************************************************** */ + + +CPL_C_START + +typedef struct GDALProxyPoolDatasetHS *GDALProxyPoolDatasetH; + +GDALProxyPoolDatasetH CPL_DLL GDALProxyPoolDatasetCreate(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess, int bShared, + const char * pszProjectionRef, + double * padfGeoTransform); + +void CPL_DLL GDALProxyPoolDatasetDelete(GDALProxyPoolDatasetH hProxyPoolDataset); + +void CPL_DLL GDALProxyPoolDatasetAddSrcBandDescription( GDALProxyPoolDatasetH hProxyPoolDataset, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + +CPL_C_END + +#endif /* GDAL_PROXY_H_INCLUDED */ diff --git a/ogr/gdal_rat.cpp b/ogr/gdal_rat.cpp new file mode 100644 index 0000000..1846e88 --- /dev/null +++ b/ogr/gdal_rat.cpp @@ -0,0 +1,1822 @@ +/****************************************************************************** + * $Id: gdal_rat.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALRasterAttributeTable and related classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_rat.h" + +CPL_CVSID("$Id: gdal_rat.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/** + * \class GDALRasterAttributeTable + * + * The GDALRasterAttributeTable (or RAT) class is used to encapsulate a table + * used to provide attribute information about pixel values. Each row + * in the table applies to a range of pixel values (or a single value in + * some cases), and might have attributes such as the histogram count for + * that range, the color pixels of that range should be drawn names of classes + * or any other generic information. + * + * Raster attribute tables can be used to represent histograms, color tables, + * and classification information. + * + * Each column in a raster attribute table has a name, a type (integer, + * floating point or string), and a GDALRATFieldUsage. The usage distinguishes + * columns with particular understood purposes (such as color, histogram + * count, name) and columns that have specific purposes not understood by + * the library (long label, suitability_for_growing_wheat, etc). + * + * In the general case each row has a column indicating the minimum pixel + * values falling into that category, and a column indicating the maximum + * pixel value. These are indicated with usage values of GFU_Min, and + * GFU_Max. In other cases where each row is a discrete pixel value, one + * column of usage GFU_MinMax can be used. + * + * In other cases all the categories are of equal size and regularly spaced + * and the categorization information can be determine just by knowing the + * value at which the categories start, and the size of a category. This + * is called "Linear Binning" and the information is kept specially on + * the raster attribute table as a whole. + * + * RATs are normally associated with GDALRasterBands and be be queried + * using the GDALRasterBand::GetDefaultRAT() method. + */ + +/************************************************************************/ +/* ~GDALRasterAttributeTable() */ +/* */ +/* Virtual Destructor */ +/************************************************************************/ + +GDALRasterAttributeTable::~GDALRasterAttributeTable() +{ + +} + +/************************************************************************/ +/* ValuesIO() */ +/* */ +/* Default Implementations */ +/************************************************************************/ + +/** + * \brief Read or Write a block of doubles to/from the Attribute Table. + * + * This method is the same as the C function GDALRATValuesIOAsDouble(). + * + * @param eRWFlag Either GF_Read or GF_Write + * @param iField column of the Attribute Table + * @param iStartRow start row to start reading/writing (zero based) + * @param iLength number of rows to read or write + * @param pdfData pointer to array of doubles to read/write. Should be at least iLength long. + * + * @return CE_None or CE_Failure if iStartRow + iLength greater than number of rows in table. + */ + +CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData) +{ + int iIndex; + + if( (iStartRow + iLength) > GetRowCount() ) + { + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + pdfData[iIndex] = GetValueAsDouble(iIndex, iField); + } + } + else + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + SetValue(iIndex, iField, pdfData[iIndex]); + } + } + return CE_None; +} + +/************************************************************************/ +/* GDALRATValuesIOAsDouble() */ +/************************************************************************/ + +/** + * \brief Read or Write a block of doubles to/from the Attribute Table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::ValuesIO() + */ +CPLErr CPL_STDCALL GDALRATValuesIOAsDouble( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, double *pdfData ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATValuesIOAsDouble", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->ValuesIO(eRWFlag, iField, iStartRow, iLength, pdfData); +} + +/** + * \brief Read or Write a block of integers to/from the Attribute Table. + * + * This method is the same as the C function GDALRATValuesIOAsInteger(). + * + * @param eRWFlag Either GF_Read or GF_Write + * @param iField column of the Attribute Table + * @param iStartRow start row to start reading/writing (zero based) + * @param iLength number of rows to read or write + * @param pnData pointer to array of ints to read/write. Should be at least iLength long. + * + * @return CE_None or CE_Failure if iStartRow + iLength greater than number of rows in table. + */ + +CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData) +{ + int iIndex; + + if( (iStartRow + iLength) > GetRowCount() ) + { + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + pnData[iIndex] = GetValueAsInt(iIndex, iField); + } + } + else + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + SetValue(iIndex, iField, pnData[iIndex]); + } + } + return CE_None; +} + +/************************************************************************/ +/* GDALRATValuesIOAsInteger() */ +/************************************************************************/ + +/** + * \brief Read or Write a block of ints to/from the Attribute Table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::ValuesIO() + */ +CPLErr CPL_STDCALL GDALRATValuesIOAsInteger( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, int *pnData) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATValuesIOAsInteger", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->ValuesIO(eRWFlag, iField, iStartRow, iLength, pnData); +} + +/** + * \brief Read or Write a block of strings to/from the Attribute Table. + * + * This method is the same as the C function GDALRATValuesIOAsString(). + * When reading, papszStrList must be already allocated to the correct size. + * The caller is expected to call CPLFree on each read string. + * + * @param eRWFlag Either GF_Read or GF_Write + * @param iField column of the Attribute Table + * @param iStartRow start row to start reading/writing (zero based) + * @param iLength number of rows to read or write + * @param papszStrList pointer to array of strings to read/write. Should be at least iLength long. + * + * @return CE_None or CE_Failure if iStartRow + iLength greater than number of rows in table. + */ + +CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList) +{ + int iIndex; + + if( (iStartRow + iLength) > GetRowCount() ) + { + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + papszStrList[iIndex] = VSIStrdup(GetValueAsString(iIndex, iField)); + } + } + else + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + SetValue(iIndex, iField, papszStrList[iIndex]); + } + } + return CE_None; +} + +/************************************************************************/ +/* GDALRATValuesIOAsString() */ +/************************************************************************/ + +/** + * \brief Read or Write a block of strings to/from the Attribute Table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::ValuesIO() + */ +CPLErr CPL_STDCALL GDALRATValuesIOAsString( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, char **papszStrList) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATValuesIOAsString", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->ValuesIO(eRWFlag, iField, iStartRow, iLength, papszStrList); +} + +/************************************************************************/ +/* SetRowCount() */ +/************************************************************************/ + +/** + * \brief Set row count. + * + * Resizes the table to include the indicated number of rows. Newly created + * rows will be initialized to their default values - "" for strings, + * and zero for numeric fields. + * + * This method is the same as the C function GDALRATSetRowCount(). + * + * @param nNewCount the new number of rows. + */ + +void GDALRasterAttributeTable::SetRowCount( int nNewCount ) +{ +} + +/************************************************************************/ +/* GDALRATSetRowCount() */ +/************************************************************************/ + +/** + * \brief Set row count. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetRowCount() + */ +void CPL_STDCALL +GDALRATSetRowCount( GDALRasterAttributeTableH hRAT, int nNewCount ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetRowCount" ); + + ((GDALRasterAttributeTable *) hRAT)->SetRowCount( nNewCount ); +} + +/************************************************************************/ +/* GetRowOfValue() */ +/************************************************************************/ + +/** + * \brief Get row for pixel value. + * + * Given a raw pixel value, the raster attribute table is scanned to + * determine which row in the table applies to the pixel value. The + * row index is returned. + * + * This method is the same as the C function GDALRATGetRowOfValue(). + * + * @param dfValue the pixel value. + * + * @return the row index or -1 if no row is appropriate. + */ + +int GDALRasterAttributeTable::GetRowOfValue( double dfValue ) const +{ + return -1; +} + +/************************************************************************/ +/* GDALRATGetRowOfValue() */ +/************************************************************************/ + +/** + * \brief Get row for pixel value. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetRowOfValue() + */ +int CPL_STDCALL +GDALRATGetRowOfValue( GDALRasterAttributeTableH hRAT, double dfValue ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetRowOfValue", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetRowOfValue( dfValue ); +} + +/************************************************************************/ +/* GetRowOfValue() */ +/* */ +/* Int arg for now just converted to double. Perhaps we will */ +/* handle this in a special way some day? */ +/************************************************************************/ + +int GDALRasterAttributeTable::GetRowOfValue( int nValue ) const + +{ + return GetRowOfValue( (double) nValue ); +} + +/************************************************************************/ +/* CreateColumn() */ +/************************************************************************/ + +/** + * \brief Create new column. + * + * If the table already has rows, all row values for the new column will + * be initialized to the default value ("", or zero). The new column is + * always created as the last column, can will be column (field) + * "GetColumnCount()-1" after CreateColumn() has completed successfully. + * + * This method is the same as the C function GDALRATCreateColumn(). + * + * @param pszFieldName the name of the field to create. + * @param eFieldType the field type (integer, double or string). + * @param eFieldUsage the field usage, GFU_Generic if not known. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr GDALRasterAttributeTable::CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) + +{ + return CE_Failure; +} + +/************************************************************************/ +/* GDALRATCreateColumn() */ +/************************************************************************/ + +/** + * \brief Create new column. + * + * This function is the same as the C++ method GDALRasterAttributeTable::CreateColumn() + */ +CPLErr CPL_STDCALL GDALRATCreateColumn( GDALRasterAttributeTableH hRAT, + const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATCreateColumn", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->CreateColumn( pszFieldName, + eFieldType, + eFieldUsage ); +} + +/************************************************************************/ +/* SetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Set linear binning information. + * + * For RATs with equal sized categories (in pixel value space) that are + * evenly spaced, this method may be used to associate the linear binning + * information with the table. + * + * This method is the same as the C function GDALRATSetLinearBinning(). + * + * @param dfRow0MinIn the lower bound (pixel value) of the first category. + * @param dfBinSizeIn the width of each category (in pixel value units). + * + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr GDALRasterAttributeTable::SetLinearBinning( double dfRow0MinIn, + double dfBinSizeIn ) + +{ + return CE_Failure; +} + +/************************************************************************/ +/* GDALRATSetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Set linear binning information. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetLinearBinning() + */ +CPLErr CPL_STDCALL +GDALRATSetLinearBinning( GDALRasterAttributeTableH hRAT, + double dfRow0Min, double dfBinSize ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATSetLinearBinning", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->SetLinearBinning( + dfRow0Min, dfBinSize ); +} + +/************************************************************************/ +/* GetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Get linear binning information. + * + * Returns linear binning information if any is associated with the RAT. + * + * This method is the same as the C function GDALRATGetLinearBinning(). + * + * @param pdfRow0Min (out) the lower bound (pixel value) of the first category. + * @param pdfBinSize (out) the width of each category (in pixel value units). + * + * @return TRUE if linear binning information exists or FALSE if there is none. + */ + +int GDALRasterAttributeTable::GetLinearBinning( double *pdfRow0Min, + double *pdfBinSize ) const +{ + return FALSE; +} + +/************************************************************************/ +/* GDALRATGetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Get linear binning information. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetLinearBinning() + */ +int CPL_STDCALL +GDALRATGetLinearBinning( GDALRasterAttributeTableH hRAT, + double *pdfRow0Min, double *pdfBinSize ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetLinearBinning", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetLinearBinning( + pdfRow0Min, pdfBinSize ); +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +CPLXMLNode *GDALRasterAttributeTable::Serialize() const + +{ + CPLXMLNode *psTree = NULL; + CPLXMLNode *psRow = NULL; + + if( ( GetColumnCount() == 0 ) && ( GetRowCount() == 0 ) ) + return NULL; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GDALRasterAttributeTable" ); + +/* -------------------------------------------------------------------- */ +/* Add attributes with regular binning info if appropriate. */ +/* -------------------------------------------------------------------- */ + char szValue[128]; + double dfRow0Min, dfBinSize; + + if( GetLinearBinning(&dfRow0Min, &dfBinSize) ) + { + sprintf( szValue, "%.16g", dfRow0Min ); + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "Row0Min" ), + CXT_Text, szValue ); + + sprintf( szValue, "%.16g", dfBinSize ); + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "BinSize" ), + CXT_Text, szValue ); + } + +/* -------------------------------------------------------------------- */ +/* Define each column. */ +/* -------------------------------------------------------------------- */ + int iCol; + int iColCount = GetColumnCount(); + + for( iCol = 0; iCol < iColCount; iCol++ ) + { + CPLXMLNode *psCol; + + psCol = CPLCreateXMLNode( psTree, CXT_Element, "FieldDefn" ); + + sprintf( szValue, "%d", iCol ); + CPLCreateXMLNode( + CPLCreateXMLNode( psCol, CXT_Attribute, "index" ), + CXT_Text, szValue ); + + CPLCreateXMLElementAndValue( psCol, "Name", + GetNameOfCol(iCol) ); + + sprintf( szValue, "%d", (int) GetTypeOfCol(iCol) ); + CPLCreateXMLElementAndValue( psCol, "Type", szValue ); + + sprintf( szValue, "%d", (int) GetUsageOfCol(iCol) ); + CPLCreateXMLElementAndValue( psCol, "Usage", szValue ); + } + +/* -------------------------------------------------------------------- */ +/* Write out each row. */ +/* -------------------------------------------------------------------- */ + int iRow; + int iRowCount = GetRowCount(); + CPLXMLNode *psTail = NULL; + + for( iRow = 0; iRow < iRowCount; iRow++ ) + { + psRow = CPLCreateXMLNode( NULL, CXT_Element, "Row" ); + if( psTail == NULL ) + CPLAddXMLChild( psTree, psRow ); + else + psTail->psNext = psRow; + psTail = psRow; + + sprintf( szValue, "%d", iRow ); + CPLCreateXMLNode( + CPLCreateXMLNode( psRow, CXT_Attribute, "index" ), + CXT_Text, szValue ); + + for( iCol = 0; iCol < iColCount; iCol++ ) + { + const char *pszValue = szValue; + + if( GetTypeOfCol(iCol) == GFT_Integer ) + sprintf( szValue, "%d", GetValueAsInt(iRow, iCol) ); + else if( GetTypeOfCol(iCol) == GFT_Real ) + sprintf( szValue, "%.16g", GetValueAsDouble(iRow, iCol) ); + else + pszValue = GetValueAsString(iRow, iCol); + + CPLCreateXMLElementAndValue( psRow, "F", pszValue ); + } + } + + return psTree; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr GDALRasterAttributeTable::XMLInit( CPLXMLNode *psTree, + const char * /*pszVRTPath*/ ) + +{ + CPLAssert( GetRowCount() == 0 && GetColumnCount() == 0 ); + +/* -------------------------------------------------------------------- */ +/* Linear binning. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psTree, "Row0Min", NULL ) + && CPLGetXMLValue( psTree, "BinSize", NULL ) ) + { + SetLinearBinning( atof(CPLGetXMLValue( psTree, "Row0Min","" )), + atof(CPLGetXMLValue( psTree, "BinSize","" )) ); + } + +/* -------------------------------------------------------------------- */ +/* Column definitions */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psChild; + + for( psChild = psTree->psChild; psChild != NULL; psChild = psChild->psNext) + { + if( psChild->eType == CXT_Element + && EQUAL(psChild->pszValue,"FieldDefn") ) + { + CreateColumn( + CPLGetXMLValue( psChild, "Name", "" ), + (GDALRATFieldType) atoi(CPLGetXMLValue( psChild, "Type", "1" )), + (GDALRATFieldUsage) atoi(CPLGetXMLValue( psChild, "Usage","0"))); + } + } + +/* -------------------------------------------------------------------- */ +/* Row data. */ +/* -------------------------------------------------------------------- */ + for( psChild = psTree->psChild; psChild != NULL; psChild = psChild->psNext) + { + if( psChild->eType == CXT_Element + && EQUAL(psChild->pszValue,"Row") ) + { + int iRow = atoi(CPLGetXMLValue(psChild,"index","0")); + int iField = 0; + CPLXMLNode *psF; + + for( psF = psChild->psChild; psF != NULL; psF = psF->psNext ) + { + if( psF->eType != CXT_Element || !EQUAL(psF->pszValue,"F") ) + continue; + + if( psF->psChild != NULL && psF->psChild->eType == CXT_Text ) + SetValue( iRow, iField++, psF->psChild->pszValue ); + else + SetValue( iRow, iField++, "" ); + } + } + } + + return CE_None; +} + + +/************************************************************************/ +/* InitializeFromColorTable() */ +/************************************************************************/ + +/** + * \brief Initialize from color table. + * + * This method will setup a whole raster attribute table based on the + * contents of the passed color table. The Value (GFU_MinMax), + * Red (GFU_Red), Green (GFU_Green), Blue (GFU_Blue), and Alpha (GFU_Alpha) + * fields are created, and a row is set for each entry in the color table. + * + * The raster attribute table must be empty before calling + * InitializeFromColorTable(). + * + * The Value fields are set based on the implicit assumption with color + * tables that entry 0 applies to pixel value 0, 1 to 1, etc. + * + * This method is the same as the C function GDALRATInitializeFromColorTable(). + * + * @param poTable the color table to copy from. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr GDALRasterAttributeTable::InitializeFromColorTable( + const GDALColorTable *poTable ) + +{ + int iRow; + + if( GetRowCount() > 0 || GetColumnCount() > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Raster Attribute Table not empty in InitializeFromColorTable()" ); + return CE_Failure; + } + + SetLinearBinning( 0.0, 1.0 ); + CreateColumn( "Value", GFT_Integer, GFU_MinMax ); + CreateColumn( "Red", GFT_Integer, GFU_Red ); + CreateColumn( "Green", GFT_Integer, GFU_Green ); + CreateColumn( "Blue", GFT_Integer, GFU_Blue ); + CreateColumn( "Alpha", GFT_Integer, GFU_Alpha ); + + SetRowCount( poTable->GetColorEntryCount() ); + + for( iRow = 0; iRow < poTable->GetColorEntryCount(); iRow++ ) + { + GDALColorEntry sEntry; + + poTable->GetColorEntryAsRGB( iRow, &sEntry ); + + SetValue( iRow, 0, iRow ); + SetValue( iRow, 1, sEntry.c1 ); + SetValue( iRow, 2, sEntry.c2 ); + SetValue( iRow, 3, sEntry.c3 ); + SetValue( iRow, 4, sEntry.c4 ); + } + + return CE_None; +} + +/************************************************************************/ +/* GDALRATInitializeFromColorTable() */ +/************************************************************************/ + +/** + * \brief Initialize from color table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::InitializeFromColorTable() + */ +CPLErr CPL_STDCALL +GDALRATInitializeFromColorTable( GDALRasterAttributeTableH hRAT, + GDALColorTableH hCT ) + + +{ + VALIDATE_POINTER1( hRAT, "GDALRATInitializeFromColorTable", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)-> + InitializeFromColorTable( (GDALColorTable *) hCT ); +} + +/************************************************************************/ +/* TranslateToColorTable() */ +/************************************************************************/ + +/** + * \brief Translate to a color table. + * + * This method will attempt to create a corresponding GDALColorTable from + * this raster attribute table. + * + * This method is the same as the C function GDALRATTranslateToColorTable(). + * + * @param nEntryCount The number of entries to produce (0 to nEntryCount-1), or -1 to auto-determine the number of entries. + * + * @return the generated color table or NULL on failure. + */ + +GDALColorTable *GDALRasterAttributeTable::TranslateToColorTable( + int nEntryCount ) + +{ +/* -------------------------------------------------------------------- */ +/* Establish which fields are red, green, blue and alpha. */ +/* -------------------------------------------------------------------- */ + int iRed, iGreen, iBlue, iAlpha; + + iRed = GetColOfUsage( GFU_Red ); + iGreen = GetColOfUsage( GFU_Green ); + iBlue = GetColOfUsage( GFU_Blue ); + iAlpha = GetColOfUsage( GFU_Alpha ); + + if( iRed == -1 || iGreen == -1 || iBlue == -1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we aren't given an explicit number of values to scan for, */ +/* search for the maximum "max" value. */ +/* -------------------------------------------------------------------- */ + if( nEntryCount == -1 ) + { + int iRow; + int iMaxCol; + + iMaxCol = GetColOfUsage( GFU_Max ); + if( iMaxCol == -1 ) + iMaxCol = GetColOfUsage( GFU_MinMax ); + + if( iMaxCol == -1 || GetRowCount() == 0 ) + return NULL; + + for( iRow = 0; iRow < GetRowCount(); iRow++ ) + nEntryCount = MAX(nEntryCount,GetValueAsInt(iRow,iMaxCol)+1); + + if( nEntryCount < 0 ) + return NULL; + + // restrict our number of entries to something vaguely sensible + nEntryCount = MIN(65535,nEntryCount); + } + +/* -------------------------------------------------------------------- */ +/* Assign values to color table. */ +/* -------------------------------------------------------------------- */ + GDALColorTable *poCT = new GDALColorTable(); + int iEntry; + + for( iEntry = 0; iEntry < nEntryCount; iEntry++ ) + { + GDALColorEntry sColor; + int iRow = GetRowOfValue( iEntry ); + + if( iRow == -1 ) + { + sColor.c1 = sColor.c2 = sColor.c3 = sColor.c4 = 0; + } + else + { + sColor.c1 = (short) GetValueAsInt( iRow, iRed ); + sColor.c2 = (short) GetValueAsInt( iRow, iGreen ); + sColor.c3 = (short) GetValueAsInt( iRow, iBlue ); + if( iAlpha == -1 ) + sColor.c4 = 255; + else + sColor.c4 = (short) GetValueAsInt( iRow, iAlpha ); + } + + poCT->SetColorEntry( iEntry, &sColor ); + } + + return poCT; +} + +/************************************************************************/ +/* GDALRATInitializeFromColorTable() */ +/************************************************************************/ + +/** + * \brief Translate to a color table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::TranslateToColorTable() + */ +GDALColorTableH CPL_STDCALL +GDALRATTranslateToColorTable( GDALRasterAttributeTableH hRAT, + int nEntryCount ) + + +{ + VALIDATE_POINTER1( hRAT, "GDALRATTranslateToColorTable", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)-> + TranslateToColorTable( nEntryCount ); +} + + +/************************************************************************/ +/* DumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump RAT in readable form. + * + * Currently the readable form is the XML encoding ... only barely + * readable. + * + * This method is the same as the C function GDALRATDumpReadable(). + * + * @param fp file to dump to or NULL for stdout. + */ + +void GDALRasterAttributeTable::DumpReadable( FILE * fp ) + +{ + CPLXMLNode *psTree = Serialize(); + char *pszXMLText = CPLSerializeXMLTree( psTree ); + + CPLDestroyXMLNode( psTree ); + + if( fp == NULL ) + fp = stdout; + + fprintf( fp, "%s\n", pszXMLText ); + + CPLFree( pszXMLText ); +} + +/************************************************************************/ +/* GDALRATDumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump RAT in readable form. + * + * This function is the same as the C++ method GDALRasterAttributeTable::DumpReadable() + */ +void CPL_STDCALL +GDALRATDumpReadable( GDALRasterAttributeTableH hRAT, FILE *fp ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATDumpReadable" ); + + ((GDALRasterAttributeTable *) hRAT)->DumpReadable( fp ); +} + + +/* \class GDALDefaultRasterAttributeTable + * + * An implementation of GDALRasterAttributeTable that keeps + * all data in memory. This is the same as the implementation + * of GDALRasterAttributeTable in GDAL <= 1.10. + */ + +/************************************************************************/ +/* GDALDefaultRasterAttributeTable() */ +/* */ +/* Simple initialization constructor. */ +/************************************************************************/ + +//! Construct empty table. + +GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable() + +{ + bColumnsAnalysed = FALSE; + nMinCol = -1; + nMaxCol = -1; + bLinearBinning = FALSE; + dfRow0Min = -0.5; + dfBinSize = 1.0; + nRowCount = 0; +} + +/************************************************************************/ +/* GDALCreateRasterAttributeTable() */ +/************************************************************************/ + +/** + * \brief Construct empty table. + * + * This function is the same as the C++ method GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable() + */ +GDALRasterAttributeTableH CPL_STDCALL GDALCreateRasterAttributeTable() + +{ + return (GDALRasterAttributeTableH) (new GDALDefaultRasterAttributeTable()); +} + +/************************************************************************/ +/* GDALDefaultRasterAttributeTable() */ +/************************************************************************/ + +//! Copy constructor. + +GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable( + const GDALDefaultRasterAttributeTable &oOther ) + +{ + // We have tried to be careful to allow wholesale assignment + *this = oOther; +} + +/************************************************************************/ +/* ~GDALDefaultRasterAttributeTable() */ +/* */ +/* All magic done by magic by the container destructors. */ +/************************************************************************/ + +GDALDefaultRasterAttributeTable::~GDALDefaultRasterAttributeTable() + +{ +} + +/************************************************************************/ +/* GDALDestroyRasterAttributeTable() */ +/************************************************************************/ + +/** + * \brief Destroys a RAT. + * + * This function is the same as the C++ method GDALRasterAttributeTable::~GDALRasterAttributeTable() + */ +void CPL_STDCALL +GDALDestroyRasterAttributeTable( GDALRasterAttributeTableH hRAT ) + +{ + if( hRAT != NULL ) + delete static_cast(hRAT); +} + +/************************************************************************/ +/* AnalyseColumns() */ +/* */ +/* Internal method to work out which column to use for various */ +/* tasks. */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::AnalyseColumns() + +{ + bColumnsAnalysed = TRUE; + + nMinCol = GetColOfUsage( GFU_Min ); + if( nMinCol == -1 ) + nMinCol = GetColOfUsage( GFU_MinMax ); + + nMaxCol = GetColOfUsage( GFU_Max ); + if( nMaxCol == -1 ) + nMaxCol = GetColOfUsage( GFU_MinMax ); +} + +/************************************************************************/ +/* GetColumnCount() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetColumnCount() const + +{ + return aoFields.size(); +} + +/************************************************************************/ +/* GDALRATGetColumnCount() */ +/************************************************************************/ + +/** + * \brief Fetch table column count. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetColumnCount() + */ +int CPL_STDCALL GDALRATGetColumnCount( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetColumnCount", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetColumnCount(); +} + +/************************************************************************/ +/* GetNameOfCol() */ +/************************************************************************/ + +const char *GDALDefaultRasterAttributeTable::GetNameOfCol( int iCol ) const + +{ + if( iCol < 0 || iCol >= (int) aoFields.size() ) + return ""; + + else + return aoFields[iCol].sName; +} + +/************************************************************************/ +/* GDALRATGetNameOfCol() */ +/************************************************************************/ + +/** + * \brief Fetch name of indicated column. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetNameOfCol() + */ +const char *CPL_STDCALL GDALRATGetNameOfCol( GDALRasterAttributeTableH hRAT, + int iCol ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetNameOfCol", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->GetNameOfCol( iCol ); +} + +/************************************************************************/ +/* GetUsageOfCol() */ +/************************************************************************/ + +GDALRATFieldUsage GDALDefaultRasterAttributeTable::GetUsageOfCol( int iCol ) const + +{ + if( iCol < 0 || iCol >= (int) aoFields.size() ) + return GFU_Generic; + + else + return aoFields[iCol].eUsage; +} + +/************************************************************************/ +/* GDALRATGetUsageOfCol() */ +/************************************************************************/ + +/** + * \brief Fetch column usage value. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetUsageOfColetNameOfCol() + */ +GDALRATFieldUsage CPL_STDCALL +GDALRATGetUsageOfCol( GDALRasterAttributeTableH hRAT, int iCol ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetUsageOfCol", GFU_Generic ); + + return ((GDALRasterAttributeTable *) hRAT)->GetUsageOfCol( iCol ); +} + +/************************************************************************/ +/* GetTypeOfCol() */ +/************************************************************************/ + +GDALRATFieldType GDALDefaultRasterAttributeTable::GetTypeOfCol( int iCol ) const + +{ + if( iCol < 0 || iCol >= (int) aoFields.size() ) + return GFT_Integer; + + else + return aoFields[iCol].eType; +} + +/************************************************************************/ +/* GDALRATGetTypeOfCol() */ +/************************************************************************/ + +/** + * \brief Fetch column type. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetTypeOfCol() + */ +GDALRATFieldType CPL_STDCALL +GDALRATGetTypeOfCol( GDALRasterAttributeTableH hRAT, int iCol ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetTypeOfCol", GFT_Integer ); + + return ((GDALRasterAttributeTable *) hRAT)->GetTypeOfCol( iCol ); +} + +/************************************************************************/ +/* GetColOfUsage() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetColOfUsage( GDALRATFieldUsage eUsage ) const + +{ + unsigned int i; + + for( i = 0; i < aoFields.size(); i++ ) + { + if( aoFields[i].eUsage == eUsage ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* GDALRATGetColOfUsage() */ +/************************************************************************/ + +/** + * \brief Fetch column index for given usage. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetColOfUsage() + */ +int CPL_STDCALL +GDALRATGetColOfUsage( GDALRasterAttributeTableH hRAT, + GDALRATFieldUsage eUsage ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetColOfUsage", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetColOfUsage( eUsage ); +} + +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetRowCount() const + +{ + return (int) nRowCount; +} + +/************************************************************************/ +/* GDALRATGetUsageOfCol() */ +/************************************************************************/ +/** + * \brief Fetch row count. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetRowCount() + */ +int CPL_STDCALL +GDALRATGetRowCount( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetRowCount", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetRowCount(); +} + +/************************************************************************/ +/* GetValueAsString() */ +/************************************************************************/ + +const char * +GDALDefaultRasterAttributeTable::GetValueAsString( int iRow, int iField ) const + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return ""; + } + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return ""; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + { + ((GDALDefaultRasterAttributeTable *) this)-> + osWorkingResult.Printf( "%d", aoFields[iField].anValues[iRow] ); + return osWorkingResult; + } + + case GFT_Real: + { + ((GDALDefaultRasterAttributeTable *) this)-> + osWorkingResult.Printf( "%.16g", aoFields[iField].adfValues[iRow]); + return osWorkingResult; + } + + case GFT_String: + { + return aoFields[iField].aosValues[iRow]; + } + } + + return ""; +} + +/************************************************************************/ +/* GDALRATGetValueAsString() */ +/************************************************************************/ +/** + * \brief Fetch field value as a string. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetValueAsString() + */ +const char * CPL_STDCALL +GDALRATGetValueAsString( GDALRasterAttributeTableH hRAT, int iRow, int iField ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetValueAsString", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->GetValueAsString(iRow, iField); +} + +/************************************************************************/ +/* GetValueAsInt() */ +/************************************************************************/ + +int +GDALDefaultRasterAttributeTable::GetValueAsInt( int iRow, int iField ) const + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return 0; + } + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return 0; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + return aoFields[iField].anValues[iRow]; + + case GFT_Real: + return (int) aoFields[iField].adfValues[iRow]; + + case GFT_String: + return atoi( aoFields[iField].aosValues[iRow].c_str() ); + } + + return 0; +} + +/************************************************************************/ +/* GDALRATGetValueAsInt() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a integer. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetValueAsInt() + */ +int CPL_STDCALL +GDALRATGetValueAsInt( GDALRasterAttributeTableH hRAT, int iRow, int iField ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetValueAsInt", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetValueAsInt( iRow, iField ); +} + +/************************************************************************/ +/* GetValueAsDouble() */ +/************************************************************************/ + +double +GDALDefaultRasterAttributeTable::GetValueAsDouble( int iRow, int iField ) const + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return 0; + } + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return 0; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + return aoFields[iField].anValues[iRow]; + + case GFT_Real: + return aoFields[iField].adfValues[iRow]; + + case GFT_String: + return atof( aoFields[iField].aosValues[iRow].c_str() ); + } + + return 0; +} + +/************************************************************************/ +/* GDALRATGetValueAsDouble() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a double. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetValueAsDouble() + */ +double CPL_STDCALL +GDALRATGetValueAsDouble( GDALRasterAttributeTableH hRAT, int iRow, int iField ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetValueAsDouble", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetValueAsDouble(iRow,iField); +} + +/************************************************************************/ +/* SetRowCount() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetRowCount( int nNewCount ) + +{ + if( nNewCount == nRowCount ) + return; + + unsigned int iField; + for( iField = 0; iField < aoFields.size(); iField++ ) + { + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues.resize( nNewCount ); + break; + + case GFT_Real: + aoFields[iField].adfValues.resize( nNewCount ); + break; + + case GFT_String: + aoFields[iField].aosValues.resize( nNewCount ); + break; + } + } + + nRowCount = nNewCount; +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetValue( int iRow, int iField, + const char *pszValue ) + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return; + } + + if( iRow == nRowCount ) + SetRowCount( nRowCount+1 ); + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues[iRow] = atoi(pszValue); + break; + + case GFT_Real: + aoFields[iField].adfValues[iRow] = atof(pszValue); + break; + + case GFT_String: + aoFields[iField].aosValues[iRow] = pszValue; + break; + } +} + +/************************************************************************/ +/* GDALRATSetValueAsString() */ +/************************************************************************/ + +/** + * \brief Set field value from string. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetValue() + */ +void CPL_STDCALL +GDALRATSetValueAsString( GDALRasterAttributeTableH hRAT, int iRow, int iField, + const char *pszValue ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetValueAsString" ); + + ((GDALRasterAttributeTable *) hRAT)->SetValue( iRow, iField, pszValue ); +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetValue( int iRow, int iField, + int nValue ) + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return; + } + + if( iRow == nRowCount ) + SetRowCount( nRowCount+1 ); + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues[iRow] = nValue; + break; + + case GFT_Real: + aoFields[iField].adfValues[iRow] = nValue; + break; + + case GFT_String: + { + char szValue[100]; + + sprintf( szValue, "%d", nValue ); + aoFields[iField].aosValues[iRow] = szValue; + } + break; + } +} + +/************************************************************************/ +/* GDALRATSetValueAsInt() */ +/************************************************************************/ + +/** + * \brief Set field value from integer. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetValue() + */ +void CPL_STDCALL +GDALRATSetValueAsInt( GDALRasterAttributeTableH hRAT, int iRow, int iField, + int nValue ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetValueAsInt" ); + + ((GDALRasterAttributeTable *) hRAT)->SetValue( iRow, iField, nValue); +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetValue( int iRow, int iField, + double dfValue ) + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return; + } + + if( iRow == nRowCount ) + SetRowCount( nRowCount+1 ); + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues[iRow] = (int) dfValue; + break; + + case GFT_Real: + aoFields[iField].adfValues[iRow] = dfValue; + break; + + case GFT_String: + { + char szValue[100]; + + sprintf( szValue, "%.15g", dfValue ); + aoFields[iField].aosValues[iRow] = szValue; + } + break; + } +} + +/************************************************************************/ +/* GDALRATSetValueAsDouble() */ +/************************************************************************/ + +/** + * \brief Set field value from double. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetValue() + */ +void CPL_STDCALL +GDALRATSetValueAsDouble( GDALRasterAttributeTableH hRAT, int iRow, int iField, + double dfValue ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetValueAsDouble" ); + + ((GDALRasterAttributeTable *) hRAT)->SetValue( iRow, iField, dfValue ); +} + +/************************************************************************/ +/* ChangesAreWrittenToFile() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::ChangesAreWrittenToFile() +{ + // GDALRasterBand.SetDefaultRAT needs to be called on instances of + // GDALDefaultRasterAttributeTable since changes are just in-memory + return FALSE; +} + +/************************************************************************/ +/* GDALRATChangesAreWrittenToFile() */ +/************************************************************************/ + +/** + * \brief Determine whether changes made to this RAT are reflected directly in the dataset + * + * This function is the same as the C++ method GDALRasterAttributeTable::ChangesAreWrittenToFile() + */ +int CPL_STDCALL +GDALRATChangesAreWrittenToFile( GDALRasterAttributeTableH hRAT ) +{ + VALIDATE_POINTER1( hRAT, "GDALRATChangesAreWrittenToFile", FALSE ); + + return ((GDALRasterAttributeTable *) hRAT)->ChangesAreWrittenToFile(); +} + +/************************************************************************/ +/* GetRowOfValue() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetRowOfValue( double dfValue ) const + +{ +/* -------------------------------------------------------------------- */ +/* Handle case of regular binning. */ +/* -------------------------------------------------------------------- */ + if( bLinearBinning ) + { + int iBin = (int) floor((dfValue - dfRow0Min) / dfBinSize); + if( iBin < 0 || iBin >= nRowCount ) + return -1; + else + return iBin; + } + +/* -------------------------------------------------------------------- */ +/* Do we have any information? */ +/* -------------------------------------------------------------------- */ + const GDALRasterAttributeField *poMin, *poMax; + + if( !bColumnsAnalysed ) + ((GDALDefaultRasterAttributeTable *) this)->AnalyseColumns(); + + if( nMinCol == -1 && nMaxCol == -1 ) + return -1; + + if( nMinCol != -1 ) + poMin = &(aoFields[nMinCol]); + else + poMin = NULL; + + if( nMaxCol != -1 ) + poMax = &(aoFields[nMaxCol]); + else + poMax = NULL; + +/* -------------------------------------------------------------------- */ +/* Search through rows for match. */ +/* -------------------------------------------------------------------- */ + int iRow; + + for( iRow = 0; iRow < nRowCount; iRow++ ) + { + if( poMin != NULL ) + { + if( poMin->eType == GFT_Integer ) + { + while( iRow < nRowCount && dfValue < poMin->anValues[iRow] ) + iRow++; + } + else if( poMin->eType == GFT_Real ) + { + while( iRow < nRowCount && dfValue < poMin->adfValues[iRow] ) + iRow++; + } + + if( iRow == nRowCount ) + break; + } + + if( poMax != NULL ) + { + if( (poMax->eType == GFT_Integer + && dfValue > poMax->anValues[iRow] ) + || (poMax->eType == GFT_Real + && dfValue > poMax->adfValues[iRow] ) ) + continue; + } + + return iRow; + } + + return -1; +} + +/************************************************************************/ +/* GetRowOfValue() */ +/* */ +/* Int arg for now just converted to double. Perhaps we will */ +/* handle this in a special way some day? */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetRowOfValue( int nValue ) const + +{ + return GetRowOfValue( (double) nValue ); +} + +/************************************************************************/ +/* SetLinearBinning() */ +/************************************************************************/ + +CPLErr GDALDefaultRasterAttributeTable::SetLinearBinning( double dfRow0MinIn, + double dfBinSizeIn ) + +{ + bLinearBinning = TRUE; + dfRow0Min = dfRow0MinIn; + dfBinSize = dfBinSizeIn; + + return CE_None; +} + +/************************************************************************/ +/* GetLinearBinning() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetLinearBinning( double *pdfRow0Min, + double *pdfBinSize ) const + +{ + if( !bLinearBinning ) + return FALSE; + + *pdfRow0Min = dfRow0Min; + *pdfBinSize = dfBinSize; + + return TRUE; +} + +/************************************************************************/ +/* CreateColumn() */ +/************************************************************************/ + +CPLErr GDALDefaultRasterAttributeTable::CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) + +{ + int iNewField = aoFields.size(); + + aoFields.resize( iNewField+1 ); + + aoFields[iNewField].sName = pszFieldName; + + // color columns should be int 0..255 + if( ( eFieldUsage == GFU_Red ) || ( eFieldUsage == GFU_Green ) || + ( eFieldUsage == GFU_Blue ) || ( eFieldUsage == GFU_Alpha ) ) + { + eFieldType = GFT_Integer; + } + aoFields[iNewField].eType = eFieldType; + aoFields[iNewField].eUsage = eFieldUsage; + + if( eFieldType == GFT_Integer ) + aoFields[iNewField].anValues.resize( nRowCount ); + else if( eFieldType == GFT_Real ) + aoFields[iNewField].adfValues.resize( nRowCount ); + else if( eFieldType == GFT_String ) + aoFields[iNewField].aosValues.resize( nRowCount ); + + return CE_None; +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +GDALDefaultRasterAttributeTable *GDALDefaultRasterAttributeTable::Clone() const + +{ + return new GDALDefaultRasterAttributeTable( *this ); +} + +/************************************************************************/ +/* GDALRATClone() */ +/************************************************************************/ + +/** + * \brief Copy Raster Attribute Table + * + * This function is the same as the C++ method GDALRasterAttributeTable::Clone() + */ +GDALRasterAttributeTableH CPL_STDCALL +GDALRATClone( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATClone", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->Clone(); +} diff --git a/ogr/gdal_rat.h b/ogr/gdal_rat.h new file mode 100644 index 0000000..f36939d --- /dev/null +++ b/ogr/gdal_rat.h @@ -0,0 +1,348 @@ +/****************************************************************************** + * $Id: gdal_rat.h 26117 2013-06-29 20:22:34Z rouault $ + * + * Project: GDAL Core + * Purpose: GDALRasterAttributeTable class declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_RAT_H_INCLUDED +#define GDAL_RAT_H_INCLUDED + +#include "cpl_minixml.h" + +// Clone and Serialize are allowed to fail if GetRowCount()*GetColCount() greater +// than this number +#define RAT_MAX_ELEM_FOR_CLONE 1000000 + +/************************************************************************/ +/* GDALRasterAttributeTable */ +/************************************************************************/ + +//! Raster Attribute Table interface. +class GDALDefaultRasterAttributeTable; + +class CPL_DLL GDALRasterAttributeTable +{ +public: + virtual ~GDALRasterAttributeTable(); + /** + * \brief Copy Raster Attribute Table + * + * Creates a new copy of an existing raster attribute table. The new copy + * becomes the responsibility of the caller to destroy. + * May fail (return NULL) if the attribute table is too large to clone + * (GetRowCount() * GetColCount() > RAT_MAX_ELEM_FOR_CLONE) + * + * This method is the same as the C function GDALRATClone(). + * + * @return new copy of the RAT as an in-memory implementation. + */ + virtual GDALDefaultRasterAttributeTable *Clone() const = 0; + + /** + * \brief Fetch table column count. + * + * This method is the same as the C function GDALRATGetColumnCount(). + * + * @return the number of columns. + */ + virtual int GetColumnCount() const = 0; + + /** + * \brief Fetch name of indicated column. + * + * This method is the same as the C function GDALRATGetNameOfCol(). + * + * @param iCol the column index (zero based). + * + * @return the column name or an empty string for invalid column numbers. + */ + virtual const char *GetNameOfCol( int ) const = 0; + + /** + * \brief Fetch column usage value. + * + * This method is the same as the C function GDALRATGetUsageOfCol(). + * + * @param iCol the column index (zero based). + * + * @return the column usage, or GFU_Generic for improper column numbers. + */ + virtual GDALRATFieldUsage GetUsageOfCol( int ) const = 0; + + /** + * \brief Fetch column type. + * + * This method is the same as the C function GDALRATGetTypeOfCol(). + * + * @param iCol the column index (zero based). + * + * @return column type or GFT_Integer if the column index is illegal. + */ + virtual GDALRATFieldType GetTypeOfCol( int ) const = 0; + + /** + * \brief Fetch column index for given usage. + * + * Returns the index of the first column of the requested usage type, or -1 + * if no match is found. + * + * This method is the same as the C function GDALRATGetUsageOfCol(). + * + * @param eUsage usage type to search for. + * + * @return column index, or -1 on failure. + */ + virtual int GetColOfUsage( GDALRATFieldUsage ) const = 0; + + /** + * \brief Fetch row count. + * + * This method is the same as the C function GDALRATGetRowCount(). + * + * @return the number of rows. + */ + virtual int GetRowCount() const = 0; + + /** + * \brief Fetch field value as a string. + * + * The value of the requested column in the requested row is returned + * as a string. If the field is numeric, it is formatted as a string + * using default rules, so some precision may be lost. + * + * The returned string is temporary and cannot be expected to be + * available after the next GDAL call. + * + * This method is the same as the C function GDALRATGetValueAsString(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * + * @return field value. + */ + virtual const char *GetValueAsString( int iRow, int iField ) const = 0; + + /** + * \brief Fetch field value as a integer. + * + * The value of the requested column in the requested row is returned + * as an integer. Non-integer fields will be converted to integer with + * the possibility of data loss. + * + * This method is the same as the C function GDALRATGetValueAsInt(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * + * @return field value + */ + virtual int GetValueAsInt( int iRow, int iField ) const = 0; + + /** + * \brief Fetch field value as a double. + * + * The value of the requested column in the requested row is returned + * as a double. Non double fields will be converted to double with + * the possibility of data loss. + * + * This method is the same as the C function GDALRATGetValueAsDouble(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * + * @return field value + */ + virtual double GetValueAsDouble( int iRow, int iField ) const = 0; + + /** + * \brief Set field value from string. + * + * The indicated field (column) on the indicated row is set from the + * passed value. The value will be automatically converted for other field + * types, with a possible loss of precision. + * + * This method is the same as the C function GDALRATSetValueAsString(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * @param pszValue the value to assign. + */ + virtual void SetValue( int iRow, int iField, const char *pszValue ) = 0; + + /** + * \brief Set field value from integer. + * + * The indicated field (column) on the indicated row is set from the + * passed value. The value will be automatically converted for other field + * types, with a possible loss of precision. + * + * This method is the same as the C function GDALRATSetValueAsInteger(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * @param nValue the value to assign. + */ + virtual void SetValue( int iRow, int iField, int nValue ) = 0; + + /** + * \brief Set field value from double. + * + * The indicated field (column) on the indicated row is set from the + * passed value. The value will be automatically converted for other field + * types, with a possible loss of precision. + * + * This method is the same as the C function GDALRATSetValueAsDouble(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * @param dfValue the value to assign. + */ + virtual void SetValue( int iRow, int iField, double dfValue) = 0; + + /** + * \brief Determine whether changes made to this RAT are reflected directly in the dataset + * + * If this returns FALSE then GDALRasterBand.SetDefaultRAT() should be called. Otherwise + * this is unnecessary since changes to this object are reflected in the dataset. + * + * This method is the same as the C function GDALRATChangesAreWrittenToFile(). + * + */ + virtual int ChangesAreWrittenToFile() = 0; + + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList); + + virtual void SetRowCount( int iCount ); + virtual int GetRowOfValue( double dfValue ) const; + virtual int GetRowOfValue( int nValue ) const; + + virtual CPLErr CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ); + virtual CPLErr SetLinearBinning( double dfRow0Min, double dfBinSize ); + virtual int GetLinearBinning( double *pdfRow0Min, double *pdfBinSize ) const; + + /** + * \brief Serialize + * + * May fail (return NULL) if the attribute table is too large to serialize + * (GetRowCount() * GetColCount() > RAT_MAX_ELEM_FOR_CLONE) + */ + virtual CPLXMLNode *Serialize() const; + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + virtual CPLErr InitializeFromColorTable( const GDALColorTable * ); + virtual GDALColorTable *TranslateToColorTable( int nEntryCount = -1 ); + + virtual void DumpReadable( FILE * = NULL ); +}; + +/************************************************************************/ +/* GDALRasterAttributeField */ +/* */ +/* (private) */ +/************************************************************************/ + +class GDALRasterAttributeField +{ +public: + CPLString sName; + + GDALRATFieldType eType; + + GDALRATFieldUsage eUsage; + + std::vector anValues; + std::vector adfValues; + std::vector aosValues; +}; + +/************************************************************************/ +/* GDALDefaultRasterAttributeTable */ +/************************************************************************/ + +//! Raster Attribute Table container. + +class CPL_DLL GDALDefaultRasterAttributeTable : public GDALRasterAttributeTable +{ +private: + std::vector aoFields; + + int bLinearBinning; + double dfRow0Min; + double dfBinSize; + + void AnalyseColumns(); + int bColumnsAnalysed; + int nMinCol; + int nMaxCol; + + int nRowCount; + + CPLString osWorkingResult; + +public: + GDALDefaultRasterAttributeTable(); + GDALDefaultRasterAttributeTable(const GDALDefaultRasterAttributeTable&); + ~GDALDefaultRasterAttributeTable(); + + GDALDefaultRasterAttributeTable *Clone() const; + + virtual int GetColumnCount() const; + + virtual const char *GetNameOfCol( int ) const; + virtual GDALRATFieldUsage GetUsageOfCol( int ) const; + virtual GDALRATFieldType GetTypeOfCol( int ) const; + + virtual int GetColOfUsage( GDALRATFieldUsage ) const; + + virtual int GetRowCount() const; + + virtual const char *GetValueAsString( int iRow, int iField ) const; + virtual int GetValueAsInt( int iRow, int iField ) const; + virtual double GetValueAsDouble( int iRow, int iField ) const; + + virtual void SetValue( int iRow, int iField, const char *pszValue ); + virtual void SetValue( int iRow, int iField, double dfValue); + virtual void SetValue( int iRow, int iField, int nValue ); + + virtual int ChangesAreWrittenToFile(); + virtual void SetRowCount( int iCount ); + + virtual int GetRowOfValue( double dfValue ) const; + virtual int GetRowOfValue( int nValue ) const; + + virtual CPLErr CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ); + virtual CPLErr SetLinearBinning( double dfRow0Min, double dfBinSize ); + virtual int GetLinearBinning( double *pdfRow0Min, double *pdfBinSize ) const; + +}; + +#endif /* ndef GDAL_RAT_H_INCLUDED */ diff --git a/ogr/gdal_version.h b/ogr/gdal_version.h new file mode 100644 index 0000000..efa1dba --- /dev/null +++ b/ogr/gdal_version.h @@ -0,0 +1,29 @@ + +/* -------------------------------------------------------------------- */ +/* GDAL Version Information. */ +/* -------------------------------------------------------------------- */ + +#ifndef GDAL_VERSION_MAJOR +# define GDAL_VERSION_MAJOR 2 +# define GDAL_VERSION_MINOR 0 +# define GDAL_VERSION_REV 0 +# define GDAL_VERSION_BUILD 0 +#endif + +/* GDAL_COMPUTE_VERSION macro introduced in GDAL 1.10 */ +/* Must be used ONLY to compare with version numbers for GDAL >= 1.10 */ +#ifndef GDAL_COMPUTE_VERSION +#define GDAL_COMPUTE_VERSION(maj,min,rev) ((maj)*1000000+(min)*10000+(rev)*100) +#endif + +/* Note: the formula to compute GDAL_VERSION_NUM has changed in GDAL 1.10 */ +#ifndef GDAL_VERSION_NUM +# define GDAL_VERSION_NUM (GDAL_COMPUTE_VERSION(GDAL_VERSION_MAJOR,GDAL_VERSION_MINOR,GDAL_VERSION_REV)+GDAL_VERSION_BUILD) +#endif + +#ifndef GDAL_RELEASE_DATE +# define GDAL_RELEASE_DATE 20140416 +#endif +#ifndef GDAL_RELEASE_NAME +# define GDAL_RELEASE_NAME "2.0.0dev" +#endif diff --git a/ogr/gdalallvalidmaskband.cpp b/ogr/gdalallvalidmaskband.cpp new file mode 100644 index 0000000..b340d28 --- /dev/null +++ b/ogr/gdalallvalidmaskband.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * $Id: gdalallvalidmaskband.cpp 15044 2008-07-26 12:04:05Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALAllValidMaskBand, a class implementing all + * a default 'all valid' band mask. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalallvalidmaskband.cpp 15044 2008-07-26 12:04:05Z rouault $"); + +/************************************************************************/ +/* GDALAllValidMaskBand() */ +/************************************************************************/ + +GDALAllValidMaskBand::GDALAllValidMaskBand( GDALRasterBand *poParent ) + +{ + poDS = NULL; + nBand = 0; + + nRasterXSize = poParent->GetXSize(); + nRasterYSize = poParent->GetYSize(); + + eDataType = GDT_Byte; + poParent->GetBlockSize( &nBlockXSize, &nBlockYSize ); +} + +/************************************************************************/ +/* ~GDALAllValidMaskBand() */ +/************************************************************************/ + +GDALAllValidMaskBand::~GDALAllValidMaskBand() + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALAllValidMaskBand::IReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ + memset( pImage, 255, nBlockXSize * nBlockYSize ); + + return CE_None; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GDALAllValidMaskBand::GetMaskBand() + +{ + return this; +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GDALAllValidMaskBand::GetMaskFlags() + +{ + return GMF_ALL_VALID; +} diff --git a/ogr/gdalclientserver.cpp b/ogr/gdalclientserver.cpp new file mode 100644 index 0000000..51296e3 --- /dev/null +++ b/ogr/gdalclientserver.cpp @@ -0,0 +1,6052 @@ +/****************************************************************************** + * $Id: gdalclientserver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GDAL Core + * Purpose: GDAL Client/server dataset mechanism. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" + +#ifdef WIN32 + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + #define _WIN32_WINNT 0x0501 + #include + #include + typedef SOCKET CPL_SOCKET; + #ifndef HAVE_GETADDRINFO + #define HAVE_GETADDRINFO 1 + #endif +#else + #include + #include + #include + #include + #include + #include + typedef int CPL_SOCKET; + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #define SOCKADDR struct sockaddr + #define WSAGetLastError() errno + #define WSACleanup() + #define closesocket(s) close(s) +#endif + +#include "gdal_pam.h" +#include "gdal_rat.h" +#include "cpl_spawn.h" +#include "cpl_multiproc.h" + +/*! +\page gdal_api_proxy GDAL API Proxy + +\section gdal_api_proxy_intro Introduction + +(GDAL >= 1.10.0) + +When dealing with some file formats, particularly the drivers relying on third-party +(potentially closed-source) libraries, it is difficult to ensure that those third-party +libraries will be robust to hostile/corrupted datasource. + +The implemented solution is to have a (private) API_PROXY driver that will expose a GDALClientDataset +object, which will forward all the GDAL API calls to another process ("server"), where the real driver +will be effectively run. This way, if the server aborts due to a fatal error, the calling process will +be unaffected and will report a clean error instead of aborting itself. + +\section gdal_api_proxy_enabling How to enable ? + +The API_PROXY mechanism can be enabled by setting the GDAL_API_PROXY config option to YES. +The option can also be set to a list of file extensions that must be the only ones to trigger +this mechanism (e.g. GDAL_API_PROXY=ecw,sid). + +When enabled, datasets can be handled with GDALOpen(), GDALCreate() or GDALCreateCopy() with +their nominal filename (or connexion string). + +Alternatively, the API_PROXY mechanism can be used selectively on a datasource by prefixing its +name with API_PROXY:, for example GDALOpen("API_PROXY:foo.tif", GA_ReadOnly). + +\section gdal_api_proxy_options Advanced options + +For now, the server launched is the gdalserver executable on Windows. On Unix, the default behaviour is +to just fork() the current process. It is also possible to launch the gdalserver executable +by forcing GDAL_API_PROXY_SERVER=YES. +The full filename of the gdalserver executable can also be specified in the GDAL_API_PROXY_SERVER. + +It is also possible to connect to a gdalserver in TCP, possibly on a remote host. In that case, +gdalserver must be launched on a host with "gdalserver -tcpserver the_tcp_port". And the client +must set GDAL_API_PROXY_SERVER="hostname:the_tcp_port", where hostname is a string or a IP address. + +In case of many dataset opening or creation, to avoid the cost of repeated process forking, +a pool of unused connections is established. Each time a dataset is closed, the associated connection +is pushed in the pool (if there's an empty bucket). When a following dataset is to be opened, one of those +connections will be reused. This behaviour is controlled with the GDAL_API_PROXY_CONN_POOL config option +that is set to YES by default, and will keep a maximum of 4 unused connections. +GDAL_API_PROXY_CONN_POOL can be set to a integer value to specify the maximum number of unused connections. + +\section gdal_api_proxy_limitations Limitations + +Datasets stored in the memory virtual file system (/vsimem) or handled by the MEM driver are excluded from +the API Proxy mechanism. + +Additionnaly, for GDALCreate() or GDALCreateCopy(), the VRT driver is also excluded from that mechanism. + +Currently, the client dataset returned is not protected by a mutex, so it is unsafe to use it concurrently +from multiple threads. However, it is safe to use several client datasets from multiple threads. + +*/ + +/* REMINDER: upgrade this number when the on-wire protocol changes */ +/* Note: please at least keep the version exchange protocol unchanged ! */ +#define GDAL_CLIENT_SERVER_PROTOCOL_MAJOR 1 +#define GDAL_CLIENT_SERVER_PROTOCOL_MINOR 0 + +#include +#include + +CPL_C_START +int CPL_DLL GDALServerLoop(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout); +const char* GDALClientDatasetGetFilename(const char* pszFilename); +int CPL_DLL GDALServerLoopSocket(CPL_SOCKET nSocket); +CPL_C_END + +#define BUFFER_SIZE 1024 +typedef struct +{ + CPL_FILE_HANDLE fin; + CPL_FILE_HANDLE fout; + CPL_SOCKET nSocket; + int bOK; + GByte abyBuffer[BUFFER_SIZE]; + int nBufferSize; +} GDALPipe; + +typedef struct +{ + CPLSpawnedProcess *sp; + GDALPipe *p; +} GDALServerSpawnedProcess; + +typedef struct +{ + int bUpdated; + double dfComplete; + char *pszProgressMsg; + int bRet; + void *hMutex; +} GDALServerAsyncProgress; + +typedef enum +{ + INSTR_INVALID = 0, + INSTR_GetGDALVersion = 1, /* do not change this ! */ + INSTR_EXIT, + INSTR_EXIT_FAIL, + INSTR_SetConfigOption, + INSTR_Progress, + INSTR_Reset, + INSTR_Open, + INSTR_Identify, + INSTR_Create, + INSTR_CreateCopy, + INSTR_QuietDelete, + INSTR_AddBand, + INSTR_GetGeoTransform, + INSTR_SetGeoTransform, + INSTR_GetProjectionRef, + INSTR_SetProjection, + INSTR_GetGCPCount, + INSTR_GetGCPProjection, + INSTR_GetGCPs, + INSTR_SetGCPs, + INSTR_GetFileList, + INSTR_FlushCache, + INSTR_SetDescription, + INSTR_GetMetadata, + INSTR_GetMetadataItem, + INSTR_SetMetadata, + INSTR_SetMetadataItem, + INSTR_IRasterIO_Read, + INSTR_IRasterIO_Write, + INSTR_IBuildOverviews, + INSTR_AdviseRead, + INSTR_CreateMaskBand, + INSTR_Band_First, + INSTR_Band_FlushCache, + INSTR_Band_GetCategoryNames, + INSTR_Band_SetCategoryNames, + INSTR_Band_SetDescription, + INSTR_Band_GetMetadata, + INSTR_Band_GetMetadataItem, + INSTR_Band_SetMetadata, + INSTR_Band_SetMetadataItem, + INSTR_Band_GetColorInterpretation, + INSTR_Band_SetColorInterpretation, + INSTR_Band_GetNoDataValue, + INSTR_Band_GetMinimum, + INSTR_Band_GetMaximum, + INSTR_Band_GetOffset, + INSTR_Band_GetScale, + INSTR_Band_SetNoDataValue, + INSTR_Band_SetOffset, + INSTR_Band_SetScale, + INSTR_Band_IReadBlock, + INSTR_Band_IWriteBlock, + INSTR_Band_IRasterIO_Read, + INSTR_Band_IRasterIO_Write, + INSTR_Band_GetStatistics, + INSTR_Band_ComputeStatistics, + INSTR_Band_SetStatistics, + INSTR_Band_ComputeRasterMinMax, + INSTR_Band_GetHistogram, + INSTR_Band_GetDefaultHistogram, + INSTR_Band_SetDefaultHistogram, + INSTR_Band_HasArbitraryOverviews, + INSTR_Band_GetOverviewCount, + INSTR_Band_GetOverview, + INSTR_Band_GetMaskBand, + INSTR_Band_GetMaskFlags, + INSTR_Band_CreateMaskBand, + INSTR_Band_Fill, + INSTR_Band_GetColorTable, + INSTR_Band_SetColorTable, + INSTR_Band_GetUnitType, + INSTR_Band_SetUnitType, + INSTR_Band_BuildOverviews, + INSTR_Band_GetDefaultRAT, + INSTR_Band_SetDefaultRAT, + INSTR_Band_AdviseRead, + INSTR_Band_End, + INSTR_END +} InstrEnum; + +#ifdef DEBUG +static const char* apszInstr[] = +{ + "INVALID", + "GetGDALVersion", + "EXIT", + "FAIL", + "SetConfigOption", + "Progress", + "Reset", + "Open", + "Identify", + "Create", + "CreateCopy", + "QuietDelete", + "AddBand", + "GetGeoTransform", + "SetGeoTransform", + "GetProjectionRef", + "SetProjection", + "GetGCPCount", + "GetGCPProjection", + "GetGCPs", + "SetGCPs", + "GetFileList", + "FlushCache", + "SetDescription", + "GetMetadata", + "GetMetadataItem", + "SetMetadata", + "SetMetadataItem", + "IRasterIO_Read", + "IRasterIO_Write", + "IBuildOverviews", + "AdviseRead", + "CreateMaskBand", + "Band_First", + "Band_FlushCache", + "Band_GetCategoryNames", + "Band_SetCategoryNames", + "Band_SetDescription", + "Band_GetMetadata", + "Band_GetMetadataItem", + "Band_SetMetadata", + "Band_SetMetadataItem", + "Band_GetColorInterpretation", + "Band_SetColorInterpretation", + "Band_GetNoDataValue", + "Band_GetMinimum", + "Band_GetMaximum", + "Band_GetOffset", + "Band_GetScale", + "Band_SetNoDataValue", + "Band_SetOffset", + "Band_SetScale", + "Band_IReadBlock", + "Band_IWriteBlock", + "Band_IRasterIO_Read", + "Band_IRasterIO_Write", + "Band_GetStatistics", + "Band_ComputeStatistics", + "Band_SetStatistics", + "Band_ComputeRasterMinMax", + "Band_GetHistogram", + "Band_GetDefaultHistogram", + "Band_SetDefaultHistogram", + "Band_HasArbitraryOverviews", + "Band_GetOverviewCount", + "Band_GetOverview", + "Band_GetMaskBand", + "Band_GetMaskFlags", + "Band_CreateMaskBand", + "Band_Fill", + "Band_GetColorTable", + "Band_SetColorTable", + "Band_GetUnitType", + "Band_SetUnitType", + "Band_BuildOverviews", + "Band_GetDefaultRAT", + "Band_SetDefaultRAT", + "Band_AdviseRead", + "Band_End", + "END", +}; +#endif + +static const GByte abyEndOfJunkMarker[] = { 0xDE, 0xAD, 0xBE, 0xEF }; + +/* Recycling of connexions to child processes */ +#define MAX_RECYCLED 128 +#define DEFAULT_RECYCLED 4 +static int bRecycleChild = FALSE; +static int nMaxRecycled = 0; +static GDALServerSpawnedProcess* aspRecycled[MAX_RECYCLED]; + +/************************************************************************/ +/* EnterObject */ +/************************************************************************/ + +#ifdef DEBUG_VERBOSE +class EnterObject +{ + const char* pszFunction; + + public: + EnterObject(const char* pszFunction) : pszFunction(pszFunction) + { + CPLDebug("GDAL", "Enter %s", pszFunction); + } + + ~EnterObject() + { + CPLDebug("GDAL", "Leave %s", pszFunction); + } +}; + +#define CLIENT_ENTER() EnterObject o(__FUNCTION__) +#else +#define CLIENT_ENTER() while(0) +#endif + +/************************************************************************/ +/* MyChdir() */ +/************************************************************************/ + +static void MyChdir(const char* pszCWD) +{ +#ifdef WIN32 + SetCurrentDirectory(pszCWD); +#else + CPLAssert(chdir(pszCWD) == 0); +#endif +} + +/************************************************************************/ +/* MyChdirRootDirectory() */ +/************************************************************************/ + +static void MyChdirRootDirectory() +{ +#ifdef WIN32 + SetCurrentDirectory("C:\\"); +#else + CPLAssert(chdir("/") == 0); +#endif +} + +/************************************************************************/ +/* GDALClientDataset */ +/************************************************************************/ + +class GDALClientDataset: public GDALPamDataset +{ + GDALServerSpawnedProcess *ssp; + GDALPipe *p; + CPLString osProjection; + CPLString osGCPProjection; + int bFreeDriver; + int nGCPCount; + GDAL_GCP *pasGCPs; + std::map aoMapMetadata; + std::map< std::pair, char*> aoMapMetadataItem; + GDALServerAsyncProgress *async; + GByte abyCaps[16]; /* 16 * 8 = 128 > INSTR_END */ + + int mCreateCopy(const char* pszFilename, + GDALDataset* poSrcDS, + int bStrict, char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData); + int mCreate( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + + GDALClientDataset(GDALServerSpawnedProcess* ssp); + + static GDALClientDataset* CreateAndConnect(); + + protected: + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace); + public: + GDALClientDataset(GDALPipe* p); + ~GDALClientDataset(); + + int Init(const char* pszFilename, GDALAccess eAccess); + + void AttachAsyncProgress(GDALServerAsyncProgress* async) { this->async = async; } + int ProcessAsyncProgress(); + int SupportsInstr(InstrEnum instr) const { return abyCaps[instr / 8] & (1 << (instr % 8)); } + + virtual void FlushCache(); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + + //virtual void SetDescription( const char * ); + + virtual const char* GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual const char* GetProjectionRef(); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual char **GetFileList(void); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + virtual CPLErr CreateMaskBand( int nFlags ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + static GDALDataset* Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + static CPLErr Delete( const char * pszName ); +}; + +/************************************************************************/ +/* GDALClientRasterBand */ +/************************************************************************/ + +class GDALClientRasterBand : public GDALPamRasterBand +{ + friend class GDALClientDataset; + + GDALPipe *p; + int iSrvBand; + std::map aMapOvrBands; + std::map aMapOvrBandsCurrent; + GDALRasterBand *poMaskBand; + std::map aoMapMetadata; + std::map< std::pair, char*> aoMapMetadataItem; + char **papszCategoryNames; + GDALColorTable *poColorTable; + char *pszUnitType; + GDALRasterAttributeTable *poRAT; + std::vector apoOldMaskBands; + GByte abyCaps[16]; /* 16 * 8 = 128 > INSTR_END */ + + int WriteInstr(InstrEnum instr); + + double GetDouble( InstrEnum instr, int *pbSuccess ); + CPLErr SetDouble( InstrEnum instr, double dfVal ); + + GDALRasterBand *CreateFakeMaskBand(); + + int bEnableLineCaching; + int nSuccessiveLinesRead; + GDALDataType eLastBufType; + int nLastYOff; + GByte *pabyCachedLines; + GDALDataType eCachedBufType; + int nCachedYStart; + int nCachedLines; + + void InvalidateCachedLines(); + CPLErr IRasterIO_read_internal( + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ); + protected: + + virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void* pImage); + virtual CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff, void* pImage); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ); + + public: + GDALClientRasterBand(GDALPipe* p, int iSrvBand, + GDALClientDataset* poDS, int nBand, GDALAccess eAccess, + int nRasterXSize, int nRasterYSize, + GDALDataType eDataType, int nBlockXSize, int nBlockYSize, + GByte abyCaps[16]); + ~GDALClientRasterBand(); + + int GetSrvBand() const { return iSrvBand; } + int SupportsInstr(InstrEnum instr) const { return abyCaps[instr / 8] & (1 << (instr % 8)); } + + void ClearOverviewCache() { aMapOvrBandsCurrent.clear(); } + + virtual CPLErr FlushCache(); + + virtual void SetDescription( const char * ); + + virtual const char* GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual char **GetCategoryNames(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual double GetScale( int *pbSuccess = NULL ); + + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable * ); + + virtual const char *GetUnitType(); + virtual CPLErr SetUnitType( const char * ); + + virtual CPLErr Fill(double dfRealValue, double dfImaginaryValue = 0); + + virtual CPLErr SetCategoryNames( char ** ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr SetOffset( double ); + virtual CPLErr SetScale( double ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc, void *pProgressData ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr ComputeRasterMinMax( int, double* ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, int ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram ); + + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + /* + virtual GDALRasterBand *GetRasterSampleOverview( int ); + */ + +}; + +/************************************************************************/ +/* GDALPipeBuild() */ +/************************************************************************/ + +static GDALPipe* GDALPipeBuild(CPLSpawnedProcess* sp) +{ + GDALPipe* p = (GDALPipe*)CPLMalloc(sizeof(GDALPipe)); + p->bOK = TRUE; + p->fin = CPLSpawnAsyncGetInputFileHandle(sp); + p->fout = CPLSpawnAsyncGetOutputFileHandle(sp); + p->nSocket = INVALID_SOCKET; + p->nBufferSize = 0; + return p; +} + +static GDALPipe* GDALPipeBuild(CPL_SOCKET nSocket) +{ + GDALPipe* p = (GDALPipe*)CPLMalloc(sizeof(GDALPipe)); + p->bOK = TRUE; + p->fin = CPL_FILE_INVALID_HANDLE; + p->fout = CPL_FILE_INVALID_HANDLE; + p->nSocket = nSocket; + p->nBufferSize = 0; + return p; +} + +static GDALPipe* GDALPipeBuild(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout) +{ + GDALPipe* p = (GDALPipe*)CPLMalloc(sizeof(GDALPipe)); + p->bOK = TRUE; + p->fin = fin; + p->fout = fout; + p->nSocket = INVALID_SOCKET; + p->nBufferSize = 0; + return p; +} + +/************************************************************************/ +/* GDALPipeWrite_internal() */ +/************************************************************************/ + +static int GDALPipeWrite_internal(GDALPipe* p, const void* data, int length) +{ + if(!p->bOK) + return FALSE; + if( p->fout != CPL_FILE_INVALID_HANDLE ) + { + int nRet = CPLPipeWrite(p->fout, data, length); + if( !nRet ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Write to pipe failed"); + p->bOK = FALSE; + } + return nRet; + } + else + { + const char* pabyData = (const char*) data; + int nRemain = length; + while( nRemain > 0 ) + { + int nRet = send(p->nSocket, pabyData, nRemain, 0); + if( nRet < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Write to socket failed"); + p->bOK = FALSE; + return FALSE; + } + pabyData += nRet; + nRemain -= nRet; + } + return TRUE; + } +} + +/************************************************************************/ +/* GDALPipeFlushBuffer() */ +/************************************************************************/ + +static int GDALPipeFlushBuffer(GDALPipe * p) +{ + if( p->nBufferSize == 0 ) + return TRUE; + if( GDALPipeWrite_internal(p, p->abyBuffer, p->nBufferSize) ) + { + p->nBufferSize = 0; + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALPipeFree() */ +/************************************************************************/ + +static void GDALPipeFree(GDALPipe * p) +{ + GDALPipeFlushBuffer(p); + if( p->nSocket != INVALID_SOCKET ) + { + closesocket(p->nSocket); + WSACleanup(); + } + CPLFree(p); +} + +/************************************************************************/ +/* GDALPipeRead() */ +/************************************************************************/ + +static int GDALPipeRead(GDALPipe* p, void* data, int length) +{ + if(!p->bOK) + return FALSE; + if(!GDALPipeFlushBuffer(p)) + return FALSE; + + if( p->fout != CPL_FILE_INVALID_HANDLE ) + { + if( CPLPipeRead(p->fin, data, length) ) + return TRUE; + // fprintf(stderr, "[%d] Read from pipe failed\n", (int)getpid()); + CPLError(CE_Failure, CPLE_AppDefined, "Read from pipe failed"); + p->bOK = FALSE; + return FALSE; + } + else + { + char* pabyData = (char*) data; + int nRemain = length; + while( nRemain > 0 ) + { + int nRet = recv(p->nSocket, pabyData, nRemain, 0); + if( nRet <= 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Read from socket failed"); + p->bOK = FALSE; + return FALSE; + } + pabyData += nRet; + nRemain -= nRet; + } + return TRUE; + } + +} + +/************************************************************************/ +/* GDALPipeWrite() */ +/************************************************************************/ + +static int GDALPipeWrite(GDALPipe* p, const void* data, + int length) +{ + //return GDALPipeWrite_internal(p, data, length); + GByte* pCur = (GByte*) data; + int nRemain = length; + while( nRemain > 0 ) + { + if( p->nBufferSize + nRemain <= BUFFER_SIZE ) + { + memcpy(p->abyBuffer + p->nBufferSize, pCur, nRemain); + pCur += nRemain; + p->nBufferSize += nRemain; + nRemain = 0; + } + else if( nRemain > BUFFER_SIZE ) + { + if( !GDALPipeFlushBuffer(p) ) + return FALSE; + if( !GDALPipeWrite_internal(p, pCur, nRemain) ) + return FALSE; + pCur += nRemain; + nRemain = 0; + } + else + { + memcpy(p->abyBuffer + p->nBufferSize, pCur, + BUFFER_SIZE - p->nBufferSize); + pCur += (BUFFER_SIZE - p->nBufferSize); + nRemain -= (BUFFER_SIZE - p->nBufferSize); + p->nBufferSize = BUFFER_SIZE; + if( !GDALPipeFlushBuffer(p) ) + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* GDALPipeRead() */ +/************************************************************************/ + +static int GDALPipeRead(GDALPipe* p, int* pnInt) +{ + return GDALPipeRead(p, pnInt, 4); +} + +static int GDALPipeRead(GDALPipe* p, CPLErr* peErr) +{ + return GDALPipeRead(p, peErr, 4); +} + +static int GDALPipeRead(GDALPipe* p, double* pdfDouble) +{ + return GDALPipeRead(p, pdfDouble, 8); +} + +static int GDALPipeRead_nolength(GDALPipe* p, int nLength, void* pabyData) +{ + return GDALPipeRead(p, pabyData, nLength); +} + +static int GDALPipeRead(GDALPipe* p, int nExpectedLength, void* pabyData) +{ + int nLength; + return GDALPipeRead(p, &nLength) && + nLength == nExpectedLength && + GDALPipeRead_nolength(p, nLength, pabyData); +} + +static int GDALPipeRead(GDALPipe* p, char** ppszStr) +{ + int nLength; + if( !GDALPipeRead(p, &nLength) || nLength < 0 ) + { + *ppszStr = NULL; + return FALSE; + } + if( nLength == 0 ) + { + *ppszStr = NULL; + return TRUE; + } + *ppszStr = (nLength < INT_MAX-1) ? (char*) VSIMalloc(nLength + 1) : NULL; + if( *ppszStr == NULL ) + return FALSE; + if( nLength > 0 && !GDALPipeRead_nolength(p, nLength, *ppszStr) ) + { + CPLFree(*ppszStr); + *ppszStr = NULL; + return FALSE; + } + (*ppszStr)[nLength] = 0; + return TRUE; +} + + +static int GDALPipeRead(GDALPipe* p, char*** ppapszStr) +{ + int nStrCount; + if( !GDALPipeRead(p, &nStrCount) ) + return FALSE; + if( nStrCount < 0 ) + { + *ppapszStr = NULL; + return TRUE; + } + + *ppapszStr = (char**) VSIMalloc2(sizeof(char*), (nStrCount + 1)); + if( *ppapszStr == NULL ) + return FALSE; + for(int i=0;iSetColorEntry(i, &eEntry); + } + } + *ppoColorTable = poColorTable; + return TRUE; +} + +static int GDALPipeRead(GDALPipe* p, GDALRasterAttributeTable** ppoRAT) +{ + *ppoRAT = NULL; + char* pszRAT = NULL; + if( !GDALPipeRead(p, &pszRAT)) + return FALSE; + if( pszRAT == NULL ) + return TRUE; + + CPLXMLNode* poNode = CPLParseXMLString( pszRAT ); + CPLFree(pszRAT); + if( poNode == NULL ) + return FALSE; + + *ppoRAT = new GDALDefaultRasterAttributeTable(); + if( (*ppoRAT)->XMLInit(poNode, NULL) != CE_None ) + { + CPLDestroyXMLNode(poNode); + delete *ppoRAT; + *ppoRAT = NULL; + return FALSE; + } + CPLDestroyXMLNode(poNode); + return TRUE; +} + +static int GDALPipeRead(GDALPipe* p, int* pnGCPCount, GDAL_GCP** ppasGCPs) +{ + *pnGCPCount = 0; + *ppasGCPs = NULL; + int nGCPCount; + if( !GDALPipeRead(p, &nGCPCount) ) + return FALSE; + GDAL_GCP* pasGCPs = (GDAL_GCP* )CPLCalloc(nGCPCount, sizeof(GDAL_GCP)); + for(int i=0;iGDALMajorObject::SetDescription(pszDescription); + CPLFree(pszDescription); + + *ppoBand = poBand; + return TRUE; +} + +/************************************************************************/ +/* GDALSkipUntilEndOfJunkMarker() */ +/************************************************************************/ + +static int GDALSkipUntilEndOfJunkMarker(GDALPipe* p) +{ + if(!p->bOK) + return FALSE; + GByte c; + size_t nIter = 0; + int nStep = 0; + CPLString osJunk; + int nMarkerSize = (int)sizeof(abyEndOfJunkMarker); + GByte abyBuffer[sizeof(abyEndOfJunkMarker)]; + if( !GDALPipeRead_nolength(p, sizeof(abyBuffer), abyBuffer ) ) + return FALSE; + if( memcmp(abyEndOfJunkMarker, abyBuffer, sizeof(abyBuffer)) == 0 ) + return TRUE; + while(TRUE) + { + if( nIter < sizeof(abyBuffer) ) + c = abyBuffer[nIter ++]; + else if( !GDALPipeRead_nolength(p, 1, &c ) ) + return FALSE; + + if( c != 0 ) + osJunk += c; + if( c == abyEndOfJunkMarker[0] ) nStep = 1; + else if( c == abyEndOfJunkMarker[nStep] ) + { + nStep ++; + if( nStep == nMarkerSize ) + { + osJunk.resize(osJunk.size() - nMarkerSize); + if( osJunk.size() ) + CPLDebug("GDAL", "Got junk : %s", osJunk.c_str()); + return TRUE; + } + } + else + nStep = 0; + } +} + +/************************************************************************/ +/* GDALPipeWrite() */ +/************************************************************************/ + +static int GDALPipeWrite(GDALPipe* p, int nInt) +{ + return GDALPipeWrite(p, &nInt, 4); +} + +static int GDALPipeWrite(GDALPipe* p, double dfDouble) +{ + return GDALPipeWrite(p, &dfDouble, 8); +} + +static int GDALPipeWrite_nolength(GDALPipe* p, int nLength, const void* pabyData) +{ + return GDALPipeWrite(p, pabyData, nLength); +} + +static int GDALPipeWrite(GDALPipe* p, int nLength, const void* pabyData) +{ + if( !GDALPipeWrite(p, nLength) || + !GDALPipeWrite_nolength(p, nLength, pabyData) ) + return FALSE; + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, const char* pszStr) +{ + if( pszStr == NULL ) + return GDALPipeWrite(p, 0); + return GDALPipeWrite(p, (int)strlen(pszStr) + 1, pszStr); +} + +static int GDALPipeWrite(GDALPipe* p, char** papszStr) +{ + if( papszStr == NULL ) + return GDALPipeWrite(p, -1); + + int nCount = CSLCount(papszStr); + if( !GDALPipeWrite(p, nCount) ) + return FALSE; + for(int i=0; i < nCount; i++) + { + if( !GDALPipeWrite(p, papszStr[i]) ) + return FALSE; + } + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, + std::vector& aBands, + GDALRasterBand* poBand) +{ + if( poBand == NULL ) + GDALPipeWrite(p, -1); + else + { + GDALPipeWrite(p, (int)aBands.size()); + aBands.push_back(poBand); + GDALPipeWrite(p, poBand->GetBand()); + GDALPipeWrite(p, poBand->GetAccess()); + GDALPipeWrite(p, poBand->GetXSize()); + GDALPipeWrite(p, poBand->GetYSize()); + GDALPipeWrite(p, poBand->GetRasterDataType()); + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + GDALPipeWrite(p, nBlockXSize); + GDALPipeWrite(p, nBlockYSize); + GDALPipeWrite(p, poBand->GetDescription() ); + } + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, GDALColorTable* poColorTable) +{ + if( poColorTable == NULL ) + { + if( !GDALPipeWrite(p, -1) ) + return FALSE; + } + else + { + int nCount = poColorTable->GetColorEntryCount(); + if( !GDALPipeWrite(p, poColorTable->GetPaletteInterpretation()) || + !GDALPipeWrite(p, nCount) ) + return FALSE; + + for(int i=0; i < nCount; i++) + { + const GDALColorEntry* poColorEntry = poColorTable->GetColorEntry(i); + if( !GDALPipeWrite(p, poColorEntry->c1) || + !GDALPipeWrite(p, poColorEntry->c2) || + !GDALPipeWrite(p, poColorEntry->c3) || + !GDALPipeWrite(p, poColorEntry->c4) ) + return FALSE; + } + } + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, const GDALRasterAttributeTable* poRAT) +{ + int bRet; + if( poRAT == NULL ) + bRet = GDALPipeWrite(p, (const char*)NULL); + else + { + CPLXMLNode* poNode = poRAT->Serialize(); + if( poNode != NULL ) + { + char* pszRAT = CPLSerializeXMLTree(poNode); + bRet = GDALPipeWrite(p, pszRAT); + CPLFree(pszRAT); + CPLDestroyXMLNode(poNode); + } + else + bRet = GDALPipeWrite(p, (const char*)NULL); + } + return bRet; +} + +static int GDALPipeWrite(GDALPipe* p, int nGCPCount, const GDAL_GCP* pasGCPs) +{ + if( !GDALPipeWrite(p, nGCPCount ) ) + return FALSE; + for(int i=0;ip->bOK ) + { + /* Store the descriptor in a free slot if available for a */ + /* later reuse */ + CPLMutexHolderD(GDALGetphDMMutex()); + for(int i = 0; i < nMaxRecycled; i ++) + { + if( aspRecycled[i] == NULL ) + { + if( !GDALEmitReset(ssp->p) ) + break; + + aspRecycled[i] = ssp; + return TRUE; + } + } + } + + if(ssp->p->bOK) + { + GDALEmitEXIT(ssp->p); + } + + CPLDebug("GDAL", "Destroy spawned process %p", ssp); + GDALPipeFree(ssp->p); + int nRet = ssp->sp ? CPLSpawnAsyncFinish(ssp->sp, TRUE, TRUE) : 0; + CPLFree(ssp); + return nRet; +} + +/************************************************************************/ +/* GDALCheckServerVersion() */ +/************************************************************************/ + +static int GDALCheckServerVersion(GDALPipe* p) +{ + GDALPipeWrite(p, INSTR_GetGDALVersion); + char bIsLSB = CPL_IS_LSB; + GDALPipeWrite_nolength(p, 1, &bIsLSB); + GDALPipeWrite(p, GDAL_RELEASE_NAME); + GDALPipeWrite(p, GDAL_VERSION_MAJOR); + GDALPipeWrite(p, GDAL_VERSION_MINOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MAJOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + GDALPipeWrite(p, 0); /* extra bytes */ + + char* pszVersion = NULL; + int nMajor, nMinor, nProtocolMajor, nProtocolMinor, nExtraBytes; + if( !GDALPipeRead(p, &pszVersion) || + !GDALPipeRead(p, &nMajor) || + !GDALPipeRead(p, &nMinor) || + !GDALPipeRead(p, &nProtocolMajor) || + !GDALPipeRead(p, &nProtocolMinor) || + !GDALPipeRead(p, &nExtraBytes) ) + { + CPLFree(pszVersion); + return FALSE; + } + + if( nExtraBytes > 0 ) + { + void* pTemp = VSIMalloc(nExtraBytes); + if( !pTemp ) + { + CPLFree(pszVersion); + return FALSE; + } + if( !GDALPipeRead_nolength(p, nExtraBytes, pTemp) ) + { + CPLFree(pszVersion); + CPLFree(pTemp); + return FALSE; + } + CPLFree(pTemp); + } + + CPLDebug("GDAL", + "Server version : %s (%d.%d), " + "Server protocol version = %d.%d", + pszVersion, + nMajor, nMinor, + nProtocolMajor, nProtocolMinor); + CPLDebug("GDAL", + "Client version : %s (%d.%d), " + "Client protocol version = %d.%d", + GDAL_RELEASE_NAME, GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, + GDAL_CLIENT_SERVER_PROTOCOL_MAJOR, + GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + if( nProtocolMajor != GDAL_CLIENT_SERVER_PROTOCOL_MAJOR ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDAL server (GDAL version=%s, protocol version=%d.%d) is " + "incompatible with GDAL client (GDAL version=%s, protocol version=%d.%d)", + pszVersion, + nProtocolMajor, nProtocolMinor, + GDAL_RELEASE_NAME, + GDAL_CLIENT_SERVER_PROTOCOL_MAJOR, + GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + CPLFree(pszVersion); + return FALSE; + } + else if( nProtocolMinor != GDAL_CLIENT_SERVER_PROTOCOL_MINOR ) + { + CPLDebug("GDAL", "Note: client/server protocol versions differ by minor number."); + } + CPLFree(pszVersion); + return TRUE; +} + +/************************************************************************/ +/* GDALServerLoopForked() */ +/************************************************************************/ + +#ifndef WIN32 +void CPLReinitAllMutex(); + +static int GDALServerLoopForked(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout) +{ + /* Do not try to close datasets at process closing */ + GDALNullifyOpenDatasetsList(); + /* Nullify the existing mutex to avoid issues with locked mutex by */ + /* parent's process threads */ + GDALNullifyProxyPoolSingleton(); +#ifdef CPL_MULTIPROC_PTHREAD + CPLReinitAllMutex(); +#endif + + memset(aspRecycled, 0, sizeof(aspRecycled)); + + return GDALServerLoop(fin, fout); +} +#endif + +/************************************************************************/ +/* GDALServerSpawnAsync() */ +/************************************************************************/ + +static GDALServerSpawnedProcess* GDALServerSpawnAsync() +{ + if( bRecycleChild ) + { + /* Try to find an existing unused descriptor to reuse it */ + CPLMutexHolderD(GDALGetphDMMutex()); + for(int i = 0; i < nMaxRecycled; i ++) + { + if( aspRecycled[i] != NULL ) + { + GDALServerSpawnedProcess* ssp = aspRecycled[i]; + aspRecycled[i] = NULL; + return ssp; + } + } + } + +#ifdef WIN32 + const char* pszSpawnServer = CPLGetConfigOption("GDAL_API_PROXY_SERVER", "gdalserver"); +#else + const char* pszSpawnServer = CPLGetConfigOption("GDAL_API_PROXY_SERVER", "NO"); +#endif + + const char* pszColon = strchr(pszSpawnServer, ':'); + if( pszColon != NULL && + pszColon != pszSpawnServer + 1 /* do not confuse with c:/some_path/gdalserver.exe */ ) + { + CPLString osHost(pszSpawnServer); + osHost.resize(pszColon - pszSpawnServer); + CPL_SOCKET nConnSocket = INVALID_SOCKET; + int nRet; + +#ifdef WIN32 + WSADATA wsaData; + + nRet = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (nRet != NO_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WSAStartup() failed with error: %d\n", nRet); + return NULL; + } +#endif + +#ifdef HAVE_GETADDRINFO + struct addrinfo sHints; + struct addrinfo* psResults = NULL, *psResultsIter; + memset(&sHints, 0, sizeof(struct addrinfo)); + sHints.ai_family = AF_UNSPEC; + sHints.ai_socktype = SOCK_STREAM; + sHints.ai_flags = 0; + sHints.ai_protocol = IPPROTO_TCP; + + nRet = getaddrinfo(osHost, pszColon + 1, &sHints, &psResults); + if (nRet) + { + CPLError(CE_Failure, CPLE_AppDefined, + "getaddrinfo(): %s", gai_strerror(nRet)); + WSACleanup(); + return NULL; + } + + for( psResultsIter = psResults; + psResultsIter != NULL; + psResultsIter = psResultsIter->ai_next) + { + nConnSocket = socket(psResultsIter->ai_family, + psResultsIter->ai_socktype, + psResultsIter->ai_protocol); + if (nConnSocket == INVALID_SOCKET) + continue; + + if (connect(nConnSocket, psResultsIter->ai_addr, + psResultsIter->ai_addrlen) != SOCKET_ERROR) + break; + + closesocket(nConnSocket); + } + + freeaddrinfo(psResults); + + if (psResultsIter == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not connect"); + WSACleanup(); + return NULL; + } +#else + struct sockaddr_in sockAddrIn; + int nPort = atoi(pszColon + 1); + sockAddrIn.sin_family = AF_INET; + sockAddrIn.sin_addr.s_addr = inet_addr(osHost); + if (sockAddrIn.sin_addr.s_addr == INADDR_NONE) + { + struct hostent *hp; + hp = gethostbyname(osHost); + if (hp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown host : %s", osHost.c_str()); + WSACleanup(); + return NULL; + } + else + { + sockAddrIn.sin_family = hp->h_addrtype; + memcpy(&(sockAddrIn.sin_addr.s_addr), hp->h_addr, hp->h_length); + } + } + sockAddrIn.sin_port = htons(nPort); + + nConnSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (nConnSocket == INVALID_SOCKET) + { + CPLError(CE_Failure, CPLE_AppDefined, + "socket() failed with error: %d", WSAGetLastError()); + WSACleanup(); + return NULL; + } + + if (connect(nConnSocket, (const SOCKADDR *)&sockAddrIn, sizeof (sockAddrIn)) == SOCKET_ERROR ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "connect() function failed with error: %d", WSAGetLastError()); + closesocket(nConnSocket); + WSACleanup(); + return NULL; + } +#endif + + GDALServerSpawnedProcess* ssp = + (GDALServerSpawnedProcess*)CPLMalloc(sizeof(GDALServerSpawnedProcess)); + ssp->sp = NULL; + ssp->p = GDALPipeBuild(nConnSocket); + + CPLDebug("GDAL", "Create spawned process %p", ssp); + if( !GDALCheckServerVersion(ssp->p) ) + { + GDALServerSpawnAsyncFinish(ssp); + return NULL; + } + return ssp; + } + +#ifndef WIN32 + VSIStatBuf sStat; + if( VSIStat(pszSpawnServer, &sStat) == 0 && sStat.st_size == 0 ) + { + int nConnSocket = socket(AF_UNIX, SOCK_STREAM, 0); + if (nConnSocket >= 0) + { + struct sockaddr_un sockAddrUnix; + sockAddrUnix.sun_family = AF_UNIX; + CPLStrlcpy(sockAddrUnix.sun_path, pszSpawnServer, sizeof(sockAddrUnix.sun_path)); + + if (connect(nConnSocket, (const SOCKADDR *)&sockAddrUnix, sizeof (sockAddrUnix)) >= 0 ) + { + GDALServerSpawnedProcess* ssp = + (GDALServerSpawnedProcess*)CPLMalloc(sizeof(GDALServerSpawnedProcess)); + ssp->sp = NULL; + ssp->p = GDALPipeBuild(nConnSocket); + + CPLDebug("GDAL", "Create spawned process %p", ssp); + if( !GDALCheckServerVersion(ssp->p) ) + { + GDALServerSpawnAsyncFinish(ssp); + return NULL; + } + return ssp; + } + else + closesocket(nConnSocket); + } + } +#endif + + if( EQUAL(pszSpawnServer, "YES") || EQUAL(pszSpawnServer, "ON") || + EQUAL(pszSpawnServer, "TRUE") || EQUAL(pszSpawnServer, "1") ) + pszSpawnServer = "gdalserver"; +#ifdef WIN32 + const char* apszGDALServer[] = { pszSpawnServer, "-stdinout", NULL }; +#else + const char* apszGDALServer[] = { pszSpawnServer, "-pipe_in", "{pipe_in}", "-pipe_out", "{pipe_out}", NULL }; + if( strstr(pszSpawnServer, "gdalserver") == NULL ) + apszGDALServer[1] = NULL; +#endif + int bCheckVersions = TRUE; + + CPLSpawnedProcess* sp; +#ifndef WIN32 + if( EQUAL(pszSpawnServer, "NO") || EQUAL(pszSpawnServer, "OFF") || + EQUAL(pszSpawnServer, "FALSE") || EQUAL(pszSpawnServer, "0") ) + { + sp = CPLSpawnAsync(GDALServerLoopForked, NULL, TRUE, TRUE, FALSE, NULL); + bCheckVersions = FALSE; + } + else +#endif + sp = CPLSpawnAsync(NULL, apszGDALServer, TRUE, TRUE, FALSE, NULL); + + if( sp == NULL ) + return NULL; + + GDALServerSpawnedProcess* ssp = + (GDALServerSpawnedProcess*)CPLMalloc(sizeof(GDALServerSpawnedProcess)); + ssp->sp = sp; + ssp->p = GDALPipeBuild(sp); + + CPLDebug("GDAL", "Create spawned process %p", ssp); + if( bCheckVersions && !GDALCheckServerVersion(ssp->p) ) + { + GDALServerSpawnAsyncFinish(ssp); + return NULL; + } + return ssp; +} + +/************************************************************************/ +/* CPLErrOnlyRet() */ +/************************************************************************/ + +static CPLErr CPLErrOnlyRet(GDALPipe* p) +{ + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* RunErrorHandler() */ +/************************************************************************/ + +class GDALServerErrorDesc +{ + public: + GDALServerErrorDesc() {} + + CPLErr eErr; + int nErrNo; + CPLString osErrorMsg; +}; + +static void CPL_STDCALL RunErrorHandler(CPLErr eErr, int nErrNo, + const char* pszErrorMsg) +{ + GDALServerErrorDesc oDesc; + oDesc.eErr = eErr; + oDesc.nErrNo = nErrNo; + oDesc.osErrorMsg = pszErrorMsg; + std::vector* paoErrors = + (std::vector*) CPLGetErrorHandlerUserData(); + if( paoErrors ) + paoErrors->push_back(oDesc); +} + +/************************************************************************/ +/* RunAsyncProgress() */ +/************************************************************************/ + +static int CPL_STDCALL RunAsyncProgress(double dfComplete, + const char *pszMessage, + void *pProgressArg) +{ + /* We don't send the progress right now, since some drivers like ECW */ + /* call the progress callback from an helper thread, while calling methods */ + /* on the source dataset. So we could end up sending mixed content on the pipe */ + /* to the client. The best is to transmit the progress in a regularly called method */ + /* of the dataset, such as IReadBlock() / IRasterIO() */ + GDALServerAsyncProgress* asyncp = (GDALServerAsyncProgress*)pProgressArg; + CPLMutexHolderD(&(asyncp->hMutex)); + asyncp->bUpdated = TRUE; + asyncp->dfComplete = dfComplete; + CPLFree(asyncp->pszProgressMsg); + asyncp->pszProgressMsg = (pszMessage) ? CPLStrdup(pszMessage) : NULL; + return asyncp->bRet; +} + +/************************************************************************/ +/* RunSyncProgress() */ +/************************************************************************/ + +static int CPL_STDCALL RunSyncProgress(double dfComplete, + const char *pszMessage, + void *pProgressArg) +{ + GDALPipe* p = (GDALPipe*)pProgressArg; + if( !GDALPipeWrite(p, INSTR_Progress) || + !GDALPipeWrite(p, dfComplete) || + !GDALPipeWrite(p, pszMessage) ) + return FALSE; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return FALSE; + int bRet = FALSE; + if( !GDALPipeRead(p, &bRet) ) + return FALSE; + GDALConsumeErrors(p); + return bRet; +} + +/************************************************************************/ +/* GDALServerLoop() */ +/************************************************************************/ + +static int GDALServerLoop(GDALPipe* p, + GDALDataset* poSrcDS, + GDALProgressFunc pfnProgress, void* pProgressData) +{ + GDALDataset* poDS = NULL; + std::vector aBands; + std::vector aoErrors; + int nRet = 1; + GDALServerAsyncProgress asyncp; + memset(&asyncp, 0, sizeof(asyncp)); + asyncp.bRet = TRUE; + void* pBuffer = NULL; + int nBufferSize = 0; + + const char* pszOldVal = CPLGetConfigOption("GDAL_API_PROXY", NULL); + char* pszOldValDup = (pszOldVal) ? CPLStrdup(pszOldVal) : NULL; + CPLSetThreadLocalConfigOption("GDAL_API_PROXY", "OFF"); + + if( poSrcDS == NULL ) + CPLPushErrorHandlerEx(RunErrorHandler, &aoErrors); + + // fprintf(stderr, "[%d] started\n", (int)getpid()); + while(TRUE) + { + int instr; + if( !GDALPipeRead(p, &instr) ) + { + // fprintf(stderr, "[%d] instr failed\n", (int)getpid()); + break; + } + + // fprintf(stderr, "[%d] %s\n", (int)getpid(), (instr >= 0 && instr < INSTR_END) ? apszInstr[instr] : "unknown"); + + GDALRasterBand* poBand = NULL; + + if( instr == INSTR_EXIT ) + { + if( poSrcDS == NULL && poDS != NULL ) + { + GDALClose((GDALDatasetH)poDS); + poDS = NULL; + aBands.resize(0); + } + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, TRUE); + nRet = 0; + break; + } + else if( instr == INSTR_EXIT_FAIL ) + { + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, TRUE); + break; + } + else if( instr == INSTR_GetGDALVersion || + instr == 0x01000000 ) + { + /* Do not change this protocol ! */ + char bClientIsLSB; + char* pszClientVersion = NULL; + int nClientMajor, nClientMinor, + nClientProtocolMajor, nClientProtocolMinor, + nExtraBytes; + if( !GDALPipeRead_nolength(p, 1, &bClientIsLSB) ) + break; + if( bClientIsLSB != CPL_IS_LSB ) + { + fprintf(stderr, "Server does not understand client endianness.\n"); + break; + } + + if (!GDALPipeRead(p, &pszClientVersion) || + !GDALPipeRead(p, &nClientMajor) || + !GDALPipeRead(p, &nClientMinor) || + !GDALPipeRead(p, &nClientProtocolMajor) || + !GDALPipeRead(p, &nClientProtocolMinor) || + !GDALPipeRead(p, &nExtraBytes) ) + { + CPLFree(pszClientVersion); + break; + } + + if( nExtraBytes > 0 ) + { + void* pTemp = VSIMalloc(nExtraBytes); + if( !pTemp ) + { + CPLFree(pszClientVersion); + break; + } + if( !GDALPipeRead_nolength(p, nExtraBytes, pTemp) ) + { + CPLFree(pszClientVersion); + CPLFree(pTemp); + break; + } + CPLFree(pTemp); + } + + CPLFree(pszClientVersion); + + GDALPipeWrite(p, GDAL_RELEASE_NAME); + GDALPipeWrite(p, GDAL_VERSION_MAJOR); + GDALPipeWrite(p, GDAL_VERSION_MINOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MAJOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + GDALPipeWrite(p, 0); /* extra bytes */ + continue; + } + else if( instr == INSTR_SetConfigOption ) + { + char *pszKey = NULL, *pszValue = NULL; + if( !GDALPipeRead(p, &pszKey) || + !GDALPipeRead(p, &pszValue) ) + { + CPLFree(pszKey); + break; + } + CPLSetConfigOption(pszKey, pszValue); + CPLFree(pszKey); + CPLFree(pszValue); + continue; + } + else if( instr == INSTR_Progress ) + { + double dfProgress; + char* pszProgressMsg = NULL; + if( !GDALPipeRead(p, &dfProgress) || + !GDALPipeRead(p, &pszProgressMsg) ) + break; + int nRet = pfnProgress(dfProgress, pszProgressMsg, pProgressData); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nRet); + CPLFree(pszProgressMsg); + } + else if( instr == INSTR_Reset ) + { + if( poSrcDS == NULL && poDS != NULL ) + { + GDALClose((GDALDatasetH)poDS); + poDS = NULL; + MyChdirRootDirectory(); + aBands.resize(0); + } + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, TRUE); + } + else if( instr == INSTR_Open ) + { + int nAccess; + char* pszFilename = NULL; + char* pszCWD = NULL; + if( !GDALPipeRead(p, &nAccess) || + !GDALPipeRead(p, &pszFilename) || + !GDALPipeRead(p, &pszCWD) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + if( poSrcDS != NULL ) + poDS = poSrcDS; + else if( poDS == NULL && pszFilename != NULL ) + poDS = (GDALDataset*) GDALOpen(pszFilename, (GDALAccess)nAccess); + CPLFree(pszFilename); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poDS != NULL); + if( poDS != NULL ) + { + CPLAssert(INSTR_END < 128); + GByte abyCaps[16]; /* 16 * 8 = 128 */ + memset(abyCaps, 0, sizeof(abyCaps)); + /* We implement all known instructions (except marker ones) */ + for(int c = 1; c < INSTR_END; c++) + { + if( c != INSTR_Band_First && c != INSTR_Band_End ) + abyCaps[c / 8] |= (1 << (c % 8)); + } + GDALPipeWrite(p, sizeof(abyCaps), abyCaps); + GDALPipeWrite(p, poDS->GetDescription() ); + GDALDriver* poDriver = poDS->GetDriver(); + if( poDriver != NULL ) + { + GDALPipeWrite(p, poDriver->GetDescription() ); + char** papszItems = poDriver->GetMetadata(); + for(int i = 0; papszItems[i] != NULL; i++ ) + { + char* pszKey = NULL; + const char* pszVal = CPLParseNameValue(papszItems[i], &pszKey ); + if( pszKey != NULL ) + { + GDALPipeWrite(p, pszKey ); + GDALPipeWrite(p, pszVal ); + CPLFree(pszKey); + } + } + GDALPipeWrite(p, (const char*)NULL); + } + else + GDALPipeWrite(p, (const char*)NULL); + + GDALPipeWrite(p, poDS->GetRasterXSize()); + GDALPipeWrite(p, poDS->GetRasterYSize()); + int nBands = poDS->GetRasterCount(); + GDALPipeWrite(p, nBands); + int i; + int bAllSame = TRUE; + GDALRasterBand* poFirstBand = NULL; + int nFBBlockXSize = 0, nFBBlockYSize = 0; + + /* Check if all bands are identical */ + for(i=0;iGetRasterBand(i+1); + if( strlen(poBand->GetDescription()) > 0 ) + { + bAllSame = FALSE; + break; + } + if( i == 0 ) + { + poFirstBand = poBand; + poBand->GetBlockSize(&nFBBlockXSize, &nFBBlockYSize); + } + else + { + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + if( poBand->GetXSize() != poFirstBand->GetXSize() || + poBand->GetYSize() != poFirstBand->GetYSize() || + poBand->GetRasterDataType() != poFirstBand->GetRasterDataType() || + nBlockXSize != nFBBlockXSize || + nBlockYSize != nFBBlockYSize ) + { + bAllSame = FALSE; + break; + } + } + } + + /* Transmit bands */ + GDALPipeWrite(p, bAllSame); + for(i=0;iGetRasterBand(i+1); + if( i > 0 && bAllSame ) + aBands.push_back(poBand); + else + GDALPipeWrite(p, aBands, poBand); + } + } + } + else if( instr == INSTR_Identify ) + { + char* pszFilename = NULL; + char* pszCWD = NULL; + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszCWD) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + int bRet = GDALIdentifyDriver(pszFilename, NULL) != NULL; + CPLFree(pszFilename); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bRet); + aoErrors.resize(0); + } + else if( instr == INSTR_Create ) + { + char* pszFilename = NULL; + char* pszCWD = NULL; + int nXSize, nYSize, nBands, nDataType; + char** papszOptions = NULL; + GDALDriver* poDriver = NULL; + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszCWD) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBands) || + !GDALPipeRead(p, &nDataType) || + !GDALPipeRead(p, &papszOptions) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + const char* pszDriver = CSLFetchNameValue(papszOptions, "SERVER_DRIVER"); + CPLString osDriver; + if( pszDriver != NULL ) + { + osDriver = pszDriver; + pszDriver = osDriver.c_str(); + poDriver = (GDALDriver* )GDALGetDriverByName(pszDriver); + } + papszOptions = CSLSetNameValue(papszOptions, "SERVER_DRIVER", NULL); + if( poDriver != NULL ) + { + poDS = poDriver->Create(pszFilename, nXSize, nYSize, nBands, + (GDALDataType)nDataType, + papszOptions); + } + else + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s", + (pszDriver) ? pszDriver : "(unknown)"); + + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poDS != NULL); + CPLFree(pszFilename); + CSLDestroy(papszOptions); + } + else if( instr == INSTR_CreateCopy ) + { + char* pszFilename = NULL; + char* pszSrcDescription = NULL; + char* pszCWD = NULL; + char** papszCreateOptions = NULL; + GDALDriver* poDriver = NULL; + int bStrict = FALSE; + + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszSrcDescription) || + !GDALPipeRead(p, &pszCWD) || + !GDALPipeRead(p, &bStrict) || + !GDALPipeRead(p, &papszCreateOptions) ) + { + CPLFree(pszFilename); + CPLFree(pszSrcDescription); + CPLFree(pszCWD); + break; + } + + CPLFree(pszSrcDescription); + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + const char* pszDriver = CSLFetchNameValue(papszCreateOptions, "SERVER_DRIVER"); + CPLString osDriver; + if( pszDriver != NULL ) + { + osDriver = pszDriver; + pszDriver = osDriver.c_str(); + poDriver = (GDALDriver* )GDALGetDriverByName(pszDriver); + } + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "SERVER_DRIVER", NULL); + GDALPipeWrite(p, poDriver != NULL); + if( poDriver != NULL ) + { + GDALClientDataset* poSrcDS = new GDALClientDataset(p); + if( !poSrcDS->Init(NULL, GA_ReadOnly) ) + { + delete poSrcDS; + CPLFree(pszFilename); + CSLDestroy(papszCreateOptions); + break; + } + poSrcDS->AttachAsyncProgress(&asyncp); + + poDS = poDriver->CreateCopy(pszFilename, poSrcDS, + bStrict, papszCreateOptions, + RunAsyncProgress, &asyncp); + + int bProgressRet = poSrcDS->ProcessAsyncProgress(); + GDALClose((GDALDatasetH)poSrcDS); + + if( !bProgressRet && poDS != NULL ) + { + GDALClose((GDALDatasetH)poDS); + poDS = NULL; + } + + if( !GDALEmitEXIT(p, (poDS != NULL) ? INSTR_EXIT : INSTR_EXIT_FAIL) ) + break; + } + else + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s", + (pszDriver) ? pszDriver : "(unknown)"); + + CPLFree(pszFilename); + CSLDestroy(papszCreateOptions); + } + else if( instr == INSTR_QuietDelete ) + { + char* pszFilename = NULL; + char* pszCWD = NULL; + + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszCWD) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + GDALDriver::QuietDelete(pszFilename); + + GDALEmitEndOfJunkMarker(p); + CPLFree(pszFilename); + } + else if( instr == INSTR_AddBand ) + { + if( poDS == NULL ) + break; + int nType; + char** papszOptions = NULL; + if( !GDALPipeRead(p, &nType) || + !GDALPipeRead(p, &papszOptions) ) + break; + CPLErr eErr = poDS->AddBand((GDALDataType)nType, papszOptions); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr == CE_None ) + { + int nBandCount = poDS->GetRasterCount(); + GDALPipeWrite(p, aBands, poDS->GetRasterBand(nBandCount)); + } + CSLDestroy(papszOptions); + } + else if( instr == INSTR_GetGeoTransform ) + { + if( poDS == NULL ) + break; + double adfGeoTransform[6]; + CPLErr eErr = poDS->GetGeoTransform(adfGeoTransform); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, 6 * sizeof(double), adfGeoTransform); + } + } + else if( instr == INSTR_SetGeoTransform ) + { + if( poDS == NULL ) + break; + double adfGeoTransform[6]; + if( !GDALPipeRead(p, 6 * sizeof(double), adfGeoTransform) ) + break; + CPLErr eErr = poDS->SetGeoTransform(adfGeoTransform); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_GetProjectionRef ) + { + if( poDS == NULL ) + break; + const char* pszVal = poDS->GetProjectionRef(); + //GDALPipeWrite(p, strlen("some_junk\xDE"), "some_junk\xDE"); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + } + else if( instr == INSTR_SetProjection ) + { + if( poDS == NULL ) + break; + char* pszProjection = NULL; + if( !GDALPipeRead(p, &pszProjection) ) + break; + CPLErr eErr = poDS->SetProjection(pszProjection); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszProjection); + } + else if( instr == INSTR_GetGCPCount ) + { + if( poDS == NULL ) + break; + int nGCPCount = poDS->GetGCPCount(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nGCPCount ); + } + else if( instr == INSTR_GetGCPProjection ) + { + if( poDS == NULL ) + break; + const char* pszVal = poDS->GetGCPProjection(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + } + else if( instr == INSTR_GetGCPs ) + { + if( poDS == NULL ) + break; + int nGCPCount = poDS->GetGCPCount(); + const GDAL_GCP* pasGCPs = poDS->GetGCPs(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nGCPCount, pasGCPs); + } + else if( instr == INSTR_SetGCPs ) + { + if( poDS == NULL ) + break; + int nGCPCount; + GDAL_GCP* pasGCPs = NULL; + if( !GDALPipeRead(p, &nGCPCount, &pasGCPs) ) + break; + char* pszGCPProjection = NULL; + if( !GDALPipeRead(p, &pszGCPProjection) ) + break; + CPLErr eErr = poDS->SetGCPs(nGCPCount, pasGCPs, pszGCPProjection); + GDALDeinitGCPs(nGCPCount, pasGCPs); + CPLFree(pasGCPs); + CPLFree(pszGCPProjection); + + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr ); + + } + else if( instr == INSTR_GetFileList ) + { + if( poDS == NULL ) + break; + char** papszFileList = poDS->GetFileList(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, papszFileList); + CSLDestroy(papszFileList); + } + else if( instr == INSTR_FlushCache ) + { + if( poDS ) + poDS->FlushCache(); + GDALEmitEndOfJunkMarker(p); + } + /*else if( instr == INSTR_SetDescription ) + { + if( poDS == NULL ) + break; + char* pszDescription = NULL; + if( !GDALPipeRead(p, &pszDescription) ) + break; + poDS->SetDescription(pszDescription); + CPLFree(pszDescription); + GDALEmitEndOfJunkMarker(p); + }*/ + else if( instr == INSTR_GetMetadata ) + { + if( poDS == NULL ) + break; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszDomain) ) + break; + char** papszMD = poDS->GetMetadata(pszDomain); + GDALEmitEndOfJunkMarker(p); + CPLFree(pszDomain); + GDALPipeWrite(p, papszMD); + } + else if( instr == INSTR_GetMetadataItem ) + { + if( poDS == NULL ) + break; + char* pszName = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszDomain); + break; + } + const char* pszVal = poDS->GetMetadataItem(pszName, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + CPLFree(pszName); + CPLFree(pszDomain); + } + else if( instr == INSTR_SetMetadata ) + { + if( poDS == NULL ) + break; + char** papszMetadata = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &papszMetadata) || + !GDALPipeRead(p, &pszDomain) ) + { + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poDS->SetMetadata(papszMetadata, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + } + else if( instr == INSTR_SetMetadataItem ) + { + if( poDS == NULL ) + break; + char* pszName = NULL; + char* pszValue = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszValue) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poDS->SetMetadataItem(pszName, pszValue, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + } + else if( instr == INSTR_IBuildOverviews ) + { + if( poDS == NULL ) + break; + char* pszResampling = NULL; + int nOverviews; + int* panOverviewList = NULL; + int nListBands; + int* panBandList = NULL; + if( !GDALPipeRead(p, &pszResampling) || + !GDALPipeRead(p, &nOverviews) || + !GDALPipeRead(p, nOverviews, &panOverviewList) || + !GDALPipeRead(p, &nListBands) || + !GDALPipeRead(p, nListBands, &panBandList) ) + { + CPLFree(pszResampling); + CPLFree(panOverviewList); + CPLFree(panBandList); + break; + } + + CPLErr eErr = poDS->BuildOverviews(pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + RunSyncProgress, p); + + CPLFree(pszResampling); + CPLFree(panOverviewList); + CPLFree(panBandList); + + if( !GDALEmitEXIT(p, (eErr != CE_Failure) ? INSTR_EXIT : INSTR_EXIT_FAIL) ) + break; + } + else if( instr == INSTR_AdviseRead ) + { + if( poDS == NULL ) + break; + int nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize; + int nDT; + int nBandCount; + int *panBandList = NULL; + char** papszOptions = NULL; + int nLength = 0; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nDT) || + !GDALPipeRead(p, &nBandCount) || + !GDALPipeRead(p, &nLength) ) + { + break; + } + + /* panBandList can be NULL, hence the following test */ + /* to check if we have band numbers to actually read */ + if( nLength != 0 ) + { + if( nLength != (int)sizeof(int) * nBandCount ) + { + break; + } + + panBandList = (int*) VSIMalloc(nLength); + if( panBandList == NULL ) + break; + + if( !GDALPipeRead_nolength(p, nLength, (void*)panBandList) ) + { + VSIFree(panBandList); + break; + } + } + + if (!GDALPipeRead(p, &papszOptions) ) + { + CPLFree(panBandList); + CSLDestroy(papszOptions); + break; + } + + CPLErr eErr = poDS->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + (GDALDataType)nDT, + nBandCount, panBandList, + papszOptions); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(panBandList); + CSLDestroy(papszOptions); + } + else if( instr == INSTR_IRasterIO_Read ) + { + if( poDS == NULL ) + break; + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + int nBandCount; + int nPixelSpace, nLineSpace, nBandSpace; + int* panBandMap = NULL; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) || + !GDALPipeRead(p, &nBandCount) || + !GDALPipeRead(p, nBandCount, &panBandMap) || + !GDALPipeRead(p, &nPixelSpace) || + !GDALPipeRead(p, &nLineSpace) || + !GDALPipeRead(p, &nBandSpace) ) + { + CPLFree(panBandMap); + break; + } + /* Note: only combinations of nPixelSpace, nLineSpace and + nBandSpace that lead to compate band-interleaved or pixel- + interleaved buffers are valid. Other combinations will lead to segfault */ + eBufType = (GDALDataType)nBufType; + int nSize = nBufXSize * nBufYSize * nBandCount * + (GDALGetDataTypeSize(eBufType) / 8); + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + + CPLErr eErr = poDS->RasterIO(GF_Read, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace); + CPLFree(panBandMap); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + GDALPipeWrite(p, nSize, pBuffer); + } + else if( instr == INSTR_IRasterIO_Write ) + { + if( poDS == NULL ) + break; + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + int nBandCount; + int nPixelSpace, nLineSpace, nBandSpace; + int* panBandMap = NULL; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) || + !GDALPipeRead(p, &nBandCount) || + !GDALPipeRead(p, nBandCount, &panBandMap) || + !GDALPipeRead(p, &nPixelSpace) || + !GDALPipeRead(p, &nLineSpace) || + !GDALPipeRead(p, &nBandSpace) ) + { + CPLFree(panBandMap); + break; + } + /* Note: only combinations of nPixelSpace, nLineSpace and + nBandSpace that lead to compate band-interleaved or pixel- + interleaved buffers are valid. Other combinations will lead to segfault */ + eBufType = (GDALDataType)nBufType; + int nExpectedSize = nBufXSize * nBufYSize * nBandCount * + (GDALGetDataTypeSize(eBufType) / 8); + int nSize; + if( !GDALPipeRead(p, &nSize) ) + break; + if( nSize != nExpectedSize ) + break; + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + if( !GDALPipeRead_nolength(p, nSize, pBuffer) ) + break; + + CPLErr eErr = poDS->RasterIO(GF_Write, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace); + CPLFree(panBandMap); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_CreateMaskBand ) + { + if( poDS == NULL ) + break; + int nFlags; + if( !GDALPipeRead(p, &nFlags) ) + break; + CPLErr eErr = poDS->CreateMaskBand(nFlags); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr > INSTR_Band_First && instr < INSTR_Band_End ) + { + int iBand; + if( !GDALPipeRead(p, &iBand) ) + break; + if( iBand < 0 || iBand >= (int)aBands.size() ) + break; + poBand = aBands[iBand]; + } + else + break; + + if( instr == INSTR_Band_FlushCache ) + { + CPLErr eErr = poBand->FlushCache(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetCategoryNames ) + { + char** papszCategoryNames = poBand->GetCategoryNames(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, papszCategoryNames); + } + else if( instr == INSTR_Band_SetCategoryNames ) + { + char** papszCategoryNames = NULL; + if( !GDALPipeRead(p, &papszCategoryNames) ) + break; + CPLErr eErr = poBand->SetCategoryNames(papszCategoryNames); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszCategoryNames); + } + else if( instr == INSTR_Band_SetDescription ) + { + char* pszDescription = NULL; + if( !GDALPipeRead(p, &pszDescription) ) + break; + poBand->SetDescription(pszDescription); + CPLFree(pszDescription); + GDALEmitEndOfJunkMarker(p); + } + else if( instr == INSTR_Band_GetMetadata ) + { + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszDomain) ) + break; + char** papszMD = poBand->GetMetadata(pszDomain); + GDALEmitEndOfJunkMarker(p); + CPLFree(pszDomain); + GDALPipeWrite(p, papszMD); + } + else if( instr == INSTR_Band_GetMetadataItem ) + { + char* pszName = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszDomain); + break; + } + const char* pszVal = poBand->GetMetadataItem(pszName, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + CPLFree(pszName); + CPLFree(pszDomain); + } + else if( instr == INSTR_Band_SetMetadata ) + { + char** papszMetadata = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &papszMetadata) || + !GDALPipeRead(p, &pszDomain) ) + { + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poBand->SetMetadata(papszMetadata, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + } + else if( instr == INSTR_Band_SetMetadataItem ) + { + char* pszName = NULL; + char* pszValue = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszValue) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poBand->SetMetadataItem(pszName, pszValue, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + } + else if( instr == INSTR_Band_GetColorInterpretation ) + { + GDALColorInterp eInterp = poBand->GetColorInterpretation(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eInterp); + } + else if( instr == INSTR_Band_SetColorInterpretation ) + { + int nVal; + if( !GDALPipeRead(p, &nVal ) ) + break; + CPLErr eErr = poBand->SetColorInterpretation((GDALColorInterp)nVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetNoDataValue ) + { + int bSuccess; + double dfVal = poBand->GetNoDataValue(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetMinimum ) + { + int bSuccess; + double dfVal = poBand->GetMinimum(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetMaximum ) + { + int bSuccess; + double dfVal = poBand->GetMaximum(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetScale ) + { + int bSuccess; + double dfVal = poBand->GetScale(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetOffset ) + { + int bSuccess; + double dfVal = poBand->GetOffset(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_SetNoDataValue ) + { + double dfVal; + if( !GDALPipeRead(p, &dfVal ) ) + break; + CPLErr eErr = poBand->SetNoDataValue(dfVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_SetOffset ) + { + double dfVal; + if( !GDALPipeRead(p, &dfVal ) ) + break; + CPLErr eErr = poBand->SetOffset(dfVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_SetScale ) + { + double dfVal; + if( !GDALPipeRead(p, &dfVal ) ) + break; + CPLErr eErr = poBand->SetScale(dfVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_IReadBlock ) + { + int nBlockXOff, nBlockYOff; + if( !GDALPipeRead(p, &nBlockXOff) || + !GDALPipeRead(p, &nBlockYOff) ) + break; + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + int nSize = nBlockXSize * nBlockYSize * + (GDALGetDataTypeSize(poBand->GetRasterDataType()) / 8); + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + CPLErr eErr = poBand->ReadBlock(nBlockXOff, nBlockYOff, pBuffer); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + GDALPipeWrite(p, nSize, pBuffer); + } + else if( instr == INSTR_Band_IWriteBlock ) + { + int nBlockXOff, nBlockYOff, nSize; + if( !GDALPipeRead(p, &nBlockXOff) || + !GDALPipeRead(p, &nBlockYOff) || + !GDALPipeRead(p, &nSize) ) + break; + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + int nExpectedSize = nBlockXSize * nBlockYSize * + (GDALGetDataTypeSize(poBand->GetRasterDataType()) / 8); + if( nExpectedSize != nSize ) + break; + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + if( !GDALPipeRead_nolength(p, nSize, pBuffer) ) + break; + + CPLErr eErr = poBand->WriteBlock(nBlockXOff, nBlockYOff, pBuffer); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_IRasterIO_Read ) + { + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) ) + break; + eBufType = (GDALDataType)nBufType; + int nSize = nBufXSize * nBufYSize * + (GDALGetDataTypeSize(eBufType) / 8); + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + + CPLErr eErr = poBand->RasterIO(GF_Read, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, 0, 0); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + GDALPipeWrite(p, nSize, pBuffer); + } + else if( instr == INSTR_Band_IRasterIO_Write ) + { + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) ) + break; + eBufType = (GDALDataType)nBufType; + int nExpectedSize = nBufXSize * nBufYSize * + (GDALGetDataTypeSize(eBufType) / 8); + int nSize; + if( !GDALPipeRead(p, &nSize) ) + break; + if( nSize != nExpectedSize ) + break; + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + if( !GDALPipeRead_nolength(p, nSize, pBuffer) ) + break; + + CPLErr eErr = poBand->RasterIO(GF_Write, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, 0, 0); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetStatistics ) + { + int bApproxOK, bForce; + if( !GDALPipeRead(p, &bApproxOK) || + !GDALPipeRead(p, &bForce) ) + break; + double dfMin = 0.0, dfMax = 0.0, dfMean = 0.0, dfStdDev = 0.0; + CPLErr eErr = poBand->GetStatistics(bApproxOK, bForce, + &dfMin, &dfMax, &dfMean, &dfStdDev); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr == CE_None ) + { + GDALPipeWrite(p, dfMin); + GDALPipeWrite(p, dfMax); + GDALPipeWrite(p, dfMean); + GDALPipeWrite(p, dfStdDev); + } + } + else if( instr == INSTR_Band_ComputeStatistics ) + { + int bApproxOK; + if( !GDALPipeRead(p, &bApproxOK) ) + break; + double dfMin = 0.0, dfMax = 0.0, dfMean = 0.0, dfStdDev = 0.0; + CPLErr eErr = poBand->ComputeStatistics(bApproxOK, + &dfMin, &dfMax, &dfMean, &dfStdDev, + NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, dfMin); + GDALPipeWrite(p, dfMax); + GDALPipeWrite(p, dfMean); + GDALPipeWrite(p, dfStdDev); + } + } + else if( instr == INSTR_Band_SetStatistics ) + { + double dfMin, dfMax, dfMean, dfStdDev; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &dfMean) || + !GDALPipeRead(p, &dfStdDev) ) + break; + CPLErr eErr = poBand->SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_ComputeRasterMinMax ) + { + int bApproxOK; + if( !GDALPipeRead(p, &bApproxOK) ) + break; + double adfMinMax[2]; + CPLErr eErr = poBand->ComputeRasterMinMax(bApproxOK, adfMinMax); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, adfMinMax[0]); + GDALPipeWrite(p, adfMinMax[1]); + } + } + else if( instr == INSTR_Band_GetHistogram ) + { + double dfMin, dfMax; + int nBuckets, bIncludeOutOfRange, bApproxOK; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &nBuckets) || + !GDALPipeRead(p, &bIncludeOutOfRange) || + !GDALPipeRead(p, &bApproxOK) ) + break; + int* panHistogram = (int*) VSIMalloc2(sizeof(int), nBuckets); + if( panHistogram == NULL ) + break; + CPLErr eErr = poBand->GetHistogram(dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, nBuckets * sizeof(int), panHistogram); + } + CPLFree(panHistogram); + } + else if( instr == INSTR_Band_GetDefaultHistogram ) + { + double dfMin, dfMax; + int nBuckets; + int bForce; + if( !GDALPipeRead(p, &bForce) ) + break; + int* panHistogram = NULL; + CPLErr eErr = poBand->GetDefaultHistogram(&dfMin, &dfMax, + &nBuckets, &panHistogram, + bForce, NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, dfMin); + GDALPipeWrite(p, dfMax); + GDALPipeWrite(p, nBuckets); + GDALPipeWrite(p, nBuckets * sizeof(int) , panHistogram); + } + CPLFree(panHistogram); + } + else if( instr == INSTR_Band_SetDefaultHistogram ) + { + double dfMin, dfMax; + int nBuckets; + int* panHistogram = NULL; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &nBuckets) || + !GDALPipeRead(p, nBuckets, &panHistogram) ) + { + CPLFree(panHistogram); + break; + } + CPLErr eErr = poBand->SetDefaultHistogram(dfMin, dfMax, + nBuckets, panHistogram); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(panHistogram); + } + else if( instr == INSTR_Band_HasArbitraryOverviews ) + { + int nVal = poBand->HasArbitraryOverviews(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nVal); + } + else if( instr == INSTR_Band_GetOverviewCount ) + { + int nVal = poBand->GetOverviewCount(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nVal); + } + else if( instr == INSTR_Band_GetOverview ) + { + int iOvr; + if( !GDALPipeRead(p, &iOvr) ) + break; + GDALRasterBand* poOvrBand = poBand->GetOverview(iOvr); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, aBands, poOvrBand); + } + else if( instr == INSTR_Band_GetMaskBand ) + { + GDALRasterBand* poMaskBand = poBand->GetMaskBand(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, aBands, poMaskBand); + } + else if( instr == INSTR_Band_GetMaskFlags ) + { + int nVal = poBand->GetMaskFlags(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nVal); + } + else if( instr == INSTR_Band_CreateMaskBand ) + { + int nFlags; + if( !GDALPipeRead(p, &nFlags) ) + break; + CPLErr eErr = poBand->CreateMaskBand(nFlags); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_Fill ) + { + double dfReal, dfImag; + if( !GDALPipeRead(p, &dfReal) || + !GDALPipeRead(p, &dfImag) ) + break; + CPLErr eErr = poBand->Fill(dfReal, dfImag); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetColorTable ) + { + GDALColorTable* poColorTable = poBand->GetColorTable(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poColorTable); + } + else if( instr == INSTR_Band_SetColorTable ) + { + GDALColorTable* poColorTable = NULL; + if( !GDALPipeRead(p, &poColorTable) ) + break; + CPLErr eErr = poBand->SetColorTable(poColorTable); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + delete poColorTable; + } + else if( instr == INSTR_Band_GetUnitType ) + { + const char* pszVal = poBand->GetUnitType(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + } + else if( instr == INSTR_Band_SetUnitType ) + { + char* pszUnitType = NULL; + if( !GDALPipeRead(p, &pszUnitType) ) + break; + CPLErr eErr = poBand->SetUnitType(pszUnitType); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszUnitType); + } + else if( instr == INSTR_Band_BuildOverviews ) + { + char* pszResampling = NULL; + int nOverviews; + int* panOverviewList = NULL; + if( !GDALPipeRead(p, &pszResampling) || + !GDALPipeRead(p, &nOverviews) || + !GDALPipeRead(p, nOverviews, &panOverviewList) ) + { + CPLFree(pszResampling); + CPLFree(panOverviewList); + break; + } + CPLErr eErr = poBand->BuildOverviews(pszResampling, nOverviews, + panOverviewList, NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszResampling); + CPLFree(panOverviewList); + } + else if( instr == INSTR_Band_GetDefaultRAT ) + { + const GDALRasterAttributeTable* poRAT = poBand->GetDefaultRAT(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poRAT); + } + else if( instr == INSTR_Band_SetDefaultRAT ) + { + GDALRasterAttributeTable* poRAT = NULL; + if( !GDALPipeRead(p, &poRAT) ) + break; + CPLErr eErr = poBand->SetDefaultRAT(poRAT); + delete poRAT; + + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_AdviseRead ) + { + int nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize; + int nDT; + char** papszOptions = NULL; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nDT) || + !GDALPipeRead(p, &papszOptions) ) + break; + CPLErr eErr = poBand->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + (GDALDataType)nDT, papszOptions); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszOptions); + } + + if( poSrcDS == NULL ) + { + GDALPipeWrite(p, (int)aoErrors.size()); + for(int i=0;i<(int)aoErrors.size();i++) + { + GDALPipeWrite(p, aoErrors[i].eErr); + GDALPipeWrite(p, aoErrors[i].nErrNo); + GDALPipeWrite(p, aoErrors[i].osErrorMsg); + } + aoErrors.resize(0); + } + else + GDALPipeWrite(p, 0); + } + + if( poSrcDS == NULL ) + CPLPopErrorHandler(); + + CPLSetThreadLocalConfigOption("GDAL_API_PROXY", pszOldValDup); + CPLFree(pszOldValDup); + + // fprintf(stderr, "[%d] finished = %d\n", (int)getpid(), nRet); + + if( poSrcDS == NULL && poDS != NULL ) + GDALClose((GDALDatasetH)poDS); + + CPLFree(pBuffer); + + CPLFree(asyncp.pszProgressMsg); + if( asyncp.hMutex ) + CPLDestroyMutex(asyncp.hMutex); + + return nRet; +} + +/************************************************************************/ +/* GDALServerLoop() */ +/************************************************************************/ + +int GDALServerLoop(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout) +{ +#ifndef WIN32 + unsetenv("CPL_SHOW_MEM_STATS"); +#endif + CPLSetConfigOption("GDAL_API_PROXY", "NO"); + + GDALPipe* p = GDALPipeBuild(fin, fout); + + int nRet = GDALServerLoop(p, NULL, NULL, NULL); + + GDALPipeFree(p); + + return nRet; +} + +/************************************************************************/ +/* GDALServerLoopSocket() */ +/************************************************************************/ + +int GDALServerLoopSocket(CPL_SOCKET nSocket) +{ +#ifndef WIN32 + unsetenv("CPL_SHOW_MEM_STATS"); +#endif + CPLSetConfigOption("GDAL_API_PROXY", "NO"); + + GDALPipe* p = GDALPipeBuild(nSocket); + + int nRet = GDALServerLoop(p, NULL, NULL, NULL); + + GDALPipeFree(p); + + return nRet; +} + +/************************************************************************/ +/* GDALClientDataset() */ +/************************************************************************/ + +GDALClientDataset::GDALClientDataset(GDALServerSpawnedProcess* ssp) +{ + this->ssp = ssp; + this->p = ssp->p; + bFreeDriver = FALSE; + nGCPCount = 0; + pasGCPs = NULL; + async = NULL; + memset(abyCaps, 0, sizeof(abyCaps)); +} + +/************************************************************************/ +/* GDALClientDataset() */ +/************************************************************************/ + +GDALClientDataset::GDALClientDataset(GDALPipe* p) +{ + this->ssp = NULL; + this->p = p; + bFreeDriver = FALSE; + nGCPCount = 0; + pasGCPs = NULL; + async = NULL; + memset(abyCaps, 0, sizeof(abyCaps)); +} +/************************************************************************/ +/* ~GDALClientDataset() */ +/************************************************************************/ + +GDALClientDataset::~GDALClientDataset() +{ + FlushCache(); + + ProcessAsyncProgress(); + + std::map::iterator oIter = aoMapMetadata.begin(); + for( ; oIter != aoMapMetadata.end(); ++oIter ) + CSLDestroy(oIter->second); + + std::map< std::pair, char*>::iterator oIterItem = + aoMapMetadataItem.begin(); + for( ; oIterItem != aoMapMetadataItem.end(); ++oIterItem ) + CPLFree(oIterItem->second); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs(nGCPCount, pasGCPs); + CPLFree(pasGCPs); + } + + if( ssp != NULL ) + GDALServerSpawnAsyncFinish(ssp); + if( bFreeDriver ) + delete poDriver; +} + +/************************************************************************/ +/* ProcessAsyncProgress() */ +/************************************************************************/ + +int GDALClientDataset::ProcessAsyncProgress() +{ + if( !async ) return TRUE; + CPLMutexHolderD(&(async->hMutex)); + if( !async->bUpdated ) return async->bRet; + async->bUpdated = FALSE; + if( !GDALPipeWrite(p, INSTR_Progress) || + !GDALPipeWrite(p, async->dfComplete) || + !GDALPipeWrite(p, async->pszProgressMsg) ) + return TRUE; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return TRUE; + + int bRet = TRUE; + if( !GDALPipeRead(p, &bRet) ) + return TRUE; + async->bRet = bRet; + GDALConsumeErrors(p); + return bRet; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr GDALClientDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + if( !SupportsInstr(INSTR_IBuildOverviews) ) + return GDALPamDataset::IBuildOverviews(pszResampling, nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( nOverviews < 0 || nOverviews > 1000 || + nListBands < 0 || nListBands > GetRasterCount() ) + return CE_Failure; + + GDALPipeWriteConfigOption(p, "BIGTIFF_OVERVIEW"); + GDALPipeWriteConfigOption(p, "COMPRESS_OVERVIEW"); + GDALPipeWriteConfigOption(p, "PREDICTOR_OVERVIEW"); + GDALPipeWriteConfigOption(p, "JPEG_QUALITY_OVERVIEW"); + GDALPipeWriteConfigOption(p, "PHOTOMETRIC_OVERVIEW"); + GDALPipeWriteConfigOption(p, "USE_RRD"); + GDALPipeWriteConfigOption(p, "HFA_USE_RRD"); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_OVR_BLOCKSIZE"); + GDALPipeWriteConfigOption(p, "GTIFF_DONT_WRITE_BLOCKS"); + + if( !GDALPipeWrite(p, INSTR_IBuildOverviews) || + !GDALPipeWrite(p, pszResampling) || + !GDALPipeWrite(p, nOverviews) || + !GDALPipeWrite(p, nOverviews * sizeof(int), panOverviewList) || + !GDALPipeWrite(p, nListBands) || + !GDALPipeWrite(p, nListBands * sizeof(int), panBandList) ) + return CE_Failure; + + if( GDALServerLoop(p, NULL, pfnProgress, pProgressData) != 0 ) + { + GDALConsumeErrors(p); + return CE_Failure; + } + + GDALConsumeErrors(p); + + for(int i=0; iClearOverviewCache(); + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GDALClientDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace) +{ + if( !SupportsInstr(( eRWFlag == GF_Read ) ? INSTR_IRasterIO_Read : INSTR_IRasterIO_Write ) ) + return GDALPamDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + + CLIENT_ENTER(); + CPLErr eRet = CE_Failure; + + ProcessAsyncProgress(); + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + int bDirectCopy; + if( nPixelSpace == nDataTypeSize && + nLineSpace == nBufXSize * nDataTypeSize && + (nBandSpace == nBufYSize * nLineSpace || + (nBandSpace == 0 && nBandCount == 1)) ) + { + bDirectCopy = TRUE; + } + else if( nBandCount > 1 && + nPixelSpace == nBandCount * nDataTypeSize && + nLineSpace == nBufXSize * nPixelSpace && + nBandSpace == nBandCount ) + { + bDirectCopy = TRUE; + } + else + bDirectCopy = FALSE; + + if( eRWFlag == GF_Write ) + { + for(int i=0;iInvalidateCachedLines(); + } + + if( !GDALPipeWrite(p, ( eRWFlag == GF_Read ) ? INSTR_IRasterIO_Read : INSTR_IRasterIO_Write ) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eBufType) || + !GDALPipeWrite(p, nBandCount) || + !GDALPipeWrite(p, nBandCount * sizeof(int), panBandMap) ) + return CE_Failure; + + if( bDirectCopy ) + { + if( !GDALPipeWrite(p, nPixelSpace) || + !GDALPipeWrite(p, nLineSpace) || + !GDALPipeWrite(p, nBandSpace) ) + return CE_Failure; + } + else + { + if( !GDALPipeWrite(p, 0) || + !GDALPipeWrite(p, 0) || + !GDALPipeWrite(p, 0) ) + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + int nSize; + if( !GDALPipeRead(p, &nSize) ) + return CE_Failure; + GIntBig nExpectedSize = (GIntBig)nBufXSize * nBufYSize * nBandCount * nDataTypeSize; + if( nSize != nExpectedSize ) + return CE_Failure; + if( bDirectCopy ) + { + if( !GDALPipeRead_nolength(p, nSize, pData) ) + return CE_Failure; + } + else + { + GByte* pBuf = (GByte*)VSIMalloc(nSize); + if( pBuf == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, pBuf) ) + { + VSIFree(pBuf); + return CE_Failure; + } + for(int iBand=0;iBand 0 ) + { + GDALDeinitGCPs(nGCPCount, pasGCPs); + CPLFree(pasGCPs); + pasGCPs = NULL; + } + nGCPCount = 0; + + if( !GDALPipeRead(p, &nGCPCount, &pasGCPs) ) + return NULL; + + GDALConsumeErrors(p); + return pasGCPs; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr GDALClientDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) +{ + if( !SupportsInstr(INSTR_SetGCPs) ) + return GDALPamDataset::SetGCPs(nGCPCount, pasGCPList, pszGCPProjection); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_SetGCPs) || + !GDALPipeWrite(p, nGCPCount, pasGCPList) || + !GDALPipeWrite(p, pszGCPProjection) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** GDALClientDataset::GetFileList() +{ + if( !SupportsInstr(INSTR_GetFileList) ) + return GDALPamDataset::GetFileList(); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_GetFileList) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char** papszFileList = NULL; + if( !GDALPipeRead(p, &papszFileList) ) + return NULL; + GDALConsumeErrors(p); + + /* If server is Windows and client is Unix, then replace backslahes */ + /* by slashes */ +#ifndef WIN32 + char** papszIter = papszFileList; + while( papszIter != NULL && *papszIter != NULL ) + { + char* pszIter = *papszIter; + char* pszBackSlash; + while( (pszBackSlash = strchr(pszIter, '\\')) != NULL ) + { + *pszBackSlash = '/'; + pszIter = pszBackSlash + 1; + } + papszIter ++; + } +#endif + + return papszFileList; +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** GDALClientDataset::GetMetadata( const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_GetMetadata) ) + return GDALPamDataset::GetMetadata(pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::map::iterator oIter = aoMapMetadata.find(CPLString(pszDomain)); + if( oIter != aoMapMetadata.end() ) + { + CSLDestroy(oIter->second); + aoMapMetadata.erase(oIter); + } + if( !GDALPipeWrite(p, INSTR_GetMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char** papszMD = NULL; + if( !GDALPipeRead(p, &papszMD) ) + return NULL; + aoMapMetadata[pszDomain] = papszMD; + GDALConsumeErrors(p); + return papszMD; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +/*void GDALClientDataset::SetDescription( const char * pszDescription ) +{ + sDescription = pszDescription; + if( !GDALPipeWrite(p, INSTR_SetDescription) || + !GDALPipeWrite(p, pszDescription) || + !GDALSkipUntilEndOfJunkMarker(p)) + return; + GDALConsumeErrors(p); +}*/ + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char* GDALClientDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_GetMetadataItem) ) + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::pair oPair = + std::pair (CPLString(pszDomain), CPLString(pszName)); + std::map< std::pair, char*>::iterator oIter = + aoMapMetadataItem.find(oPair); + if( oIter != aoMapMetadataItem.end() ) + { + CPLFree(oIter->second); + aoMapMetadataItem.erase(oIter); + } + if( !GDALPipeWrite(p, INSTR_GetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char* pszItem = NULL; + if( !GDALPipeRead(p, &pszItem) ) + return NULL; + aoMapMetadataItem[oPair] = pszItem; + GDALConsumeErrors(p); + return pszItem; +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +CPLErr GDALClientDataset::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_SetMetadata) ) + return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_SetMetadata) || + !GDALPipeWrite(p, papszMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALClientDataset::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_SetMetadataItem) ) + return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_SetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszValue) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void GDALClientDataset::FlushCache() +{ + if( !SupportsInstr(INSTR_FlushCache) ) + { + GDALPamDataset::FlushCache(); + return; + } + + for(int i=0;iInvalidateCachedLines(); + + CLIENT_ENTER(); + SetPamFlags(0); + GDALPamDataset::FlushCache(); + if( !GDALPipeWrite(p, INSTR_FlushCache) || + !GDALSkipUntilEndOfJunkMarker(p) ) + return; + GDALConsumeErrors(p); +} + +/************************************************************************/ +/* AddBand() */ +/************************************************************************/ + +CPLErr GDALClientDataset::AddBand( GDALDataType eType, + char **papszOptions ) +{ + if( !SupportsInstr(INSTR_AddBand) ) + return GDALPamDataset::AddBand(eType, papszOptions); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_AddBand) || + !GDALPipeWrite(p, eType) || + !GDALPipeWrite(p, papszOptions) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet == CE_None ) + { + GDALRasterBand* poBand = NULL; + if( !GDALPipeRead(p, this, &poBand, abyCaps) ) + return CE_Failure; + SetBand(GetRasterCount() + 1, poBand); + } + GDALConsumeErrors(p); + return eRet; +} + + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr GDALClientDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ) +{ + if( !SupportsInstr(INSTR_AdviseRead) ) + return GDALPamDataset::AdviseRead(nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, nBandCount, panBandList, + papszOptions); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_AdviseRead) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eDT) || + !GDALPipeWrite(p, nBandCount) || + !GDALPipeWrite(p, panBandList ? nBandCount * sizeof(int) : 0, panBandList) || + !GDALPipeWrite(p, papszOptions) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GDALClientDataset::CreateMaskBand( int nFlags ) +{ + if( !SupportsInstr(INSTR_CreateMaskBand) ) + return GDALPamDataset::CreateMaskBand(nFlags); + + CLIENT_ENTER(); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK", bRecycleChild); + if( !GDALPipeWrite(p, INSTR_CreateMaskBand) || + !GDALPipeWrite(p, nFlags) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GDALClientRasterBand() */ +/************************************************************************/ + +GDALClientRasterBand::GDALClientRasterBand(GDALPipe* p, int iSrvBand, + GDALClientDataset* poDS, + int nBand, GDALAccess eAccess, + int nRasterXSize, int nRasterYSize, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize, + GByte abyCapsIn[16]) +{ + this->p = p; + this->iSrvBand = iSrvBand; + this->poDS = poDS; + this->nBand = nBand; + this->eAccess = eAccess; + this->nRasterXSize = nRasterXSize; + this->nRasterYSize = nRasterYSize; + this->eDataType = eDataType; + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + papszCategoryNames = NULL; + poColorTable = NULL; + pszUnitType = NULL; + poMaskBand = NULL; + poRAT = NULL; + memcpy(abyCaps, abyCapsIn, sizeof(abyCaps)); + bEnableLineCaching = CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_LINE_CACHING", "YES")); + nSuccessiveLinesRead = 0; + eLastBufType = GDT_Unknown; + nLastYOff = -1; + pabyCachedLines = NULL; + eCachedBufType = GDT_Unknown; + nCachedYStart = -1; + nCachedLines = 0; + +} + +/************************************************************************/ +/* ~GDALClientRasterBand() */ +/************************************************************************/ + +GDALClientRasterBand::~GDALClientRasterBand() +{ + CSLDestroy(papszCategoryNames); + delete poColorTable; + CPLFree(pszUnitType); + delete poMaskBand; + delete poRAT; + CPLFree(pabyCachedLines); + + std::map::iterator oIter = aMapOvrBands.begin(); + for( ; oIter != aMapOvrBands.end(); ++oIter ) + delete oIter->second; + + std::map< std::pair, char*>::iterator oIterItem = + aoMapMetadataItem.begin(); + for( ; oIterItem != aoMapMetadataItem.end(); ++oIterItem ) + CPLFree(oIterItem->second); + + std::map::iterator oIterMD = aoMapMetadata.begin(); + for( ; oIterMD != aoMapMetadata.end(); ++oIterMD ) + CSLDestroy(oIterMD->second); + + for(int i=0; i < (int)apoOldMaskBands.size(); i++) + delete apoOldMaskBands[i]; +} + +/************************************************************************/ +/* CreateFakeMaskBand() */ +/************************************************************************/ + +GDALRasterBand* GDALClientRasterBand::CreateFakeMaskBand() +{ + if( poMaskBand == NULL ) + poMaskBand = new GDALAllValidMaskBand(this); + return poMaskBand; +} + +/************************************************************************/ +/* WriteInstr() */ +/************************************************************************/ + +int GDALClientRasterBand::WriteInstr(InstrEnum instr) +{ + return GDALPipeWrite(p, instr) && + GDALPipeWrite(p, iSrvBand); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::FlushCache() +{ + if( !SupportsInstr(INSTR_Band_FlushCache) ) + return GDALPamRasterBand::FlushCache(); + + InvalidateCachedLines(); + + CLIENT_ENTER(); + CPLErr eErr = GDALPamRasterBand::FlushCache(); + if( eErr == CE_None ) + { + if( !WriteInstr(INSTR_Band_FlushCache) ) + return CE_Failure; + return CPLErrOnlyRet(p); + } + return eErr; +} +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char ** GDALClientRasterBand::GetCategoryNames() +{ + if( !SupportsInstr(INSTR_Band_GetCategoryNames) ) + return GDALPamRasterBand::GetCategoryNames(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetCategoryNames) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + CSLDestroy(papszCategoryNames); + papszCategoryNames = NULL; + if( !GDALPipeRead(p, &papszCategoryNames) ) + return NULL; + GDALConsumeErrors(p); + return papszCategoryNames; +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetCategoryNames( char ** papszCategoryNames ) +{ + if( !SupportsInstr(INSTR_Band_SetCategoryNames) ) + return GDALPamRasterBand::SetCategoryNames(papszCategoryNames); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetCategoryNames) || + !GDALPipeWrite(p, papszCategoryNames) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void GDALClientRasterBand::SetDescription( const char * pszDescription ) +{ + if( !SupportsInstr(INSTR_Band_SetDescription) ) + { + GDALPamRasterBand::SetDescription(pszDescription); + return; + } + + CLIENT_ENTER(); + sDescription = pszDescription; + if( !WriteInstr(INSTR_Band_SetDescription) || + !GDALPipeWrite(p, pszDescription) || + !GDALSkipUntilEndOfJunkMarker(p)) + return; + GDALConsumeErrors(p); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** GDALClientRasterBand::GetMetadata( const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_GetMetadata) ) + return GDALPamRasterBand::GetMetadata(pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::map::iterator oIter = aoMapMetadata.find(CPLString(pszDomain)); + if( oIter != aoMapMetadata.end() ) + { + CSLDestroy(oIter->second); + aoMapMetadata.erase(oIter); + } + if( !WriteInstr(INSTR_Band_GetMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char** papszMD = NULL; + if( !GDALPipeRead(p, &papszMD) ) + return NULL; + aoMapMetadata[pszDomain] = papszMD; + GDALConsumeErrors(p); + return papszMD; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char* GDALClientRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_GetMetadataItem) ) + return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::pair oPair = + std::pair (CPLString(pszDomain), CPLString(pszName)); + std::map< std::pair, char*>::iterator oIter = + aoMapMetadataItem.find(oPair); + if( oIter != aoMapMetadataItem.end() ) + { + CPLFree(oIter->second); + aoMapMetadataItem.erase(oIter); + } + if( !WriteInstr(INSTR_Band_GetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char* pszItem = NULL; + if( !GDALPipeRead(p, &pszItem) ) + return NULL; + aoMapMetadataItem[oPair] = pszItem; + GDALConsumeErrors(p); + return pszItem; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_SetMetadata) ) + return GDALPamRasterBand::SetMetadata(papszMetadata, pszDomain); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetMetadata) || + !GDALPipeWrite(p, papszMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_SetMetadataItem) ) + return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszValue) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GDALClientRasterBand::GetColorInterpretation() +{ + if( !SupportsInstr(INSTR_Band_GetColorInterpretation) ) + return GDALPamRasterBand::GetColorInterpretation(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetColorInterpretation) ) + return GCI_Undefined; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return GCI_Undefined; + + int nInt; + if( !GDALPipeRead(p, &nInt) ) + return GCI_Undefined; + GDALConsumeErrors(p); + return (GDALColorInterp)nInt; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetColorInterpretation(GDALColorInterp eInterp) +{ + if( !SupportsInstr(INSTR_Band_SetColorInterpretation) ) + return GDALPamRasterBand::SetColorInterpretation(eInterp); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetColorInterpretation) || + !GDALPipeWrite(p, eInterp) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) +{ + if( !SupportsInstr(INSTR_Band_GetStatistics) ) + return GDALPamRasterBand::GetStatistics( + bApproxOK, bForce, pdfMin, pdfMax, pdfMean, pdfStdDev); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + CPLErr eDefaultRet = CE_Failure; + if( CSLTestBoolean(CPLGetConfigOption("QGIS_HACK", "NO")) ) + { + if( pdfMin ) *pdfMin = 0; + if( pdfMax ) *pdfMax = 255; + if( pdfMean ) *pdfMean = 0; + if( pdfStdDev ) *pdfStdDev = 0; + eDefaultRet = CE_None; + } + if( !WriteInstr( INSTR_Band_GetStatistics) || + !GDALPipeWrite(p, bApproxOK) || + !GDALPipeWrite(p, bForce) ) + return eDefaultRet; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return eDefaultRet; + + CPLErr eRet = eDefaultRet; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet == CE_None ) + { + double dfMin, dfMax, dfMean, dfStdDev; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &dfMean) || + !GDALPipeRead(p, &dfStdDev) ) + return eDefaultRet; + if( pdfMin ) *pdfMin = dfMin; + if( pdfMax ) *pdfMax = dfMax; + if( pdfMean ) *pdfMean = dfMean; + if( pdfStdDev ) *pdfStdDev = dfStdDev; + } + else if( eDefaultRet == CE_None ) + eRet = eDefaultRet; + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::ComputeStatistics( int bApproxOK, + double *pdfMin, + double *pdfMax, + double *pdfMean, + double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_ComputeStatistics) ) + return GDALPamRasterBand::ComputeStatistics( + bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + if( !WriteInstr(INSTR_Band_ComputeStatistics) || + !GDALPipeWrite(p, bApproxOK) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + double dfMin, dfMax, dfMean, dfStdDev; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &dfMean) || + !GDALPipeRead(p, &dfStdDev) ) + return CE_Failure; + if( pdfMin ) *pdfMin = dfMin; + if( pdfMax ) *pdfMax = dfMax; + if( pdfMean ) *pdfMean = dfMean; + if( pdfStdDev ) *pdfStdDev = dfStdDev; + } + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ) +{ + if( !SupportsInstr(INSTR_Band_SetStatistics) ) + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetStatistics) || + !GDALPipeWrite(p, dfMin) || + !GDALPipeWrite(p, dfMax) || + !GDALPipeWrite(p, dfMean) || + !GDALPipeWrite(p, dfStdDev) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::ComputeRasterMinMax( int bApproxOK, + double* padfMinMax ) +{ + if( !SupportsInstr(INSTR_Band_ComputeRasterMinMax) ) + return GDALPamRasterBand::ComputeRasterMinMax(bApproxOK, padfMinMax); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + if( !WriteInstr(INSTR_Band_ComputeRasterMinMax) || + !GDALPipeWrite(p, bApproxOK) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + if( !GDALPipeRead(p, padfMinMax + 0) || + !GDALPipeRead(p, padfMinMax + 1) ) + return CE_Failure; + } + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, + int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_GetHistogram) ) + return GDALPamRasterBand::GetHistogram( + dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + CPLErr eDefaultRet = CE_Failure; + if( CSLTestBoolean(CPLGetConfigOption("QGIS_HACK", "NO")) ) + { + memset(panHistogram, 0, sizeof(int) * nBuckets); + eDefaultRet = CE_None; + } + if( !WriteInstr(INSTR_Band_GetHistogram) || + !GDALPipeWrite(p, dfMin) || + !GDALPipeWrite(p, dfMax) || + !GDALPipeWrite(p, nBuckets) || + !GDALPipeWrite(p, bIncludeOutOfRange) || + !GDALPipeWrite(p, bApproxOK) ) + return eDefaultRet; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return eDefaultRet; + + CPLErr eRet = eDefaultRet; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + int nSize; + if( !GDALPipeRead(p, &nSize) || + nSize != nBuckets * (int)sizeof(int) || + !GDALPipeRead_nolength(p, nSize, panHistogram) ) + return eDefaultRet; + } + else if( eDefaultRet == CE_None ) + eRet = eDefaultRet; + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::GetDefaultHistogram( double *pdfMin, + double *pdfMax, + int *pnBuckets, + int ** ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_GetDefaultHistogram) ) + return GDALPamRasterBand::GetDefaultHistogram( + pdfMin, pdfMax, pnBuckets, ppanHistogram, bForce, pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetDefaultHistogram) || + !GDALPipeWrite(p, bForce) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + double dfMin, dfMax; + int nBuckets, nSize; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &nBuckets) || + !GDALPipeRead(p, &nSize) ) + return CE_Failure; + if( nSize != nBuckets * (int)sizeof(int) ) + return CE_Failure; + if( pdfMin ) *pdfMin = dfMin; + if( pdfMax ) *pdfMax = dfMax; + if( pnBuckets ) *pnBuckets = nBuckets; + if( ppanHistogram ) + { + *ppanHistogram = (int*)VSIMalloc(nSize); + if( *ppanHistogram == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, *ppanHistogram) ) + return CE_Failure; + } + else + { + int *panHistogram = (int*)VSIMalloc(nSize); + if( panHistogram == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, panHistogram) ) + { + CPLFree(panHistogram); + return CE_Failure; + } + CPLFree(panHistogram); + } + } + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram ) +{ + if( !SupportsInstr(INSTR_Band_SetDefaultHistogram) ) + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetDefaultHistogram) || + !GDALPipeWrite(p, dfMin) || + !GDALPipeWrite(p, dfMax) || + !GDALPipeWrite(p, nBuckets) || + !GDALPipeWrite(p, nBuckets * sizeof(int), panHistogram) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void* pImage) +{ + if( !SupportsInstr(INSTR_Band_IReadBlock) ) + return CE_Failure; + + CLIENT_ENTER(); + if( poDS != NULL ) + ((GDALClientDataset*)poDS)->ProcessAsyncProgress(); + + if( !WriteInstr(INSTR_Band_IReadBlock) || + !GDALPipeWrite(p, nBlockXOff) || + !GDALPipeWrite(p, nBlockYOff) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + int nSize; + if( !GDALPipeRead(p, &nSize) || + nSize != nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8) || + !GDALPipeRead_nolength(p, nSize, pImage) ) + return CE_Failure; + + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void* pImage) +{ + if( !SupportsInstr(INSTR_Band_IWriteBlock) ) + return CE_Failure; + + InvalidateCachedLines(); + + CLIENT_ENTER(); + int nSize = nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + if( !WriteInstr(INSTR_Band_IWriteBlock) || + !GDALPipeWrite(p, nBlockXOff) || + !GDALPipeWrite(p, nBlockYOff) || + !GDALPipeWrite(p, nSize, pImage) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* IRasterIO_read_internal() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::IRasterIO_read_internal( + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ) +{ + CPLErr eRet = CE_Failure; + + if( !WriteInstr(INSTR_Band_IRasterIO_Read) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eBufType) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + if( !GDALPipeRead(p, &eRet) ) + return eRet; + + int nSize; + if( !GDALPipeRead(p, &nSize) ) + return CE_Failure; + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + GIntBig nExpectedSize = (GIntBig)nBufXSize * nBufYSize * nDataTypeSize; + if( nSize != nExpectedSize ) + return CE_Failure; + if( nPixelSpace == nDataTypeSize && + nLineSpace == nBufXSize * nDataTypeSize ) + { + if( !GDALPipeRead_nolength(p, nSize, pData) ) + return CE_Failure; + } + else + { + GByte* pBuf = (GByte*)VSIMalloc(nSize); + if( pBuf == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, pBuf) ) + { + VSIFree(pBuf); + return CE_Failure; + } + for(int j=0;jProcessAsyncProgress(); + + if( eRWFlag == GF_Read ) + { + /*if( GetAccess() == GA_Update ) + FlushCache();*/ + + /* Detect scanline reading pattern and read several rows in advance */ + /* to save a few client/server roundtrips */ + if( bEnableLineCaching && + nXOff == 0 && nXSize == nRasterXSize && nYSize == 1 && + nBufXSize == nXSize && nBufYSize == nYSize ) + { + int nBufTypeSize = GDALGetDataTypeSize(eBufType) / 8; + + /* Is the current line already cached ? */ + if( nCachedYStart >= 0 && + nYOff >= nCachedYStart && nYOff < nCachedYStart + nCachedLines && + eBufType == eCachedBufType ) + { + nSuccessiveLinesRead ++; + + int nCachedBufTypeSize = GDALGetDataTypeSize(eCachedBufType) / 8; + GDALCopyWords(pabyCachedLines + (nYOff - nCachedYStart) * nXSize * nCachedBufTypeSize, + eCachedBufType, nCachedBufTypeSize, + pData, eBufType, nPixelSpace, + nXSize); + nLastYOff = nYOff; + eLastBufType = eBufType; + return CE_None; + } + + if( nYOff == nLastYOff + 1 && + eBufType == eLastBufType ) + { + nSuccessiveLinesRead ++; + if( nSuccessiveLinesRead >= 2 ) + { + if( pabyCachedLines == NULL ) + { + nCachedLines = 10 * 1024 * 1024 / (nXSize * nBufTypeSize); + if( nCachedLines > 1 ) + pabyCachedLines = (GByte*) VSIMalloc( + nCachedLines * nXSize * nBufTypeSize); + } + if( pabyCachedLines != NULL ) + { + int nLinesToRead = nCachedLines; + if( nYOff + nLinesToRead > nRasterYSize ) + nLinesToRead = nRasterYSize - nYOff; + eRet = IRasterIO_read_internal( nXOff, nYOff, nXSize, nLinesToRead, + pabyCachedLines, nXSize, nLinesToRead, + eBufType, + nBufTypeSize, nBufTypeSize * nXSize ); + if( eRet == CE_None ) + { + eCachedBufType = eBufType; + nCachedYStart = nYOff; + + int nCachedBufTypeSize = GDALGetDataTypeSize(eCachedBufType) / 8; + GDALCopyWords(pabyCachedLines + (nYOff - nCachedYStart) * nXSize * nCachedBufTypeSize, + eCachedBufType, nCachedBufTypeSize, + pData, eBufType, nPixelSpace, + nXSize); + nLastYOff = nYOff; + eLastBufType = eBufType; + + return CE_None; + } + else + InvalidateCachedLines(); + } + } + } + else + InvalidateCachedLines(); + } + else + InvalidateCachedLines(); + + nLastYOff = nYOff; + eLastBufType = eBufType; + + return IRasterIO_read_internal( nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace ); + } + else + { + InvalidateCachedLines(); + + if( !WriteInstr(INSTR_Band_IRasterIO_Write) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eBufType) ) + return CE_Failure; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + GIntBig nSizeBig = (GIntBig)nBufXSize * nBufYSize * nDataTypeSize; + int nSize = (int)nSizeBig; + if( nSizeBig != nSize ) + return CE_Failure; + if( nPixelSpace == nDataTypeSize && + nLineSpace == nBufXSize * nDataTypeSize ) + { + if( !GDALPipeWrite(p, nSize, pData) ) + return CE_Failure; + } + else + { + GByte* pBuf = (GByte*)VSIMalloc(nSize); + if( pBuf == NULL ) + return CE_Failure; + for(int j=0;j::iterator oIter = + aMapOvrBandsCurrent.find(iOverview); + if( oIter != aMapOvrBandsCurrent.end() ) + return oIter->second; + + if( !WriteInstr(INSTR_Band_GetOverview) || + !GDALPipeWrite(p, iOverview) ) + return NULL; + + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + GDALRasterBand* poBand = NULL; + if( !GDALPipeRead(p, (GDALClientDataset*) NULL, &poBand, abyCaps) ) + return NULL; + + GDALConsumeErrors(p); + + aMapOvrBands[iOverview] = poBand; + aMapOvrBandsCurrent[iOverview] = poBand; + return poBand; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GDALClientRasterBand::GetMaskBand() +{ + if( !SupportsInstr(INSTR_Band_GetMaskBand) ) + return GDALPamRasterBand::GetMaskBand(); + + CLIENT_ENTER(); + if( poMaskBand ) + return poMaskBand; + + if( !WriteInstr(INSTR_Band_GetMaskBand) ) + return CreateFakeMaskBand(); + + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CreateFakeMaskBand(); + + GDALRasterBand* poBand = NULL; + if( !GDALPipeRead(p, (GDALClientDataset*) NULL, &poBand, abyCaps) ) + return CreateFakeMaskBand(); + + GDALConsumeErrors(p); + + poMaskBand = poBand; + return poMaskBand; +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GDALClientRasterBand::GetMaskFlags() +{ + if( !SupportsInstr(INSTR_Band_GetMaskFlags) ) + return GDALPamRasterBand::GetMaskFlags(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetMaskFlags) ) + return 0; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return 0; + int nFlags; + if( !GDALPipeRead(p, &nFlags) ) + return 0; + GDALConsumeErrors(p); + return nFlags; +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::CreateMaskBand( int nFlags ) +{ + if( !SupportsInstr(INSTR_Band_CreateMaskBand) ) + return GDALPamRasterBand::CreateMaskBand(nFlags); + + CLIENT_ENTER(); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK", bRecycleChild); + if( !WriteInstr(INSTR_Band_CreateMaskBand) || + !GDALPipeWrite(p, nFlags) ) + return CE_Failure; + CPLErr eErr = CPLErrOnlyRet(p); + if( eErr == CE_None && poMaskBand != NULL ) + { + apoOldMaskBands.push_back(poMaskBand); + poMaskBand = NULL; + } + return eErr; +} + +/************************************************************************/ +/* Fill() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::Fill(double dfRealValue, double dfImaginaryValue) +{ + if( !SupportsInstr(INSTR_Band_Fill) ) + return GDALPamRasterBand::Fill(dfRealValue, dfImaginaryValue); + + InvalidateCachedLines(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_Fill) || + !GDALPipeWrite(p, dfRealValue) || + !GDALPipeWrite(p, dfImaginaryValue) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::BuildOverviews( const char * pszResampling, + int nOverviews, + int * panOverviewList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_BuildOverviews) ) + return GDALPamRasterBand::BuildOverviews(pszResampling, nOverviews, panOverviewList, + pfnProgress, pProgressData); + + InvalidateCachedLines(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_BuildOverviews) || + !GDALPipeWrite(p, pszResampling) || + !GDALPipeWrite(p, nOverviews) || + !GDALPipeWrite(p, nOverviews * sizeof(int), panOverviewList) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *GDALClientRasterBand::GetDefaultRAT() +{ + if( !SupportsInstr(INSTR_Band_GetDefaultRAT) ) + return GDALPamRasterBand::GetDefaultRAT(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetDefaultRAT) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + GDALRasterAttributeTable* poNewRAT = NULL; + if( !GDALPipeRead(p, &poNewRAT) ) + return NULL; + + if( poNewRAT != NULL && poRAT != NULL ) + { + *poRAT = *poNewRAT; + delete poNewRAT; + } + else if( poNewRAT != NULL && poRAT == NULL ) + { + poRAT = poNewRAT; + } + else if( poRAT != NULL ) + { + delete poRAT; + poRAT = NULL; + } + + GDALConsumeErrors(p); + return poRAT; +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetDefaultRAT( const GDALRasterAttributeTable * poRAT ) +{ + if( !SupportsInstr(INSTR_Band_SetDefaultRAT) ) + return GDALPamRasterBand::SetDefaultRAT(poRAT); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetDefaultRAT) || + !GDALPipeWrite(p, poRAT) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ) +{ + if( !SupportsInstr(INSTR_Band_AdviseRead) ) + return GDALPamRasterBand::AdviseRead(nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + eDT, papszOptions); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_AdviseRead) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eDT) || + !GDALPipeWrite(p, papszOptions) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* CreateAndConnect() */ +/************************************************************************/ + +GDALClientDataset* GDALClientDataset::CreateAndConnect() +{ + GDALServerSpawnedProcess* ssp = GDALServerSpawnAsync(); + if( ssp == NULL ) + return NULL; + return new GDALClientDataset(ssp); +} + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +int GDALClientDataset::Init(const char* pszFilename, GDALAccess eAccess) +{ + // FIXME find a way of transmitting the relevant config options to the forked Open() ? + GDALPipeWriteConfigOption(p, "GTIFF_POINT_GEO_IGNORE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_OVR_BLOCKSIZE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "GTIFF_LINEAR_UNITS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GTIFF_IGNORE_READ_ERRORS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_RENDERING_OPTIONS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_DPI", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_LIB", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_LAYERS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_LAYERS_OFF", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_JPEG_TO_RGB", bRecycleChild); + GDALPipeWriteConfigOption(p, "RPFTOC_FORCE_RGBA", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_NETCDF_BOTTOMUP", bRecycleChild); + GDALPipeWriteConfigOption(p, "OGR_SQLITE_SYNCHRONOUS", bRecycleChild); + + char* pszCWD = CPLGetCurrentDir(); + + if( !GDALPipeWrite(p, INSTR_Open) || + !GDALPipeWrite(p, eAccess) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD)) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return FALSE; + int bRet = FALSE; + if( !GDALPipeRead(p, &bRet) ) + return FALSE; + + if( bRet == FALSE ) + { + GDALConsumeErrors(p); + return FALSE; + } + + if( !GDALPipeRead(p, sizeof(abyCaps), abyCaps) ) + return FALSE; + + this->eAccess = eAccess; + + char* pszDescription = NULL; + if( !GDALPipeRead(p, &pszDescription) ) + return FALSE; + if( pszDescription != NULL ) + SetDescription(pszDescription); + CPLFree(pszDescription); + + char* pszDriverName = NULL; + if( !GDALPipeRead(p, &pszDriverName) ) + return FALSE; + + if( pszDriverName != NULL ) + { + bFreeDriver = TRUE; + poDriver = new GDALDriver(); + poDriver->SetDescription(pszDriverName); + CPLFree(pszDriverName); + pszDriverName = NULL; + + while(TRUE) + { + char* pszKey = NULL, *pszVal = NULL; + if( !GDALPipeRead(p, &pszKey) ) + return FALSE; + if( pszKey == NULL ) + break; + if( !GDALPipeRead(p, &pszVal) ) + { + CPLFree(pszKey); + CPLFree(pszVal); + return FALSE; + } + poDriver->SetMetadataItem( pszKey, pszVal ); + CPLFree(pszKey); + CPLFree(pszVal); + } + } + CPLFree(pszDriverName); + + int bAllSame; + if( !GDALPipeRead(p, &nRasterXSize) || + !GDALPipeRead(p, &nRasterYSize) || + !GDALPipeRead(p, &nBands) || + !GDALPipeRead(p, &bAllSame) ) + return FALSE; + + for(int i=0;i 0 && bAllSame ) + { + GDALClientRasterBand* poFirstBand = (GDALClientRasterBand*) GetRasterBand(1); + int nBlockXSize, nBlockYSize; + poFirstBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + poBand = new GDALClientRasterBand(p, poFirstBand->GetSrvBand() + i, + this, i + 1, poFirstBand->GetAccess(), + poFirstBand->GetXSize(), + poFirstBand->GetYSize(), + poFirstBand->GetRasterDataType(), + nBlockXSize, nBlockYSize, + abyCaps); + } + else + { + if( !GDALPipeRead(p, this, &poBand, abyCaps) ) + return FALSE; + if( poBand == NULL ) + return FALSE; + } + + SetBand(i+1, poBand); + } + + GDALConsumeErrors(p); + + return TRUE; +} + +/************************************************************************/ +/* GDALClientDatasetGetFilename() */ +/************************************************************************/ + +static int IsSeparateExecutable() +{ +#ifdef WIN32 + return TRUE; +#else + const char* pszSpawnServer = CPLGetConfigOption("GDAL_API_PROXY_SERVER", "NO"); + if( EQUAL(pszSpawnServer, "NO") || EQUAL(pszSpawnServer, "OFF") || + EQUAL(pszSpawnServer, "FALSE") || EQUAL(pszSpawnServer, "0") ) + return FALSE; + else + return TRUE; +#endif +} + +const char* GDALClientDatasetGetFilename(const char* pszFilename) +{ + const char* pszSpawn; + if( EQUALN(pszFilename, "API_PROXY:", strlen("API_PROXY:")) ) + { + pszFilename += strlen("API_PROXY:"); + pszSpawn = "YES"; + } + else + { + pszSpawn = CPLGetConfigOption("GDAL_API_PROXY", "NO"); + if( EQUAL(pszSpawn, "NO") || EQUAL(pszSpawn, "OFF") || + EQUAL(pszSpawn, "FALSE") || EQUAL(pszSpawn, "0") ) + { + return NULL; + } + } + + /* Those datasets cannot work in a multi-process context */ + /* /vsistdin/ and /vsistdout/ can work on Unix in the fork() only context (i.e. GDAL_API_PROXY_SERVER undefined) */ + /* since the forked process will inherit the same descriptors as the parent */ + + if( EQUALN(pszFilename, "MEM:::", 6) || + strstr(pszFilename, "/vsimem/") != NULL || + strstr(pszFilename, "/vsimem\\") != NULL || + (strstr(pszFilename, "/vsistdout/") != NULL && IsSeparateExecutable()) || + (strstr(pszFilename, "/vsistdin/") != NULL && IsSeparateExecutable()) || + EQUALN(pszFilename,"NUMPY:::",8) ) + return NULL; + + if( !(EQUAL(pszSpawn, "YES") || EQUAL(pszSpawn, "ON") || + EQUAL(pszSpawn, "TRUE") || EQUAL(pszSpawn, "1")) ) + { + CPLString osExt(CPLGetExtension(pszFilename)); + + /* If the file extension is listed in the GDAL_API_PROXY, then */ + /* we have a match */ + char** papszTokens = CSLTokenizeString2( pszSpawn, " ,", CSLT_HONOURSTRINGS ); + if( CSLFindString(papszTokens, osExt) >= 0 ) + { + CSLDestroy(papszTokens); + return pszFilename; + } + + /* Otherwise let's suppose that driver names are listed in GDAL_API_PROXY */ + /* and check if the file extension matches the extension declared by the */ + /* driver */ + char** papszIter = papszTokens; + while( *papszIter != NULL ) + { + GDALDriverH hDriver = GDALGetDriverByName(*papszIter); + if( hDriver != NULL ) + { + const char* pszDriverExt = + GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSION, NULL); + if( pszDriverExt != NULL && EQUAL(pszDriverExt, osExt) ) + { + CSLDestroy(papszTokens); + return pszFilename; + } + } + papszIter++; + } + CSLDestroy(papszTokens); + return NULL; + } + + return pszFilename; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GDALClientDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + const char* pszFilename = + GDALClientDatasetGetFilename(poOpenInfo->pszFilename); + if( pszFilename == NULL ) + return NULL; + + CLIENT_ENTER(); + + GDALClientDataset* poDS = CreateAndConnect(); + if( poDS == NULL ) + return NULL; + + CPLErrorReset(); + if( !poDS->Init(pszFilename, poOpenInfo->eAccess) ) + { + if( CPLGetLastErrorType() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not open %s", + pszFilename); + } + delete poDS; + return NULL; + } + if( poDS != NULL ) + CPLErrorReset(); + + return poDS; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GDALClientDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + const char* pszFilename = + GDALClientDatasetGetFilename(poOpenInfo->pszFilename); + if( pszFilename == NULL ) + return FALSE; + + CLIENT_ENTER(); + + GDALServerSpawnedProcess* ssp = GDALServerSpawnAsync(); + if( ssp == NULL ) + return FALSE; + + char* pszCWD = CPLGetCurrentDir(); + + GDALPipe* p = ssp->p; + if( !GDALPipeWrite(p, INSTR_Identify) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD) || + !GDALSkipUntilEndOfJunkMarker(p) ) + { + GDALServerSpawnAsyncFinish(ssp); + CPLFree(pszCWD); + return FALSE; + } + + CPLFree(pszCWD); + + int bRet; + if( !GDALPipeRead(p, &bRet) ) + { + GDALServerSpawnAsyncFinish(ssp); + return FALSE; + } + + GDALServerSpawnAsyncFinish(ssp); + return bRet; +} + +/************************************************************************/ +/* GDALClientDatasetQuietDelete() */ +/************************************************************************/ + +static int GDALClientDatasetQuietDelete(GDALPipe* p, + const char* pszFilename) +{ + char* pszCWD = CPLGetCurrentDir(); + if( !GDALPipeWrite(p, INSTR_QuietDelete) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD) || + !GDALSkipUntilEndOfJunkMarker(p) ) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + GDALConsumeErrors(p); + return TRUE; +} + +/************************************************************************/ +/* mCreateCopy() */ +/************************************************************************/ + +int GDALClientDataset::mCreateCopy( const char* pszFilename, + GDALDataset* poSrcDS, + int bStrict, char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + /*if( !SupportsInstr(INSTR_CreateCopy) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "CreateCopy() not supported by server"); + return FALSE; + }*/ + + const char* pszServerDriver = + CSLFetchNameValue(papszOptions, "SERVER_DRIVER"); + if( pszServerDriver == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Creation options should contain a SERVER_DRIVER item"); + return FALSE; + } + + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) ) + { + if( !GDALClientDatasetQuietDelete(p, pszFilename) ) + return FALSE; + } + + GDALPipeWriteConfigOption(p, "GTIFF_POINT_GEO_IGNORE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GTIFF_DELETE_ON_ERROR", bRecycleChild); + GDALPipeWriteConfigOption(p, "ESRI_XML_PAM", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "OGR_SQLITE_SYNCHRONOUS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_WRITE_GEOREF_ON_IMAGE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_OGC_BP_WRITE_WKT", bRecycleChild); + + char* pszCWD = CPLGetCurrentDir(); + + if( !GDALPipeWrite(p, INSTR_CreateCopy) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, poSrcDS->GetDescription()) || + !GDALPipeWrite(p, pszCWD) || + !GDALPipeWrite(p, bStrict) || + !GDALPipeWrite(p, papszOptions) ) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + + int bDriverOK; + if( !GDALPipeRead(p, &bDriverOK) ) + return FALSE; + + if( !bDriverOK ) + { + GDALConsumeErrors(p); + return FALSE; + } + + if( GDALServerLoop(p, + poSrcDS, + pfnProgress, pProgressData) != 0 ) + { + GDALConsumeErrors(p); + return FALSE; + } + + GDALConsumeErrors(p); + + return Init(NULL, GA_Update); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *GDALClientDataset::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + CLIENT_ENTER(); + + GDALClientDataset* poDS = CreateAndConnect(); + if( poDS !=NULL && !poDS->mCreateCopy(pszFilename, poSrcDS, bStrict, + papszOptions, + pfnProgress, pProgressData) ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* mCreate() */ +/************************************************************************/ + +int GDALClientDataset::mCreate( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + /*if( !SupportsInstr(INSTR_Create) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Create() not supported by server"); + return FALSE; + }*/ + + const char* pszServerDriver = + CSLFetchNameValue(papszOptions, "SERVER_DRIVER"); + if( pszServerDriver == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Creation options should contain a SERVER_DRIVER item"); + return FALSE; + } + + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) ) + { + if( !GDALClientDatasetQuietDelete(p, pszFilename) ) + return FALSE; + } + + GDALPipeWriteConfigOption(p,"GTIFF_POINT_GEO_IGNORE", bRecycleChild); + GDALPipeWriteConfigOption(p,"GTIFF_DELETE_ON_ERROR", bRecycleChild); + GDALPipeWriteConfigOption(p,"ESRI_XML_PAM", bRecycleChild); + GDALPipeWriteConfigOption(p,"GTIFF_DONT_WRITE_BLOCKS", bRecycleChild); + + char* pszCWD = CPLGetCurrentDir(); + + if( !GDALPipeWrite(p, INSTR_Create) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBands) || + !GDALPipeWrite(p, eType) || + !GDALPipeWrite(p, papszOptions) ) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return FALSE; + int bOK; + if( !GDALPipeRead(p, &bOK) ) + return FALSE; + + if( !bOK ) + { + GDALConsumeErrors(p); + return FALSE; + } + + GDALConsumeErrors(p); + + return Init(NULL, GA_Update); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset* GDALClientDataset::Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + CLIENT_ENTER(); + + GDALClientDataset* poDS = CreateAndConnect(); + if( poDS != NULL && !poDS->mCreate(pszName, nXSize, nYSize, nBands, + eType, papszOptions) ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +CPLErr GDALClientDataset::Delete( const char * pszFilename ) +{ + pszFilename = + GDALClientDatasetGetFilename(pszFilename); + if( pszFilename == NULL ) + return CE_Failure; + + CLIENT_ENTER(); + + GDALServerSpawnedProcess* ssp = GDALServerSpawnAsync(); + if( ssp == NULL ) + return CE_Failure; + + GDALPipe* p = ssp->p; + if( !GDALClientDatasetQuietDelete(p, pszFilename) ) + { + GDALServerSpawnAsyncFinish(ssp); + return CE_Failure; + } + + GDALServerSpawnAsyncFinish(ssp); + return CE_None; +} + +/************************************************************************/ +/* GDALUnloadAPIPROXYDriver() */ +/************************************************************************/ +static GDALDriver* poAPIPROXYDriver = NULL; + +static void GDALUnloadAPIPROXYDriver(GDALDriver* poDriver) +{ + if( bRecycleChild ) + { + /* Kill all unused descriptors */ + bRecycleChild = FALSE; + for(int i=0;i 0 ) + { + bRecycleChild = TRUE; + nMaxRecycled = MIN(atoi(pszConnPool), MAX_RECYCLED); + } + else if( CSLTestBoolean(pszConnPool) ) + { + bRecycleChild = TRUE; + nMaxRecycled = DEFAULT_RECYCLED; + } + memset(aspRecycled, 0, sizeof(aspRecycled)); + + poAPIPROXYDriver = new GDALDriver(); + + poAPIPROXYDriver->SetDescription( "API_PROXY" ); + poAPIPROXYDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poAPIPROXYDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "API_PROXY" ); + + poAPIPROXYDriver->pfnOpen = GDALClientDataset::Open; + poAPIPROXYDriver->pfnIdentify = GDALClientDataset::Identify; + poAPIPROXYDriver->pfnCreateCopy = GDALClientDataset::CreateCopy; + poAPIPROXYDriver->pfnCreate = GDALClientDataset::Create; + poAPIPROXYDriver->pfnDelete = GDALClientDataset::Delete; + poAPIPROXYDriver->pfnUnloadDriver = GDALUnloadAPIPROXYDriver; + } + return poAPIPROXYDriver; +} diff --git a/ogr/gdalcolortable.cpp b/ogr/gdalcolortable.cpp new file mode 100644 index 0000000..f88fc11 --- /dev/null +++ b/ogr/gdalcolortable.cpp @@ -0,0 +1,462 @@ +/****************************************************************************** + * $Id: gdalcolortable.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Color table implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalcolortable.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* GDALColorTable() */ +/************************************************************************/ + +/** + * \brief Construct a new color table. + * + * This constructor is the same as the C GDALCreateColorTable() function. + * + * @param eInterpIn the interpretation to be applied to GDALColorEntry + * values. + */ + +GDALColorTable::GDALColorTable( GDALPaletteInterp eInterpIn ) + +{ + eInterp = eInterpIn; +} + +/************************************************************************/ +/* GDALCreateColorTable() */ +/************************************************************************/ + +/** + * \brief Construct a new color table. + * + * This function is the same as the C++ method GDALColorTable::GDALColorTable() + */ +GDALColorTableH CPL_STDCALL GDALCreateColorTable( GDALPaletteInterp eInterp ) + +{ + return (GDALColorTableH) (new GDALColorTable( eInterp )); +} + + +/************************************************************************/ +/* ~GDALColorTable() */ +/************************************************************************/ + +/** + * \brief Destructor. + * + * This descructor is the same as the C GDALDestroyColorTable() function. + */ + +GDALColorTable::~GDALColorTable() + +{ +} + +/************************************************************************/ +/* GDALDestroyColorTable() */ +/************************************************************************/ + +/** + * \brief Destroys a color table. + * + * This function is the same as the C++ method GDALColorTable::~GDALColorTable() + */ +void CPL_STDCALL GDALDestroyColorTable( GDALColorTableH hTable ) + +{ + delete (GDALColorTable *) hTable; +} + +/************************************************************************/ +/* GetColorEntry() */ +/************************************************************************/ + +/** + * \brief Fetch a color entry from table. + * + * This method is the same as the C function GDALGetColorEntry(). + * + * @param i entry offset from zero to GetColorEntryCount()-1. + * + * @return pointer to internal color entry, or NULL if index is out of range. + */ + +const GDALColorEntry *GDALColorTable::GetColorEntry( int i ) const + +{ + if( i < 0 || i >= static_cast(aoEntries.size()) ) + return NULL; + else + return &aoEntries[i]; +} + +/************************************************************************/ +/* GDALGetColorEntry() */ +/************************************************************************/ + + +/** + * \brief Fetch a color entry from table. + * + * This function is the same as the C++ method GDALColorTable::GetColorEntry() + */ +const GDALColorEntry * CPL_STDCALL +GDALGetColorEntry( GDALColorTableH hTable, int i ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetColorEntry", NULL ); + + return ((GDALColorTable *) hTable)->GetColorEntry( i ); +} + + +/************************************************************************/ +/* GetColorEntryAsRGB() */ +/************************************************************************/ + +/** + * \brief Fetch a table entry in RGB format. + * + * In theory this method should support translation of color palettes in + * non-RGB color spaces into RGB on the fly, but currently it only works + * on RGB color tables. + * + * This method is the same as the C function GDALGetColorEntryAsRGB(). + * + * @param i entry offset from zero to GetColorEntryCount()-1. + * + * @param poEntry the existing GDALColorEntry to be overrwritten with the RGB + * values. + * + * @return TRUE on success, or FALSE if the conversion isn't supported. + */ + +int GDALColorTable::GetColorEntryAsRGB( int i, GDALColorEntry *poEntry ) const + +{ + if( eInterp != GPI_RGB || i < 0 || i >= static_cast(aoEntries.size()) ) + return FALSE; + + *poEntry = aoEntries[i]; + return TRUE; +} + +/************************************************************************/ +/* GDALGetColorEntryAsRGB() */ +/************************************************************************/ + +/** + * \brief Fetch a table entry in RGB format. + * + * This function is the same as the C++ method GDALColorTable::GetColorEntryAsRGB() + */ +int CPL_STDCALL GDALGetColorEntryAsRGB( GDALColorTableH hTable, int i, + GDALColorEntry *poEntry ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetColorEntryAsRGB", 0 ); + VALIDATE_POINTER1( poEntry, "GDALGetColorEntryAsRGB", 0 ); + + return ((GDALColorTable *) hTable)->GetColorEntryAsRGB( i, poEntry ); +} + +/************************************************************************/ +/* SetColorEntry() */ +/************************************************************************/ + +/** + * \brief Set entry in color table. + * + * Note that the passed in color entry is copied, and no internal reference + * to it is maintained. Also, the passed in entry must match the color + * interpretation of the table to which it is being assigned. + * + * The table is grown as needed to hold the supplied offset. + * + * This function is the same as the C function GDALSetColorEntry(). + * + * @param i entry offset from zero to GetColorEntryCount()-1. + * @param poEntry value to assign to table. + */ + +void GDALColorTable::SetColorEntry( int i, const GDALColorEntry * poEntry ) + +{ + if( i < 0 ) + return; + + try + { + if( i >= static_cast(aoEntries.size()) ) + { + GDALColorEntry oBlack; + oBlack.c1 = oBlack.c2 = oBlack.c3 = oBlack.c4 = 0; + aoEntries.resize(i+1, oBlack); + } + + aoEntries[i] = *poEntry; + } + catch(std::exception &e) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what()); + } +} + +/************************************************************************/ +/* GDALSetColorEntry() */ +/************************************************************************/ + +/** + * \brief Set entry in color table. + * + * This function is the same as the C++ method GDALColorTable::SetColorEntry() + */ +void CPL_STDCALL GDALSetColorEntry( GDALColorTableH hTable, int i, + const GDALColorEntry * poEntry ) + +{ + VALIDATE_POINTER0( hTable, "GDALSetColorEntry" ); + VALIDATE_POINTER0( poEntry, "GDALSetColorEntry" ); + + ((GDALColorTable *) hTable)->SetColorEntry( i, poEntry ); +} + + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * \brief Make a copy of a color table. + * + * This method is the same as the C function GDALCloneColorTable(). + */ + +GDALColorTable *GDALColorTable::Clone() const + +{ + return new GDALColorTable(*this); +} + +/************************************************************************/ +/* GDALCloneColorTable() */ +/************************************************************************/ + +/** + * \brief Make a copy of a color table. + * + * This function is the same as the C++ method GDALColorTable::Clone() + */ +GDALColorTableH CPL_STDCALL GDALCloneColorTable( GDALColorTableH hTable ) + +{ + VALIDATE_POINTER1( hTable, "GDALCloneColorTable", NULL ); + + return (GDALColorTableH) ((GDALColorTable *) hTable)->Clone(); +} + +/************************************************************************/ +/* GetColorEntryCount() */ +/************************************************************************/ + +/** + * \brief Get number of color entries in table. + * + * This method is the same as the function GDALGetColorEntryCount(). + * + * @return the number of color entries. + */ + +int GDALColorTable::GetColorEntryCount() const + +{ + return aoEntries.size(); +} + +/************************************************************************/ +/* GDALGetColorEntryCount() */ +/************************************************************************/ + +/** + * \brief Get number of color entries in table. + * + * This function is the same as the C++ method GDALColorTable::GetColorEntryCount() + */ +int CPL_STDCALL GDALGetColorEntryCount( GDALColorTableH hTable ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetColorEntryCount", 0 ); + + return ((GDALColorTable *) hTable)->GetColorEntryCount(); +} + +/************************************************************************/ +/* GetPaletteInterpretation() */ +/************************************************************************/ + +/** + * \brief Fetch palette interpretation. + * + * The returned value is used to interprete the values in the GDALColorEntry. + * + * This method is the same as the C function GDALGetPaletteInterpretation(). + * + * @return palette interpretation enumeration value, usually GPI_RGB. + */ + +GDALPaletteInterp GDALColorTable::GetPaletteInterpretation() const + +{ + return eInterp; +} + +/************************************************************************/ +/* GDALGetPaltteInterpretation() */ +/************************************************************************/ + +/** + * \brief Fetch palette interpretation. + * + * This function is the same as the C++ method GDALColorTable::GetPaletteInterpretation() + */ +GDALPaletteInterp CPL_STDCALL +GDALGetPaletteInterpretation( GDALColorTableH hTable ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetPaletteInterpretation", GPI_Gray ); + + return ((GDALColorTable *) hTable)->GetPaletteInterpretation(); +} + +/** + * \brief Create color ramp + * + * Automatically creates a color ramp from one color entry to + * another. It can be called several times to create multiples ramps + * in the same color table. + * + * This function is the same as the C function GDALCreateColorRamp(). + * + * @param nStartIndex index to start the ramp on the color table [0..255] + * @param psStartColor a color entry value to start the ramp + * @param nEndIndex index to end the ramp on the color table [0..255] + * @param psEndColor a color entry value to end the ramp + * @return total number of entries, -1 to report error + */ + +int GDALColorTable::CreateColorRamp( + int nStartIndex, const GDALColorEntry *psStartColor, + int nEndIndex, const GDALColorEntry *psEndColor ) +{ + /* validate indexes */ + + if( nStartIndex < 0 || nStartIndex > 255 || + nEndIndex < 0 || nEndIndex > 255 || + nStartIndex > nEndIndex ) + { + return -1; + } + + /* validate color entries */ + + if( psStartColor == NULL || psEndColor == NULL ) + { + return -1; + } + + /* calculate number of colors in-between */ + + int nColors = nEndIndex - nStartIndex; + + /* set starting color */ + + SetColorEntry( nStartIndex, psStartColor ); + + if( nColors == 0 ) + { + return GetColorEntryCount(); /* it should not proceed */ + } + + /* set ending color */ + + SetColorEntry( nEndIndex, psEndColor ); + + /* calculate the slope of the linear transformation */ + + double dfSlope1, dfSlope2, dfSlope3, dfSlope4; + + dfSlope1 = ( psEndColor->c1 - psStartColor->c1 ) / (double) nColors; + dfSlope2 = ( psEndColor->c2 - psStartColor->c2 ) / (double) nColors; + dfSlope3 = ( psEndColor->c3 - psStartColor->c3 ) / (double) nColors; + dfSlope4 = ( psEndColor->c4 - psStartColor->c4 ) / (double) nColors; + + /* loop through the new colors */ + + GDALColorEntry sColor = *psStartColor; + + int i; + + for( i = 1; i < nColors; i++ ) + { + sColor.c1 = (short) ( i * dfSlope1 + (double) psStartColor->c1 ); + sColor.c2 = (short) ( i * dfSlope2 + (double) psStartColor->c2 ); + sColor.c3 = (short) ( i * dfSlope3 + (double) psStartColor->c3 ); + sColor.c4 = (short) ( i * dfSlope4 + (double) psStartColor->c4 ); + + SetColorEntry( nStartIndex + i, &sColor ); + } + + /* return the total number of colors */ + + return GetColorEntryCount(); +} + +/************************************************************************/ +/* GDALCreateColorRamp() */ +/************************************************************************/ + +/** + * \brief Create color ramp + * + * This function is the same as the C++ method GDALColorTable::CreateColorRamp() + */ +void CPL_STDCALL +GDALCreateColorRamp( GDALColorTableH hTable, + int nStartIndex, const GDALColorEntry *psStartColor, + int nEndIndex, const GDALColorEntry *psEndColor ) +{ + VALIDATE_POINTER0( hTable, "GDALCreateColorRamp" ); + + ((GDALColorTable *) hTable)->CreateColorRamp( nStartIndex, psStartColor, + nEndIndex, psEndColor ); +} diff --git a/ogr/gdaldataset.cpp b/ogr/gdaldataset.cpp new file mode 100644 index 0000000..699a358 --- /dev/null +++ b/ogr/gdaldataset.cpp @@ -0,0 +1,5280 @@ +/****************************************************************************** + * $Id: gdaldataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GDAL Core + * Purpose: Base class for raster file formats. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2003, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_hash_set.h" +#include "cpl_multiproc.h" +#include "ogr_featurestyle.h" +#include "swq.h" +#include "ogr_gensql.h" +#include "ogr_attrind.h" +#include "ogr_p.h" +#include "ogrunionlayer.h" + +#ifdef SQLITE_ENABLED +#include "../sqlite/ogrsqliteexecutesql.h" +#endif + +#include + +CPL_CVSID("$Id: gdaldataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +GDALAsyncReader * +GDALGetDefaultAsyncReader( GDALDataset *poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions); +CPL_C_END + +typedef struct +{ + /* PID of the thread that mark the dataset as shared */ + /* This may not be the actual PID, but the responsiblePID */ + GIntBig nPID; + char *pszDescription; + GDALAccess eAccess; + + GDALDataset *poDS; +} SharedDatasetCtxt; + +/* Set of datasets opened as shared datasets (with GDALOpenShared) */ +/* The values in the set are of type SharedDatasetCtxt */ +static CPLHashSet* phSharedDatasetSet = NULL; + +/* Set of all datasets created in the constructor of GDALDataset */ +/* In the case of a shared dataset, memorize the PID of the thread */ +/* that marked the dataset as shared, so that we can remove it from */ +/* the phSharedDatasetSet in the destructor of the dataset, even */ +/* if GDALClose is called from a different thread */ +static std::map* poAllDatasetMap = NULL; + +static void *hDLMutex = NULL; + +/* Static array of all datasets. Used by GDALGetOpenDatasets */ +/* Not thread-safe. See GDALGetOpenDatasets */ +static GDALDataset** ppDatasets = NULL; + +static unsigned long GDALSharedDatasetHashFunc(const void* elt) +{ + SharedDatasetCtxt* psStruct = (SharedDatasetCtxt*) elt; + return (unsigned long) (CPLHashSetHashStr(psStruct->pszDescription) ^ psStruct->eAccess ^ psStruct->nPID); +} + +static int GDALSharedDatasetEqualFunc(const void* elt1, const void* elt2) +{ + SharedDatasetCtxt* psStruct1 = (SharedDatasetCtxt*) elt1; + SharedDatasetCtxt* psStruct2 = (SharedDatasetCtxt*) elt2; + return strcmp(psStruct1->pszDescription, psStruct2->pszDescription) == 0 && + psStruct1->nPID == psStruct2->nPID && + psStruct1->eAccess == psStruct2->eAccess; +} + +static void GDALSharedDatasetFreeFunc(void* elt) +{ + SharedDatasetCtxt* psStruct = (SharedDatasetCtxt*) elt; + CPLFree(psStruct->pszDescription); + CPLFree(psStruct); +} + +/************************************************************************/ +/* Functions shared between gdalproxypool.cpp and gdaldataset.cpp */ +/************************************************************************/ + +/* The open-shared mutex must be used by the ProxyPool too */ +void** GDALGetphDLMutex() +{ + return &hDLMutex; +} + +/* The current thread will act in the behalf of the thread of PID responsiblePID */ +void GDALSetResponsiblePIDForCurrentThread(GIntBig responsiblePID) +{ + GIntBig* pResponsiblePID = (GIntBig*) CPLGetTLS(CTLS_RESPONSIBLEPID); + if (pResponsiblePID == NULL) + { + pResponsiblePID = (GIntBig*) CPLMalloc(sizeof(GIntBig)); + CPLSetTLS(CTLS_RESPONSIBLEPID, pResponsiblePID, TRUE); + } + *pResponsiblePID = responsiblePID; +} + +/* Get the PID of the thread that the current thread will act in the behalf of */ +/* By default : the current thread acts in the behalf of itself */ +GIntBig GDALGetResponsiblePIDForCurrentThread() +{ + GIntBig* pResponsiblePID = (GIntBig*) CPLGetTLS(CTLS_RESPONSIBLEPID); + if (pResponsiblePID == NULL) + return CPLGetPID(); + return *pResponsiblePID; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* GDALDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \class GDALDataset "gdal_priv.h" + * + * A dataset encapsulating one or more raster bands. Details are + * further discussed in the GDAL + * Data Model. + * + * Use GDALOpen() or GDALOpenShared() to create a GDALDataset for a named file, + * or GDALDriver::Create() or GDALDriver::CreateCopy() to create a new + * dataset. + */ + +/************************************************************************/ +/* GDALDataset() */ +/************************************************************************/ + +GDALDataset::GDALDataset() + +{ + poDriver = NULL; + eAccess = GA_ReadOnly; + nRasterXSize = 512; + nRasterYSize = 512; + nBands = 0; + papoBands = NULL; + nRefCount = 1; + bShared = FALSE; + +/* -------------------------------------------------------------------- */ +/* Add this dataset to the open dataset list. */ +/* -------------------------------------------------------------------- */ + { + CPLMutexHolderD( &hDLMutex ); + + if (poAllDatasetMap == NULL) + poAllDatasetMap = new std::map; + (*poAllDatasetMap)[this] = -1; + } + +/* -------------------------------------------------------------------- */ +/* Set forced caching flag. */ +/* -------------------------------------------------------------------- */ + bForceCachedIO = CSLTestBoolean( + CPLGetConfigOption( "GDAL_FORCE_CACHING", "NO") ); + + m_poStyleTable = NULL; + m_hMutex = NULL; +} + + +/************************************************************************/ +/* ~GDALDataset() */ +/************************************************************************/ + +/** + * \brief Destroy an open GDALDataset. + * + * This is the accepted method of closing a GDAL dataset and deallocating + * all resources associated with it. + * + * Equivalent of the C callable GDALClose(). Except that GDALClose() first + * decrements the reference count, and then closes only if it has dropped to + * zero. + * + * For Windows users, it is not recommended to use the delete operator on the + * dataset object because of known issues when allocating and freeing memory across + * module boundaries. Calling GDALClose() is then a better option. + */ + +GDALDataset::~GDALDataset() + +{ + int i; + + // we don't want to report destruction of datasets that + // were never really open. + if( nBands != 0 || !EQUAL(GetDescription(),"") ) + { + if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() ) + CPLDebug( "GDAL", + "GDALClose(%s, this=%p) (pid=%d, responsiblePID=%d)", GetDescription(), this, + (int)CPLGetPID(), + (int)GDALGetResponsiblePIDForCurrentThread() ); + else + CPLDebug( "GDAL", + "GDALClose(%s, this=%p)", GetDescription(), this ); + } + +/* -------------------------------------------------------------------- */ +/* Remove dataset from the "open" dataset list. */ +/* -------------------------------------------------------------------- */ + { + CPLMutexHolderD( &hDLMutex ); + if( poAllDatasetMap ) + { + std::map::iterator oIter = poAllDatasetMap->find(this); + CPLAssert(oIter != poAllDatasetMap->end()); + GIntBig nPIDCreatorForShared = oIter->second; + poAllDatasetMap->erase(oIter); + + if (bShared && phSharedDatasetSet != NULL) + { + SharedDatasetCtxt* psStruct; + SharedDatasetCtxt sStruct; + sStruct.nPID = nPIDCreatorForShared; + sStruct.eAccess = eAccess; + sStruct.pszDescription = (char*) GetDescription(); + psStruct = (SharedDatasetCtxt*) CPLHashSetLookup(phSharedDatasetSet, &sStruct); + if (psStruct && psStruct->poDS == this) + { + CPLHashSetRemove(phSharedDatasetSet, psStruct); + } + else + { + CPLDebug("GDAL", "Should not happen. Cannot find %s, this=%p in phSharedDatasetSet", GetDescription(), this); + } + } + + if (poAllDatasetMap->size() == 0) + { + delete poAllDatasetMap; + poAllDatasetMap = NULL; + if (phSharedDatasetSet) + { + CPLHashSetDestroy(phSharedDatasetSet); + } + phSharedDatasetSet = NULL; + CPLFree(ppDatasets); + ppDatasets = NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Destroy the raster bands if they exist. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBands && papoBands != NULL; i++ ) + { + if( papoBands[i] != NULL ) + delete papoBands[i]; + } + + CPLFree( papoBands ); + + if ( m_poStyleTable ) + { + delete m_poStyleTable; + m_poStyleTable = NULL; + } + + if( m_hMutex != NULL ) + CPLDestroyMutex( m_hMutex ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +/** + * \brief Flush all write cached data to disk. + * + * Any raster (or other GDAL) data written via GDAL calls, but buffered + * internally will be written to disk. + * + * The default implementation of this method just calls the FlushCache() method + * on each of the raster bands and the SyncToDisk() method + * on each of the layers. Conceptionally, calling FlushCache() on a dataset + * should include any work that might be accomplished by calling SyncToDisk() + * on layers in that dataset. + * + * Using this method does not prevent use from calling GDALClose() + * to properly close a dataset and ensure that important data not addressed + * by FlushCache() is written in the file. + * + * This method is the same as the C function GDALFlushCache(). + */ + +void GDALDataset::FlushCache() + +{ + int i; + + // This sometimes happens if a dataset is destroyed before completely + // built. + + if( papoBands != NULL ) + { + for( i = 0; i < nBands; i++ ) + { + if( papoBands[i] != NULL ) + papoBands[i]->FlushCache(); + } + } + + int nLayers = GetLayerCount(); + if( nLayers > 0 ) + { + CPLMutexHolderD( &m_hMutex ); + for( i = 0; i < nLayers ; i++ ) + { + OGRLayer *poLayer = GetLayer(i); + + if( poLayer ) + { + poLayer->SyncToDisk(); + } + } + } +} + +/************************************************************************/ +/* GDALFlushCache() */ +/************************************************************************/ + +/** + * \brief Flush all write cached data to disk. + * + * @see GDALDataset::FlushCache(). + */ + +void CPL_STDCALL GDALFlushCache( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER0( hDS, "GDALFlushCache" ); + + ((GDALDataset *) hDS)->FlushCache(); +} + +/************************************************************************/ +/* BlockBasedFlushCache() */ +/* */ +/* This helper method can be called by the */ +/* GDALDataset::FlushCache() for particular drivers to ensure */ +/* that buffers will be flushed in a manner suitable for pixel */ +/* interleaved (by block) IO. That is, if all the bands have */ +/* the same size blocks then a given block will be flushed for */ +/* all bands before proceeding to the next block. */ +/************************************************************************/ + +void GDALDataset::BlockBasedFlushCache() + +{ + GDALRasterBand *poBand1; + int nBlockXSize, nBlockYSize, iBand; + + poBand1 = GetRasterBand( 1 ); + if( poBand1 == NULL ) + { + GDALDataset::FlushCache(); + return; + } + + poBand1->GetBlockSize( &nBlockXSize, &nBlockYSize ); + +/* -------------------------------------------------------------------- */ +/* Verify that all bands match. */ +/* -------------------------------------------------------------------- */ + for( iBand = 1; iBand < nBands; iBand++ ) + { + int nThisBlockXSize, nThisBlockYSize; + GDALRasterBand *poBand = GetRasterBand( iBand+1 ); + + poBand->GetBlockSize( &nThisBlockXSize, &nThisBlockYSize ); + if( nThisBlockXSize != nBlockXSize && nThisBlockYSize != nBlockYSize ) + { + GDALDataset::FlushCache(); + return; + } + } + +/* -------------------------------------------------------------------- */ +/* Now flush writable data. */ +/* -------------------------------------------------------------------- */ + for( int iY = 0; iY < poBand1->nBlocksPerColumn; iY++ ) + { + for( int iX = 0; iX < poBand1->nBlocksPerRow; iX++ ) + { + for( iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand( iBand+1 ); + + CPLErr eErr; + + eErr = poBand->FlushBlock( iX, iY ); + + if( eErr != CE_None ) + return; + } + } + } +} + +/************************************************************************/ +/* RasterInitialize() */ +/* */ +/* Initialize raster size */ +/************************************************************************/ + +void GDALDataset::RasterInitialize( int nXSize, int nYSize ) + +{ + CPLAssert( nXSize > 0 && nYSize > 0 ); + + nRasterXSize = nXSize; + nRasterYSize = nYSize; +} + +/************************************************************************/ +/* AddBand() */ +/************************************************************************/ + +/** + * \brief Add a band to a dataset. + * + * This method will add a new band to the dataset if the underlying format + * supports this action. Most formats do not. + * + * Note that the new GDALRasterBand is not returned. It may be fetched + * after successful completion of the method by calling + * GDALDataset::GetRasterBand(GDALDataset::GetRasterCount()) as the newest + * band will always be the last band. + * + * @param eType the data type of the pixels in the new band. + * + * @param papszOptions a list of NAME=VALUE option strings. The supported + * options are format specific. NULL may be passed by default. + * + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr GDALDataset::AddBand( GDALDataType eType, char ** papszOptions ) + +{ + (void) eType; + (void) papszOptions; + + ReportError( CE_Failure, CPLE_NotSupported, + "Dataset does not support the AddBand() method." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALAddBand() */ +/************************************************************************/ + +/** + * \brief Add a band to a dataset. + * + * @see GDALDataset::AddBand(). + */ + +CPLErr CPL_STDCALL GDALAddBand( GDALDatasetH hDataset, + GDALDataType eType, char **papszOptions ) + +{ + VALIDATE_POINTER1( hDataset, "GDALAddBand", CE_Failure ); + + return ((GDALDataset *) hDataset)->AddBand( eType, papszOptions ); +} + +/************************************************************************/ +/* SetBand() */ +/* */ +/* Set a band in the band array, updating the band count, and */ +/* array size appropriately. */ +/************************************************************************/ + +void GDALDataset::SetBand( int nNewBand, GDALRasterBand * poBand ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to grow the bands list? */ +/* -------------------------------------------------------------------- */ + if( nBands < nNewBand || papoBands == NULL ) { + int i; + GDALRasterBand** papoNewBands; + + if( papoBands == NULL ) + papoNewBands = (GDALRasterBand **) + VSICalloc(sizeof(GDALRasterBand*), MAX(nNewBand,nBands)); + else + papoNewBands = (GDALRasterBand **) + VSIRealloc(papoBands, sizeof(GDALRasterBand*) * + MAX(nNewBand,nBands)); + if (papoNewBands == NULL) + { + ReportError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate band array"); + return; + } + papoBands = papoNewBands; + + for( i = nBands; i < nNewBand; i++ ) + papoBands[i] = NULL; + + nBands = MAX(nBands,nNewBand); + } + +/* -------------------------------------------------------------------- */ +/* Set the band. Resetting the band is currently not permitted. */ +/* -------------------------------------------------------------------- */ + if( papoBands[nNewBand-1] != NULL ) + { + ReportError(CE_Failure, CPLE_NotSupported, + "Cannot set band %d as it is already set", nNewBand); + return; + } + + papoBands[nNewBand-1] = poBand; + +/* -------------------------------------------------------------------- */ +/* Set back reference information on the raster band. Note */ +/* that the GDALDataset is a friend of the GDALRasterBand */ +/* specifically to allow this. */ +/* -------------------------------------------------------------------- */ + poBand->nBand = nNewBand; + poBand->poDS = this; + poBand->nRasterXSize = nRasterXSize; + poBand->nRasterYSize = nRasterYSize; + poBand->eAccess = eAccess; /* default access to be same as dataset */ +} + +/************************************************************************/ +/* GetRasterXSize() */ +/************************************************************************/ + +/** + + \brief Fetch raster width in pixels. + + Equivalent of the C function GDALGetRasterXSize(). + + @return the width in pixels of raster bands in this GDALDataset. + +*/ + +int GDALDataset::GetRasterXSize() + +{ + return nRasterXSize; +} + +/************************************************************************/ +/* GDALGetRasterXSize() */ +/************************************************************************/ + +/** + * \brief Fetch raster width in pixels. + * + * @see GDALDataset::GetRasterXSize(). + */ + +int CPL_STDCALL GDALGetRasterXSize( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALGetRasterXSize", 0 ); + + return ((GDALDataset *) hDataset)->GetRasterXSize(); +} + + +/************************************************************************/ +/* GetRasterYSize() */ +/************************************************************************/ + +/** + + \brief Fetch raster height in pixels. + + Equivalent of the C function GDALGetRasterYSize(). + + @return the height in pixels of raster bands in this GDALDataset. + +*/ + +int GDALDataset::GetRasterYSize() + +{ + return nRasterYSize; +} + +/************************************************************************/ +/* GDALGetRasterYSize() */ +/************************************************************************/ + +/** + * \brief Fetch raster height in pixels. + * + * @see GDALDataset::GetRasterYSize(). + */ + +int CPL_STDCALL GDALGetRasterYSize( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALGetRasterYSize", 0 ); + + return ((GDALDataset *) hDataset)->GetRasterYSize(); +} + +/************************************************************************/ +/* GetRasterBand() */ +/************************************************************************/ + +/** + + \brief Fetch a band object for a dataset. + + Equivalent of the C function GDALGetRasterBand(). + + @param nBandId the index number of the band to fetch, from 1 to + GetRasterCount(). + + @return the nBandId th band object + +*/ + +GDALRasterBand * GDALDataset::GetRasterBand( int nBandId ) + +{ + if ( papoBands ) + { + if( nBandId < 1 || nBandId > nBands ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "GDALDataset::GetRasterBand(%d) - Illegal band #\n", + nBandId ); + return NULL; + } + else + return( papoBands[nBandId-1] ); + } + return NULL; +} + +/************************************************************************/ +/* GDALGetRasterBand() */ +/************************************************************************/ + +/** + * \brief Fetch a band object for a dataset. + * @see GDALDataset::GetRasterBand(). + */ + +GDALRasterBandH CPL_STDCALL GDALGetRasterBand( GDALDatasetH hDS, int nBandId ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetRasterBand", NULL ); + + return( (GDALRasterBandH) ((GDALDataset *) hDS)->GetRasterBand(nBandId) ); +} + +/************************************************************************/ +/* GetRasterCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of raster bands on this dataset. + * + * Same as the C function GDALGetRasterCount(). + * + * @return the number of raster bands. + */ + +int GDALDataset::GetRasterCount() + +{ + return papoBands ? nBands : 0; +} + +/************************************************************************/ +/* GDALGetRasterCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of raster bands on this dataset. + * + * @see GDALDataset::GetRasterCount(). + */ + +int CPL_STDCALL GDALGetRasterCount( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetRasterCount", 0 ); + + return( ((GDALDataset *) hDS)->GetRasterCount() ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +/** + * \brief Fetch the projection definition string for this dataset. + * + * Same as the C function GDALGetProjectionRef(). + * + * The returned string defines the projection coordinate system of the + * image in OpenGIS WKT format. It should be suitable for use with the + * OGRSpatialReference class. + * + * When a projection definition is not available an empty (but not NULL) + * string is returned. + * + * @return a pointer to an internal projection reference string. It should + * not be altered, freed or expected to last for long. + * + * @see http://www.gdal.org/ogr/osr_tutorial.html + */ + +const char *GDALDataset::GetProjectionRef() + +{ + return( "" ); +} + +/************************************************************************/ +/* GDALGetProjectionRef() */ +/************************************************************************/ + +/** + * \brief Fetch the projection definition string for this dataset. + * + * @see GDALDataset::GetProjectionRef() + */ + +const char * CPL_STDCALL GDALGetProjectionRef( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetProjectionRef", NULL ); + + return( ((GDALDataset *) hDS)->GetProjectionRef() ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +/** + * \brief Set the projection reference string for this dataset. + * + * The string should be in OGC WKT or PROJ.4 format. An error may occur + * because of incorrectly specified projection strings, because the dataset + * is not writable, or because the dataset does not support the indicated + * projection. Many formats do not support writing projections. + * + * This method is the same as the C GDALSetProjection() function. + * + * @param pszProjection projection reference string. + * + * @return CE_Failure if an error occurs, otherwise CE_None. + */ + +CPLErr GDALDataset::SetProjection( const char * pszProjection ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "Dataset does not support the SetProjection() method." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetProjection() */ +/************************************************************************/ + +/** + * \brief Set the projection reference string for this dataset. + * + * @see GDALDataset::SetProjection() + */ + +CPLErr CPL_STDCALL GDALSetProjection( GDALDatasetH hDS, const char * pszProjection ) + +{ + VALIDATE_POINTER1( hDS, "GDALSetProjection", CE_Failure ); + + return( ((GDALDataset *) hDS)->SetProjection(pszProjection) ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Fetch the affine transformation coefficients. + * + * Fetches the coefficients for transforming between pixel/line (P,L) raster + * space, and projection coordinates (Xp,Yp) space. + * + * \code + * Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2]; + * Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5]; + * \endcode + * + * In a north up image, padfTransform[1] is the pixel width, and + * padfTransform[5] is the pixel height. The upper left corner of the + * upper left pixel is at position (padfTransform[0],padfTransform[3]). + * + * The default transform is (0,1,0,0,0,1) and should be returned even when + * a CE_Failure error is returned, such as for formats that don't support + * transformation to projection coordinates. + * + * This method does the same thing as the C GDALGetGeoTransform() function. + * + * @param padfTransform an existing six double buffer into which the + * transformation will be placed. + * + * @return CE_None on success, or CE_Failure if no transform can be fetched. + */ + +CPLErr GDALDataset::GetGeoTransform( double * padfTransform ) + +{ + CPLAssert( padfTransform != NULL ); + + padfTransform[0] = 0.0; /* X Origin (top left corner) */ + padfTransform[1] = 1.0; /* X Pixel size */ + padfTransform[2] = 0.0; + + padfTransform[3] = 0.0; /* Y Origin (top left corner) */ + padfTransform[4] = 0.0; + padfTransform[5] = 1.0; /* Y Pixel Size */ + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALGetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Fetch the affine transformation coefficients. + * + * @see GDALDataset::GetGeoTransform() + */ + +CPLErr CPL_STDCALL GDALGetGeoTransform( GDALDatasetH hDS, double * padfTransform ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGeoTransform", CE_Failure ); + + return( ((GDALDataset *) hDS)->GetGeoTransform(padfTransform) ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Set the affine transformation coefficients. + * + * See GetGeoTransform() for details on the meaning of the padfTransform + * coefficients. + * + * This method does the same thing as the C GDALSetGeoTransform() function. + * + * @param padfTransform a six double buffer containing the transformation + * coefficients to be written with the dataset. + * + * @return CE_None on success, or CE_Failure if this transform cannot be + * written. + */ + +CPLErr GDALDataset::SetGeoTransform( double * padfTransform ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetGeoTransform() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALSetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Set the affine transformation coefficients. + * + * @see GDALDataset::SetGeoTransform() + */ + +CPLErr CPL_STDCALL +GDALSetGeoTransform( GDALDatasetH hDS, double * padfTransform ) + +{ + VALIDATE_POINTER1( hDS, "GDALSetGeoTransform", CE_Failure ); + + return( ((GDALDataset *) hDS)->SetGeoTransform(padfTransform) ); +} + +/************************************************************************/ +/* GetInternalHandle() */ +/************************************************************************/ + +/** + * \brief Fetch a format specific internally meaningful handle. + * + * This method is the same as the C GDALGetInternalHandle() method. + * + * @param pszHandleName the handle name desired. The meaningful names + * will be specific to the file format. + * + * @return the desired handle value, or NULL if not recognised/supported. + */ + +void *GDALDataset::GetInternalHandle( const char * pszHandleName ) + +{ + return( NULL ); +} + +/************************************************************************/ +/* GDALGetInternalHandle() */ +/************************************************************************/ + +/** + * \brief Fetch a format specific internally meaningful handle. + * + * @see GDALDataset::GetInternalHandle() + */ + +void * CPL_STDCALL +GDALGetInternalHandle( GDALDatasetH hDS, const char * pszRequest ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetInternalHandle", NULL ); + + return( ((GDALDataset *) hDS)->GetInternalHandle(pszRequest) ); +} + +/************************************************************************/ +/* GetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch the driver to which this dataset relates. + * + * This method is the same as the C GDALGetDatasetDriver() function. + * + * @return the driver on which the dataset was created with GDALOpen() or + * GDALCreate(). + */ + +GDALDriver * GDALDataset::GetDriver() + +{ + return poDriver; +} + +/************************************************************************/ +/* GDALGetDatasetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch the driver to which this dataset relates. + * + * @see GDALDataset::GetDriver() + */ + +GDALDriverH CPL_STDCALL GDALGetDatasetDriver( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALGetDatasetDriver", NULL ); + + return (GDALDriverH) ((GDALDataset *) hDataset)->GetDriver(); +} + +/************************************************************************/ +/* Reference() */ +/************************************************************************/ + +/** + * \brief Add one to dataset reference count. + * + * The reference is one after instantiation. + * + * This method is the same as the C GDALReferenceDataset() function. + * + * @return the post-increment reference count. + */ + +int GDALDataset::Reference() + +{ + return ++nRefCount; +} + +/************************************************************************/ +/* GDALReferenceDataset() */ +/************************************************************************/ + +/** + * \brief Add one to dataset reference count. + * + * @see GDALDataset::Reference() + */ + +int CPL_STDCALL GDALReferenceDataset( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALReferenceDataset", 0 ); + + return ((GDALDataset *) hDataset)->Reference(); +} + +/************************************************************************/ +/* Dereference() */ +/************************************************************************/ + +/** + * \brief Subtract one from dataset reference count. + * + * The reference is one after instantiation. Generally when the reference + * count has dropped to zero the dataset may be safely deleted (closed). + * + * This method is the same as the C GDALDereferenceDataset() function. + * + * @return the post-decrement reference count. + */ + +int GDALDataset::Dereference() + +{ + return --nRefCount; +} + +/************************************************************************/ +/* GDALDereferenceDataset() */ +/************************************************************************/ + +/** + * \brief Subtract one from dataset reference count. + * + * @see GDALDataset::Dereference() + */ + +int CPL_STDCALL GDALDereferenceDataset( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALDereferenceDataset", 0 ); + + return ((GDALDataset *) hDataset)->Dereference(); +} + +/************************************************************************/ +/* GetShared() */ +/************************************************************************/ + +/** + * \brief Returns shared flag. + * + * @return TRUE if the GDALDataset is available for sharing, or FALSE if not. + */ + +int GDALDataset::GetShared() + +{ + return bShared; +} + +/************************************************************************/ +/* MarkAsShared() */ +/************************************************************************/ + +/** + * \brief Mark this dataset as available for sharing. + */ + +void GDALDataset::MarkAsShared() + +{ + CPLAssert( !bShared ); + + bShared = TRUE; + + GIntBig nPID = GDALGetResponsiblePIDForCurrentThread(); + SharedDatasetCtxt* psStruct; + + /* Insert the dataset in the set of shared opened datasets */ + CPLMutexHolderD( &hDLMutex ); + if (phSharedDatasetSet == NULL) + phSharedDatasetSet = CPLHashSetNew(GDALSharedDatasetHashFunc, GDALSharedDatasetEqualFunc, GDALSharedDatasetFreeFunc); + + psStruct = (SharedDatasetCtxt*)CPLMalloc(sizeof(SharedDatasetCtxt)); + psStruct->poDS = this; + psStruct->nPID = nPID; + psStruct->eAccess = eAccess; + psStruct->pszDescription = CPLStrdup(GetDescription()); + if(CPLHashSetLookup(phSharedDatasetSet, psStruct) != NULL) + { + CPLFree(psStruct); + ReportError(CE_Failure, CPLE_AppDefined, + "An existing shared dataset already has this description. This should not happen."); + } + else + { + CPLHashSetInsert(phSharedDatasetSet, psStruct); + + (*poAllDatasetMap)[this] = nPID; + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +/** + * \brief Get number of GCPs. + * + * This method is the same as the C function GDALGetGCPCount(). + * + * @return number of GCPs for this dataset. Zero if there are none. + */ + +int GDALDataset::GetGCPCount() + +{ + return 0; +} + +/************************************************************************/ +/* GDALGetGCPCount() */ +/************************************************************************/ + +/** + * \brief Get number of GCPs. + * + * @see GDALDataset::GetGCPCount() + */ + +int CPL_STDCALL GDALGetGCPCount( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGCPCount", 0 ); + + return ((GDALDataset *) hDS)->GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +/** + * \brief Get output projection for GCPs. + * + * This method is the same as the C function GDALGetGCPProjection(). + * + * The projection string follows the normal rules from GetProjectionRef(). + * + * @return internal projection string or "" if there are no GCPs. + * It should not be altered, freed or expected to last for long. + */ + +const char *GDALDataset::GetGCPProjection() + +{ + return ""; +} + +/************************************************************************/ +/* GDALGetGCPProjection() */ +/************************************************************************/ + +/** + * \brief Get output projection for GCPs. + * + * @see GDALDataset::GetGCPProjection() + */ + +const char * CPL_STDCALL GDALGetGCPProjection( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGCPProjection", NULL ); + + return ((GDALDataset *) hDS)->GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +/** + * \brief Fetch GCPs. + * + * This method is the same as the C function GDALGetGCPs(). + * + * @return pointer to internal GCP structure list. It should not be modified, + * and may change on the next GDAL call. + */ + +const GDAL_GCP *GDALDataset::GetGCPs() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetGCPs() */ +/************************************************************************/ + +/** + * \brief Fetch GCPs. + * + * @see GDALDataset::GetGCPs() + */ + +const GDAL_GCP * CPL_STDCALL GDALGetGCPs( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGCPs", NULL ); + + return ((GDALDataset *) hDS)->GetGCPs(); +} + + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +/** + * \brief Assign GCPs. + * + * This method is the same as the C function GDALSetGCPs(). + * + * This method assigns the passed set of GCPs to this dataset, as well as + * setting their coordinate system. Internally copies are made of the + * coordinate system and list of points, so the caller remains responsible for + * deallocating these arguments if appropriate. + * + * Most formats do not support setting of GCPs, even formats that can + * handle GCPs. These formats will return CE_Failure. + * + * @param nGCPCount number of GCPs being assigned. + * + * @param pasGCPList array of GCP structures being assign (nGCPCount in array). + * + * @param pszGCPProjection the new OGC WKT coordinate system to assign for the + * GCP output coordinates. This parameter should be "" if no output coordinate + * system is known. + * + * @return CE_None on success, CE_Failure on failure (including if action is + * not supported for this format). + */ + +CPLErr GDALDataset::SetGCPs( int nGCPCount, + const GDAL_GCP *pasGCPList, + const char * pszGCPProjection ) + +{ + (void) nGCPCount; + (void) pasGCPList; + (void) pszGCPProjection; + + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "Dataset does not support the SetGCPs() method." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetGCPs() */ +/************************************************************************/ + +/** + * \brief Assign GCPs. + * + * @see GDALDataset::SetGCPs() + */ + +CPLErr CPL_STDCALL GDALSetGCPs( GDALDatasetH hDS, int nGCPCount, + const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) + +{ + VALIDATE_POINTER1( hDS, "GDALSetGCPs", CE_Failure ); + + return ((GDALDataset *) hDS)->SetGCPs( nGCPCount, pasGCPList, + pszGCPProjection ); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +/** + * \brief Build raster overview(s) + * + * If the operation is unsupported for the indicated dataset, then + * CE_Failure is returned, and CPLGetLastErrorNo() will return + * CPLE_NotSupported. + * + * This method is the same as the C function GDALBuildOverviews(). + * + * @param pszResampling one of "NEAREST", "GAUSS", "CUBIC", "AVERAGE", "MODE", + * "AVERAGE_MAGPHASE" or "NONE" controlling the downsampling method applied. + * @param nOverviews number of overviews to build. + * @param panOverviewList the list of overview decimation factors to build. + * @param nListBands number of bands to build overviews for in panBandList. Build + * for all bands if this is 0. + * @param panBandList list of band numbers. + * @param pfnProgress a function to call to report progress, or NULL. + * @param pProgressData application data to pass to the progress function. + * + * @return CE_None on success or CE_Failure if the operation doesn't work. + * + * For example, to build overview level 2, 4 and 8 on all bands the following + * call could be made: + *
    + *   int       anOverviewList[3] = { 2, 4, 8 };
    + *   
    + *   poDataset->BuildOverviews( "NEAREST", 3, anOverviewList, 0, NULL, 
    + *                              GDALDummyProgress, NULL );
    + * 
    + * + * @see GDALRegenerateOverviews() + */ + +CPLErr GDALDataset::BuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + CPLErr eErr; + int *panAllBandList = NULL; + + if( nListBands == 0 ) + { + nListBands = GetRasterCount(); + panAllBandList = (int *) CPLMalloc(sizeof(int) * nListBands); + for( int i = 0; i < nListBands; i++ ) + panAllBandList[i] = i+1; + + panBandList = panAllBandList; + } + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + eErr = IBuildOverviews( pszResampling, nOverviews, panOverviewList, + nListBands, panBandList, pfnProgress, pProgressData ); + + if( panAllBandList != NULL ) + CPLFree( panAllBandList ); + + return eErr; +} + +/************************************************************************/ +/* GDALBuildOverviews() */ +/************************************************************************/ + +/** + * \brief Build raster overview(s) + * + * @see GDALDataset::BuildOverviews() + */ + +CPLErr CPL_STDCALL GDALBuildOverviews( GDALDatasetH hDataset, + const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + VALIDATE_POINTER1( hDataset, "GDALBuildOverviews", CE_Failure ); + + return ((GDALDataset *) hDataset)->BuildOverviews( + pszResampling, nOverviews, panOverviewList, nListBands, panBandList, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* IBuildOverviews() */ +/* */ +/* Default implementation. */ +/************************************************************************/ + +CPLErr GDALDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + if( oOvManager.IsInitialized() ) + return oOvManager.BuildOverviews( NULL, pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + else + { + ReportError( CE_Failure, CPLE_NotSupported, + "BuildOverviews() not supported for this dataset." ); + + return( CE_Failure ); + } +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* The default implementation of IRasterIO() is to pass the */ +/* request off to each band objects rasterio methods with */ +/* appropriate arguments. */ +/************************************************************************/ + +CPLErr GDALDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace) + +{ + int iBandIndex; + CPLErr eErr = CE_None; + const char* pszInterleave = NULL; + + CPLAssert( NULL != pData ); + + if (nXSize == nBufXSize && nYSize == nBufYSize && nBandCount > 1 && + (pszInterleave = GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE")) != NULL && + EQUAL(pszInterleave, "PIXEL")) + { + return BlockBasedRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + } + + for( iBandIndex = 0; + iBandIndex < nBandCount && eErr == CE_None; + iBandIndex++ ) + { + GDALRasterBand *poBand = GetRasterBand(panBandMap[iBandIndex]); + GByte *pabyBandData; + + if (poBand == NULL) + { + eErr = CE_Failure; + break; + } + + pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpace; + + eErr = poBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (void *) pabyBandData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace ); + } + + return eErr; +} + +/************************************************************************/ +/* ValidateRasterIOOrAdviseReadParameters() */ +/************************************************************************/ + +CPLErr GDALDataset::ValidateRasterIOOrAdviseReadParameters( + const char* pszCallingFunc, + int* pbStopProcessingOnCENone, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandMap) +{ + +/* -------------------------------------------------------------------- */ +/* Some size values are "noop". Lets just return to avoid */ +/* stressing lower level functions. */ +/* -------------------------------------------------------------------- */ + if( nXSize < 1 || nYSize < 1 || nBufXSize < 1 || nBufYSize < 1 ) + { + CPLDebug( "GDAL", + "%s skipped for odd window or buffer size.\n" + " Window = (%d,%d)x%dx%d\n" + " Buffer = %dx%d\n", + pszCallingFunc, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); + + *pbStopProcessingOnCENone = TRUE; + return CE_None; + } + + CPLErr eErr = CE_None; + *pbStopProcessingOnCENone = FALSE; + + if( nXOff < 0 || nXOff > INT_MAX - nXSize || nXOff + nXSize > nRasterXSize + || nYOff < 0 || nYOff > INT_MAX - nYSize || nYOff + nYSize > nRasterYSize ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Access window out of range in %s. Requested\n" + "(%d,%d) of size %dx%d on raster of %dx%d.", + pszCallingFunc, nXOff, nYOff, nXSize, nYSize, nRasterXSize, nRasterYSize ); + eErr = CE_Failure; + } + + if( panBandMap == NULL && nBandCount > GetRasterCount() ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "%s: nBandCount cannot be greater than %d", + pszCallingFunc, GetRasterCount() ); + eErr = CE_Failure; + } + + for( int i = 0; i < nBandCount && eErr == CE_None; i++ ) + { + int iBand = (panBandMap != NULL) ? panBandMap[i] : i + 1; + if( iBand < 1 || iBand > GetRasterCount() ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "%s: panBandMap[%d] = %d, this band does not exist on dataset.", + pszCallingFunc, i, iBand ); + eErr = CE_Failure; + } + + if( eErr == CE_None && GetRasterBand( iBand ) == NULL ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "%s: panBandMap[%d]=%d, this band should exist but is NULL!", + pszCallingFunc, i, iBand ); + eErr = CE_Failure; + } + } + + return eErr; +} + + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data from multiple bands. + * + * This method allows reading a region of one or more GDALRasterBands from + * this dataset into a buffer, or writing data from a buffer into a region + * of the GDALRasterBands. It automatically takes care of data type + * translation if the data type (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * The method also takes care of image decimation / replication if the + * buffer size (nBufXSize x nBufYSize) is different than the size of the + * region being accessed (nXSize x nYSize). + * + * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or + * writing from various organization of buffers. + * + * For highest performance full resolution data access, read and write + * on "block boundaries" as returned by GetBlockSize(), or use the + * ReadBlock() and WriteBlock() methods. + * + * This method is the same as the C GDALDatasetRasterIO() function. + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pData The buffer into which the data should be read, or from which + * it should be written. This buffer must contain at least + * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized + * in left to right,top to bottom pixel order. Spacing is controlled by the + * nPixelSpace, and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nBandSpace the byte offset from the start of one bands data to the + * start of the next. If defaulted (0) the value will be + * nLineSpace * nBufYSize implying band sequential organization + * of the data buffer. + * + * @return CE_Failure if the access fails, otherwise CE_None. + */ + +CPLErr GDALDataset::RasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace ) + +{ + int i = 0; + int bNeedToFreeBandMap = FALSE; + CPLErr eErr = CE_None; + + if( NULL == pData ) + { + ReportError( CE_Failure, CPLE_AppDefined, + "The buffer into which the data should be read is null" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + + if( eRWFlag != GF_Read && eRWFlag != GF_Write ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "eRWFlag = %d, only GF_Read (0) and GF_Write (1) are legal.", + eRWFlag ); + return CE_Failure; + } + + int bStopProcessing = FALSE; + eErr = ValidateRasterIOOrAdviseReadParameters( "RasterIO()", &bStopProcessing, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandMap); + if( eErr != CE_None || bStopProcessing ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* If pixel and line spacing are defaulted assign reasonable */ +/* value assuming a packed buffer. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8; + + if( nLineSpace == 0 ) + { + if (nPixelSpace > INT_MAX / nBufXSize) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Int overflow : %d x %d", nPixelSpace, nBufXSize ); + return CE_Failure; + } + nLineSpace = nPixelSpace * nBufXSize; + } + + /* The nBandCount > 1 test is necessary to allow reading only */ + /* one band, even if the nLineSpace would overflow, but as it */ + /* is not used in that case, it can be left to 0 (#3481) */ + if( nBandSpace == 0 && nBandCount > 1 ) + { + if (nLineSpace > INT_MAX / nBufYSize) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Int overflow : %d x %d", nLineSpace, nBufYSize ); + return CE_Failure; + } + nBandSpace = nLineSpace * nBufYSize; + } + + if( panBandMap == NULL ) + { + panBandMap = (int *) VSIMalloc2(sizeof(int), nBandCount); + if (panBandMap == NULL) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory while allocating band map array" ); + return CE_Failure; + } + for( i = 0; i < nBandCount; i++ ) + panBandMap[i] = i+1; + + bNeedToFreeBandMap = TRUE; + } + + +/* -------------------------------------------------------------------- */ +/* We are being forced to use cached IO instead of a driver */ +/* specific implementation. */ +/* -------------------------------------------------------------------- */ + if( bForceCachedIO ) + { + eErr = + BlockBasedRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + } + +/* -------------------------------------------------------------------- */ +/* Call the format specific function. */ +/* -------------------------------------------------------------------- */ + else if( eErr == CE_None ) + { + eErr = + IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if( bNeedToFreeBandMap ) + CPLFree( panBandMap ); + + return eErr; +} + +/************************************************************************/ +/* GDALDatasetRasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data from multiple bands. + * + * @see GDALDataset::RasterIO() + */ + +CPLErr CPL_STDCALL +GDALDatasetRasterIO( GDALDatasetH hDS, GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetRasterIO", CE_Failure ); + + GDALDataset *poDS = (GDALDataset *) hDS; + + return( poDS->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ) ); +} + +/************************************************************************/ +/* GetOpenDatasets() */ +/************************************************************************/ + +/** + * \brief Fetch all open GDAL dataset handles. + * + * This method is the same as the C function GDALGetOpenDatasets(). + * + * NOTE: This method is not thread safe. The returned list may change + * at any time and it should not be freed. + * + * @param pnCount integer into which to place the count of dataset pointers + * being returned. + * + * @return a pointer to an array of dataset handles. + */ + +GDALDataset **GDALDataset::GetOpenDatasets( int *pnCount ) + +{ + CPLMutexHolderD( &hDLMutex ); + + if (poAllDatasetMap != NULL) + { + int i = 0; + *pnCount = poAllDatasetMap->size(); + ppDatasets = (GDALDataset**) CPLRealloc(ppDatasets, (*pnCount) * sizeof(GDALDataset*)); + std::map::iterator oIter = poAllDatasetMap->begin(); + for(; oIter != poAllDatasetMap->end(); ++oIter, i++ ) + ppDatasets[i] = oIter->first; + return ppDatasets; + } + else + { + *pnCount = 0; + return NULL; + } +} + +/************************************************************************/ +/* GDALGetOpenDatasets() */ +/************************************************************************/ + +/** + * \brief Fetch all open GDAL dataset handles. + * + * @see GDALDataset::GetOpenDatasets() + */ + +void CPL_STDCALL GDALGetOpenDatasets( GDALDatasetH **ppahDSList, int *pnCount ) + +{ + VALIDATE_POINTER0( ppahDSList, "GDALGetOpenDatasets" ); + VALIDATE_POINTER0( pnCount, "GDALGetOpenDatasets" ); + + *ppahDSList = (GDALDatasetH *) GDALDataset::GetOpenDatasets( pnCount); +} + + +/************************************************************************/ +/* GDALCleanOpenDatasetsList() */ +/************************************************************************/ + +/* Usefull when called from the child of a fork(), to avoid closing */ +/* the datasets of the parent at the child termination */ +void GDALNullifyOpenDatasetsList() +{ + poAllDatasetMap = NULL; + phSharedDatasetSet = NULL; + ppDatasets = NULL; + hDLMutex = NULL; +} + +/************************************************************************/ +/* GDALGetAccess() */ +/************************************************************************/ + +/** + * \brief Return access flag + * + * @see GDALDataset::GetAccess() + */ + +int CPL_STDCALL GDALGetAccess( GDALDatasetH hDS ) +{ + VALIDATE_POINTER1( hDS, "GDALGetAccess", 0 ); + + return ((GDALDataset *) hDS)->GetAccess(); +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +/** + * \brief Advise driver of upcoming read requests. + * + * Some GDAL drivers operate more efficiently if they know in advance what + * set of upcoming read requests will be made. The AdviseRead() method allows + * an application to notify the driver of the region and bands of interest, + * and at what resolution the region will be read. + * + * Many drivers just ignore the AdviseRead() call, but it can dramatically + * accelerate access via some drivers. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param papszOptions a list of name=value strings with special control + * options. Normally this is NULL. + * + * @return CE_Failure if the request is invalid and CE_None if it works or + * is ignored. + */ + +CPLErr GDALDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + char **papszOptions ) + +{ + int iBand; + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + int bStopProcessing = FALSE; + CPLErr eErr = ValidateRasterIOOrAdviseReadParameters( "AdviseRead()", &bStopProcessing, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandMap); + if( eErr != CE_None || bStopProcessing ) + return eErr; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + CPLErr eErr; + GDALRasterBand *poBand; + + if( panBandMap == NULL ) + poBand = GetRasterBand(iBand+1); + else + poBand = GetRasterBand(panBandMap[iBand]); + + eErr = poBand->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eBufType, papszOptions ); + + if( eErr != CE_None ) + return eErr; + } + + return CE_None; +} + +/************************************************************************/ +/* GDALDatasetAdviseRead() */ +/************************************************************************/ + +/** + * \brief Advise driver of upcoming read requests. + * + * @see GDALDataset::AdviseRead() + */ +CPLErr CPL_STDCALL +GDALDatasetAdviseRead( GDALDatasetH hDS, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, GDALDataType eDT, + int nBandCount, int *panBandMap,char **papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetAdviseRead", CE_Failure ); + + return ((GDALDataset *) hDS)->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, + nBandCount, panBandMap, + papszOptions ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +/** + * \brief Fetch files forming dataset. + * + * Returns a list of files believed to be part of this dataset. If it returns + * an empty list of files it means there is believed to be no local file + * system files associated with the dataset (for instance a virtual dataset). + * The returned file list is owned by the caller and should be deallocated + * with CSLDestroy(). + * + * The returned filenames will normally be relative or absolute paths + * depending on the path used to originally open the dataset. The strings + * will be UTF-8 encoded. + * + * This method is the same as the C GDALGetFileList() function. + * + * @return NULL or a NULL terminated array of file names. + */ + +char **GDALDataset::GetFileList() + +{ + CPLString osMainFilename = GetDescription(); + int bMainFileReal; + VSIStatBufL sStat; + +/* -------------------------------------------------------------------- */ +/* Is the main filename even a real filesystem object? */ +/* -------------------------------------------------------------------- */ + bMainFileReal = VSIStatExL( osMainFilename, &sStat, VSI_STAT_EXISTS_FLAG ) == 0; + +/* -------------------------------------------------------------------- */ +/* Form new list. */ +/* -------------------------------------------------------------------- */ + char **papszList = NULL; + + if( bMainFileReal ) + papszList = CSLAddString( papszList, osMainFilename ); + +/* -------------------------------------------------------------------- */ +/* Do we have a known overview file? */ +/* -------------------------------------------------------------------- */ + if( oOvManager.IsInitialized() && oOvManager.poODS != NULL ) + { + char **papszOvrList = oOvManager.poODS->GetFileList(); + papszList = CSLInsertStrings( papszList, -1, papszOvrList ); + CSLDestroy( papszOvrList ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a known overview file? */ +/* -------------------------------------------------------------------- */ + if( oOvManager.HaveMaskFile() ) + { + char **papszMskList = oOvManager.poMaskDS->GetFileList(); + char **papszIter = papszMskList; + while( papszIter && *papszIter ) + { + if( CSLFindString( papszList, *papszIter ) < 0 ) + papszList = CSLAddString( papszList, *papszIter ); + papszIter ++; + } + CSLDestroy( papszMskList ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a world file? */ +/* -------------------------------------------------------------------- */ + if( bMainFileReal ) + { + const char* pszExtension = CPLGetExtension( osMainFilename ); + if( strlen(pszExtension) > 2 ) + { + // first + last + 'w' + char szDerivedExtension[4]; + szDerivedExtension[0] = pszExtension[0]; + szDerivedExtension[1] = pszExtension[strlen(pszExtension)-1]; + szDerivedExtension[2] = 'w'; + szDerivedExtension[3] = '\0'; + CPLString osWorldFilename = CPLResetExtension( osMainFilename, szDerivedExtension ); + + if (oOvManager.papszInitSiblingFiles) + { + int iSibling = CSLFindString(oOvManager.papszInitSiblingFiles, + CPLGetFilename(osWorldFilename)); + if (iSibling >= 0) + { + osWorldFilename.resize(strlen(osWorldFilename) - + strlen(oOvManager.papszInitSiblingFiles[iSibling])); + osWorldFilename += oOvManager.papszInitSiblingFiles[iSibling]; + papszList = CSLAddString( papszList, osWorldFilename ); + } + } + else if( VSIStatExL( osWorldFilename, &sStat, VSI_STAT_EXISTS_FLAG ) == 0 ) + papszList = CSLAddString( papszList, osWorldFilename ); + } + } + + return papszList; +} + +/************************************************************************/ +/* GDALGetFileList() */ +/************************************************************************/ + +/** + * \brief Fetch files forming dataset. + * + * @see GDALDataset::GetFileList() + */ + +char ** CPL_STDCALL GDALGetFileList( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetFileList", NULL ); + + return ((GDALDataset *) hDS)->GetFileList(); +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the dataset + * + * The default implementation of the CreateMaskBand() method is implemented + * based on similar rules to the .ovr handling implemented using the + * GDALDefaultOverviews object. A TIFF file with the extension .msk will + * be created with the same basename as the original file, and it will have + * one band. + * The mask images will be deflate compressed tiled images with the same + * block size as the original image if possible. + * + * Note that if you got a mask band with a previous call to GetMaskBand(), + * it might be invalidated by CreateMaskBand(). So you have to call GetMaskBand() + * again. + * + * @since GDAL 1.5.0 + * + * @param nFlags ignored. GMF_PER_DATASET will be assumed. + * @return CE_None on success or CE_Failure on an error. + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ +CPLErr GDALDataset::CreateMaskBand( int nFlags ) + +{ + if( oOvManager.IsInitialized() ) + { + CPLErr eErr = oOvManager.CreateMaskBand( nFlags, -1 ); + if (eErr != CE_None) + return eErr; + + /* Invalidate existing raster band masks */ + int i; + for(i=0;ibOwnMask) + delete poBand->poMask; + poBand->bOwnMask = false; + poBand->poMask = NULL; + } + + return CE_None; + } + + ReportError( CE_Failure, CPLE_NotSupported, + "CreateMaskBand() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALCreateDatasetMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the dataset + * @see GDALDataset::CreateMaskBand() + */ +CPLErr CPL_STDCALL GDALCreateDatasetMaskBand( GDALDatasetH hDS, int nFlags ) + +{ + VALIDATE_POINTER1( hDS, "GDALCreateDatasetMaskBand", CE_Failure ); + + return ((GDALDataset *) hDS)->CreateMaskBand( nFlags ); +} + +/************************************************************************/ +/* GDALOpen() */ +/************************************************************************/ + +/** + * \brief Open a raster file as a GDALDataset. + * + * This function will try to open the passed file, or virtual dataset + * name by invoking the Open method of each registered GDALDriver in turn. + * The first successful open will result in a returned dataset. If all + * drivers fail then NULL is returned and an error is issued. + * + * Several recommendations : + *
      + *
    • If you open a dataset object with GA_Update access, it is not recommended + * to open a new dataset on the same underlying file.
    • + *
    • The returned dataset should only be accessed by one thread at a time. If you + * want to use it from different threads, you must add all necessary code (mutexes, etc.) + * to avoid concurrent use of the object. (Some drivers, such as GeoTIFF, maintain internal + * state variables that are updated each time a new block is read, thus preventing concurrent + * use.)
    • + *
    + * + * For drivers supporting the VSI virtual file API, it is possible to open + * a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + * (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + * + * In some situations (dealing with unverified data), the datasets can be opened in another + * process through the \ref gdal_api_proxy mechanism. + * + * \sa GDALOpenShared() + * \sa GDALOpenEx() + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. It should be in UTF-8 + * encoding. + * + * @param eAccess the desired access, either GA_Update or GA_ReadOnly. Many + * drivers support only read only access. + * + * @return A GDALDatasetH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDataset *. + */ + +GDALDatasetH CPL_STDCALL +GDALOpen( const char * pszFilename, GDALAccess eAccess ) + +{ + return GDALOpenEx( pszFilename, + GDAL_OF_RASTER | + (eAccess == GA_Update ? GDAL_OF_UPDATE : 0) | + GDAL_OF_VERBOSE_ERROR, + NULL, NULL, NULL ); +} + + +/************************************************************************/ +/* GDALOpenEx() */ +/************************************************************************/ + +/** + * \brief Open a raster or vector file as a GDALDataset. + * + * This function will try to open the passed file, or virtual dataset + * name by invoking the Open method of each registered GDALDriver in turn. + * The first successful open will result in a returned dataset. If all + * drivers fail then NULL is returned and an error is issued. + * + * Several recommendations : + *
      + *
    • If you open a dataset object with GDAL_OF_UPDATE access, it is not recommended + * to open a new dataset on the same underlying file.
    • + *
    • The returned dataset should only be accessed by one thread at a time. If you + * want to use it from different threads, you must add all necessary code (mutexes, etc.) + * to avoid concurrent use of the object. (Some drivers, such as GeoTIFF, maintain internal + * state variables that are updated each time a new block is read, thus preventing concurrent + * use.)
    • + *
    + * + * For drivers supporting the VSI virtual file API, it is possible to open + * a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + * (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + * + * In some situations (dealing with unverified data), the datasets can be opened in another + * process through the \ref gdal_api_proxy mechanism. + * + * In order to reduce the need for searches through the operating system + * file system machinery, it is possible to give an optional list of files with + * the papszSiblingFiles parameter. + * This is the list of all files at the same level in the file system as the + * target file, including the target file. The filenames must not include any + * path components, are an essentially just the output of CPLReadDir() on the + * parent directory. If the target object does not have filesystem semantics + * then the file list should be NULL. + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. It should be in UTF-8 + * encoding. + * + * @param nOpenFlags a combination of GDAL_OF_ flags that may be combined through + * logical or operator. + *
      + *
    • Driver kind: GDAL_OF_RASTER for raster drivers, GDAL_OF_VECTOR for vector drivers. + * If none of the value is specified, both kinds are implied.
    • + *
    • Access mode: GDAL_OF_READONLY (exclusive)or GDAL_OF_UPDATE.
    • + *
    • Shared mode: GDAL_OF_SHARED. If set, it allows the sharing of + * GDALDataset handles for a dataset with other callers that have set GDAL_OF_SHARED. + * In particular, GDALOpenEx() will first consult its list of currently + * open and shared GDALDataset's, and if the GetDescription() name for one + * exactly matches the pszFilename passed to GDALOpenEx() it will be + * referenced and returned, if GDALOpenEx() is called from the same thread.
    • + *
    • Verbose error: GDAL_OF_VERBOSE_ERROR. If set, a failed attempt to open the + * file will lead to an error message to be reported.
    • + *
    + * + * @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL + * terminated list of strings with the driver short names that must be considered. + * + * @param papszOpenOptions NULL, or a NULL terminated list of strings with open + * options passed to candidate drivers. + * + * @param papszSiblingFiles NULL, or a NULL terminated list of strings that are + * filenames that are auxiliary to the main filename. If NULL is passed, a probing + * of the file system will be done. + * + * @return A GDALDatasetH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDataset *. + * + * @since GDAL 2.0 + */ + +GDALDatasetH CPL_STDCALL GDALOpenEx( const char* pszFilename, + unsigned int nOpenFlags, + const char* const* papszAllowedDrivers, + const char* const* papszOpenOptions, + const char* const* papszSiblingFiles ) +{ + VALIDATE_POINTER1( pszFilename, "GDALOpen", NULL ); + +/* -------------------------------------------------------------------- */ +/* In case of shared dataset, first scan the existing list to see */ +/* if it could already contain the requested dataset. */ +/* -------------------------------------------------------------------- */ + if( nOpenFlags & GDAL_OF_SHARED ) + { + CPLMutexHolderD( &hDLMutex ); + + if (phSharedDatasetSet != NULL) + { + GIntBig nThisPID = GDALGetResponsiblePIDForCurrentThread(); + SharedDatasetCtxt* psStruct; + SharedDatasetCtxt sStruct; + + sStruct.nPID = nThisPID; + sStruct.pszDescription = (char*) pszFilename; + sStruct.eAccess = (nOpenFlags & GDAL_OF_UPDATE) ? GA_Update : GA_ReadOnly; + psStruct = (SharedDatasetCtxt*) CPLHashSetLookup(phSharedDatasetSet, &sStruct); + if (psStruct == NULL && (nOpenFlags & GDAL_OF_UPDATE) == 0) + { + sStruct.eAccess = GA_Update; + psStruct = (SharedDatasetCtxt*) CPLHashSetLookup(phSharedDatasetSet, &sStruct); + } + if (psStruct) + { + psStruct->poDS->Reference(); + return psStruct->poDS; + } + } + } + + /* If no driver kind is specified, assume all are to be probed */ + if( (nOpenFlags & GDAL_OF_KIND_MASK) == 0 ) + nOpenFlags |= GDAL_OF_KIND_MASK; + + { + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount == NULL ) + { + pnRecCount = (int*) CPLMalloc(sizeof(int)); + *pnRecCount = 0; + CPLSetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP, pnRecCount, TRUE ); + } + if( *pnRecCount == 100 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALOpen() called with too many recursion levels"); + return NULL; + } + (*pnRecCount) ++; + } + + int iDriver; + GDALDriverManager *poDM = GetGDALDriverManager(); + CPLLocaleC oLocaleForcer; + + CPLErrorReset(); + CPLAssert( NULL != poDM ); + + /* Build GDALOpenInfo just now to avoid useless file stat'ing if a */ + /* shared dataset was asked before */ + GDALOpenInfo oOpenInfo(pszFilename, + nOpenFlags, + (char**) papszSiblingFiles); + oOpenInfo.papszOpenOptions = (char**) papszOpenOptions; + + for( iDriver = -1; iDriver < poDM->GetDriverCount(); iDriver++ ) + { + GDALDriver *poDriver; + GDALDataset *poDS; + + if( iDriver < 0 ) + poDriver = GDALGetAPIPROXYDriver(); + else + { + poDriver = poDM->GetDriver( iDriver ); + if (papszAllowedDrivers != NULL && + CSLFindString((char**)papszAllowedDrivers, GDALGetDriverShortName(poDriver)) == -1) + continue; + } + + if( (nOpenFlags & GDAL_OF_RASTER) != 0 && + (nOpenFlags & GDAL_OF_VECTOR) == 0 && + poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == NULL ) + continue; + if( (nOpenFlags & GDAL_OF_VECTOR) != 0 && + (nOpenFlags & GDAL_OF_RASTER) == 0 && + poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + continue; + + if( poDriver->pfnIdentify && poDriver->pfnIdentify(&oOpenInfo) > 0 ) + GDALValidateOpenOptions( poDriver, papszOpenOptions ); + + if ( poDriver->pfnOpen != NULL ) + { + poDS = poDriver->pfnOpen( &oOpenInfo ); + } + else if( poDriver->pfnOpenWithDriverArg != NULL ) + { + poDS = poDriver->pfnOpenWithDriverArg( poDriver, &oOpenInfo ); + } + else + continue; + + if( poDS != NULL ) + { + if( strlen(poDS->GetDescription()) == 0 ) + poDS->SetDescription( pszFilename ); + + if( poDS->poDriver == NULL ) + poDS->poDriver = poDriver; + + + if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() ) + CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s (pid=%d, responsiblePID=%d).", + pszFilename, poDS, poDriver->GetDescription(), + (int)CPLGetPID(), (int)GDALGetResponsiblePIDForCurrentThread() ); + else + CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s.", + pszFilename, poDS, poDriver->GetDescription() ); + + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount ) + (*pnRecCount) --; + + if( nOpenFlags & GDAL_OF_SHARED ) + { + if (strcmp(pszFilename, poDS->GetDescription()) != 0) + { + CPLError(CE_Warning, CPLE_NotSupported, + "A dataset opened by GDALOpenShared should have the same filename (%s) " + "and description (%s)", + pszFilename, poDS->GetDescription()); + } + else + { + poDS->MarkAsShared(); + } + } + + return (GDALDatasetH) poDS; + } + + if( CPLGetLastErrorNo() != 0 ) + { + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount ) + (*pnRecCount) --; + + return NULL; + } + } + + if( nOpenFlags & GDAL_OF_VERBOSE_ERROR ) + { + if( oOpenInfo.bStatOK ) + CPLError( CE_Failure, CPLE_OpenFailed, + "`%s' not recognised as a supported file format.\n", + pszFilename ); + else + CPLError( CE_Failure, CPLE_OpenFailed, + "`%s' does not exist in the file system,\n" + "and is not recognised as a supported dataset name.\n", + pszFilename ); + } + + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount ) + (*pnRecCount) --; + + return NULL; +} + +/************************************************************************/ +/* GDALOpenShared() */ +/************************************************************************/ + +/** + * \brief Open a raster file as a GDALDataset. + * + * This function works the same as GDALOpen(), but allows the sharing of + * GDALDataset handles for a dataset with other callers to GDALOpenShared(). + * + * In particular, GDALOpenShared() will first consult it's list of currently + * open and shared GDALDataset's, and if the GetDescription() name for one + * exactly matches the pszFilename passed to GDALOpenShared() it will be + * referenced and returned. + * + * Starting with GDAL 1.6.0, if GDALOpenShared() is called on the same pszFilename + * from two different threads, a different GDALDataset object will be returned as + * it is not safe to use the same dataset from different threads, unless the user + * does explicitely use mutexes in its code. + * + * For drivers supporting the VSI virtual file API, it is possible to open + * a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + * (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + * + * In some situations (dealing with unverified data), the datasets can be opened in another + * process through the \ref gdal_api_proxy mechanism. + * + * \sa GDALOpen() + * \sa GDALOpenEx() + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. It should be in + * UTF-8 encoding. + * + * @param eAccess the desired access, either GA_Update or GA_ReadOnly. Many + * drivers support only read only access. + * + * @return A GDALDatasetH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDataset *. + */ + +GDALDatasetH CPL_STDCALL +GDALOpenShared( const char *pszFilename, GDALAccess eAccess ) +{ + VALIDATE_POINTER1( pszFilename, "GDALOpenShared", NULL ); + return GDALOpenEx( pszFilename, + GDAL_OF_RASTER | + (eAccess == GA_Update ? GDAL_OF_UPDATE : 0) | + GDAL_OF_SHARED | + GDAL_OF_VERBOSE_ERROR, + NULL, NULL, NULL ); +} + +/************************************************************************/ +/* GDALClose() */ +/************************************************************************/ + +/** + * \brief Close GDAL dataset. + * + * For non-shared datasets (opened with GDALOpen()) the dataset is closed + * using the C++ "delete" operator, recovering all dataset related resources. + * For shared datasets (opened with GDALOpenShared()) the dataset is + * dereferenced, and closed only if the referenced count has dropped below 1. + * + * @param hDS The dataset to close. May be cast from a "GDALDataset *". + */ + +void CPL_STDCALL GDALClose( GDALDatasetH hDS ) + +{ + if( hDS == NULL ) + return; + + GDALDataset *poDS = (GDALDataset *) hDS; + CPLMutexHolderD( &hDLMutex ); + CPLLocaleC oLocaleForcer; + + if (poDS->GetShared()) + { +/* -------------------------------------------------------------------- */ +/* If this file is in the shared dataset list then dereference */ +/* it, and only delete/remote it if the reference count has */ +/* dropped to zero. */ +/* -------------------------------------------------------------------- */ + if( poDS->Dereference() > 0 ) + return; + + delete poDS; + return; + } + +/* -------------------------------------------------------------------- */ +/* This is not shared dataset, so directly delete it. */ +/* -------------------------------------------------------------------- */ + delete poDS; +} + +/************************************************************************/ +/* GDALDumpOpenDataset() */ +/************************************************************************/ + +static int GDALDumpOpenSharedDatasetsForeach(void* elt, void* user_data) +{ + SharedDatasetCtxt* psStruct = (SharedDatasetCtxt*) elt; + FILE *fp = (FILE*) user_data; + const char *pszDriverName; + GDALDataset *poDS = psStruct->poDS; + + if( poDS->GetDriver() == NULL ) + pszDriverName = "DriverIsNULL"; + else + pszDriverName = poDS->GetDriver()->GetDescription(); + + poDS->Reference(); + VSIFPrintf( fp, " %d %c %-6s %7d %dx%dx%d %s\n", + poDS->Dereference(), + poDS->GetShared() ? 'S' : 'N', + pszDriverName, + (int)psStruct->nPID, + poDS->GetRasterXSize(), + poDS->GetRasterYSize(), + poDS->GetRasterCount(), + poDS->GetDescription() ); + + return TRUE; +} + + +static int GDALDumpOpenDatasetsForeach(GDALDataset* poDS, FILE *fp) +{ + const char *pszDriverName; + + /* Don't list shared datasets. They have already been listed by */ + /* GDALDumpOpenSharedDatasetsForeach */ + if (poDS->GetShared()) + return TRUE; + + if( poDS->GetDriver() == NULL ) + pszDriverName = "DriverIsNULL"; + else + pszDriverName = poDS->GetDriver()->GetDescription(); + + poDS->Reference(); + VSIFPrintf( fp, " %d %c %-6s %7d %dx%dx%d %s\n", + poDS->Dereference(), + poDS->GetShared() ? 'S' : 'N', + pszDriverName, + -1, + poDS->GetRasterXSize(), + poDS->GetRasterYSize(), + poDS->GetRasterCount(), + poDS->GetDescription() ); + + return TRUE; +} + +/** + * \brief List open datasets. + * + * Dumps a list of all open datasets (shared or not) to the indicated + * text file (may be stdout or stderr). This function is primarily intended + * to assist in debugging "dataset leaks" and reference counting issues. + * The information reported includes the dataset name, referenced count, + * shared status, driver name, size, and band count. + */ + +int CPL_STDCALL GDALDumpOpenDatasets( FILE *fp ) + +{ + VALIDATE_POINTER1( fp, "GDALDumpOpenDatasets", 0 ); + + CPLMutexHolderD( &hDLMutex ); + + if (poAllDatasetMap != NULL) + { + VSIFPrintf( fp, "Open GDAL Datasets:\n" ); + std::map::iterator oIter = poAllDatasetMap->begin(); + for(; oIter != poAllDatasetMap->end(); ++oIter ) + { + GDALDumpOpenDatasetsForeach(oIter->first, fp); + } + if (phSharedDatasetSet != NULL) + { + CPLHashSetForeach(phSharedDatasetSet, GDALDumpOpenSharedDatasetsForeach, fp); + } + return (int)poAllDatasetMap->size(); + } + else + { + return 0; + } +} + +/************************************************************************/ +/* BeginAsyncReader() */ +/************************************************************************/ + +/** + * \brief Sets up an asynchronous data request + * + * This method establish an asynchronous raster read request for the + * indicated window on the dataset into the indicated buffer. The parameters + * for windowing, buffer size, buffer type and buffer organization are similar + * to those for GDALDataset::RasterIO(); however, this call only launches + * the request and filling the buffer is accomplished via calls to + * GetNextUpdatedRegion() on the return GDALAsyncReader session object. + * + * Once all processing for the created session is complete, or if no further + * refinement of the request is required, the GDALAsyncReader object should + * be destroyed with the GDALDataset::EndAsyncReader() method. + * + * Note that the data buffer (pData) will potentially continue to be + * updated as long as the session lives, but it is not deallocated when + * the session (GDALAsyncReader) is destroyed with EndAsyncReader(). It + * should be deallocated by the application at that point. + * + * Additional information on asynchronous IO in GDAL may be found at: + * http://trac.osgeo.org/gdal/wiki/rfc24_progressive_data_support + * + * This method is the same as the C GDALBeginAsyncReader() function. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pBuf The buffer into which the data should be read. This buffer must + * contain at least nBufXSize * nBufYSize * nBandCount words of type eBufType. + * It is organized in left to right,top to bottom pixel order. Spacing is + * controlled by the nPixelSpace, and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nBandSpace the byte offset from the start of one bands data to the + * start of the next. If defaulted (zero) the value will be + * nLineSpace * nBufYSize implying band sequential organization + * of the data buffer. + * + * @param papszOptions Driver specific control options in a string list or NULL. + * Consult driver documentation for options supported. + * + * @return The GDALAsyncReader object representing the request. + */ + +GDALAsyncReader* +GDALDataset::BeginAsyncReader(int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions) +{ + // See gdaldefaultasync.cpp + + return + GDALGetDefaultAsyncReader( this, + nXOff, nYOff, nXSize, nYSize, + pBuf, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + papszOptions ); +} + +/************************************************************************/ +/* GDALBeginAsyncReader() */ +/************************************************************************/ + +GDALAsyncReaderH CPL_STDCALL +GDALBeginAsyncReader(GDALDatasetH hDS, int xOff, int yOff, + int xSize, int ySize, + void *pBuf, + int bufXSize, int bufYSize, + GDALDataType bufType, + int nBandCount, int* bandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, + char **papszOptions) + +{ + VALIDATE_POINTER1( hDS, "GDALDataset", NULL ); + return (GDALAsyncReaderH)((GDALDataset *) hDS)-> + BeginAsyncReader(xOff, yOff, + xSize, ySize, + pBuf, bufXSize, bufYSize, + bufType, nBandCount, bandMap, + nPixelSpace, nLineSpace, + nBandSpace, papszOptions); +} + +/************************************************************************/ +/* EndAsyncReader() */ +/************************************************************************/ + +/** + * End asynchronous request. + * + * This method destroys an asynchronous io request and recovers all + * resources associated with it. + * + * This method is the same as the C function GDALEndAsyncReader(). + * + * @param poARIO pointer to a GDALAsyncReader + */ + +void GDALDataset::EndAsyncReader(GDALAsyncReader *poARIO ) +{ + delete poARIO; +} + +/************************************************************************/ +/* GDALEndAsyncReader() */ +/************************************************************************/ +void CPL_STDCALL GDALEndAsyncReader(GDALDatasetH hDS, GDALAsyncReaderH hAsyncReaderH) +{ + VALIDATE_POINTER0( hDS, "GDALDataset" ); + VALIDATE_POINTER0( hAsyncReaderH, "GDALAsyncReader" ); + ((GDALDataset *) hDS) -> EndAsyncReader((GDALAsyncReader *)hAsyncReaderH); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +/** + * Drop references to any other datasets referenced by this dataset. + * + * This method should release any reference to other datasets (e.g. a VRT + * dataset to its sources), but not close the current dataset itself. + * + * If at least, one reference to a dependent dataset has been dropped, + * this method should return TRUE. Otherwise it *should* return FALSE. + * (Failure to return the proper value might result in infinite loop) + * + * This method can be called several times on a given dataset. After + * the first time, it should not do anything and return FALSE. + * + * The driver implementation may choose to destroy its raster bands, + * so be careful not to call any method on the raster bands afterwards. + * + * Basically the only safe action you can do after calling CloseDependantDatasets() + * is to call the destructor. + * + * Note: the only legitimate caller of CloseDependantDatasets() is + * GDALDriverManager::~GDALDriverManager() + * + * @return TRUE if at least one reference to another dataset has been dropped. + */ +int GDALDataset::CloseDependentDatasets() +{ + return oOvManager.CloseDependentDatasets(); +} + +/************************************************************************/ +/* ReportError() */ +/************************************************************************/ + +/** + * \brief Emits an error related to a dataset. + * + * This function is a wrapper for regular CPLError(). The only difference + * with CPLError() is that it prepends the error message with the dataset + * name. + * + * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. + * @param err_no the error number (CPLE_*) from cpl_error.h. + * @param fmt a printf() style format string. Any additional arguments + * will be treated as arguments to fill in this format in a manner + * similar to printf(). + * + * @since GDAL 1.9.0 + */ + +void GDALDataset::ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + char szNewFmt[256]; + const char* pszDSName = GetDescription(); + if (strlen(fmt) + strlen(pszDSName) + 3 >= sizeof(szNewFmt) - 1) + pszDSName = CPLGetFilename(pszDSName); + if (pszDSName[0] != '\0' && + strlen(fmt) + strlen(pszDSName) + 3 < sizeof(szNewFmt) - 1) + { + snprintf(szNewFmt, sizeof(szNewFmt), "%s: %s", + pszDSName, fmt); + CPLErrorV( eErrClass, err_no, szNewFmt, args ); + } + else + { + CPLErrorV( eErrClass, err_no, fmt, args ); + } + va_end(args); +} + +/************************************************************************/ +/* GetDriverName() */ +/************************************************************************/ + +const char* GDALDataset::GetDriverName() +{ + if( poDriver ) + return poDriver->GetDescription(); + return ""; +} + +/************************************************************************/ +/* GDALDatasetReleaseResultSet() */ +/************************************************************************/ + +/** + \brief Release results of ExecuteSQL(). + + This function should only be used to deallocate OGRLayers resulting from + an ExecuteSQL() call on the same GDALDataset. Failure to deallocate a + results set before destroying the GDALDataset may cause errors. + + This function is the same as the C++ method GDALDataset::ReleaseResultSet() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param poResultsSet the result of a previous ExecuteSQL() call. + +*/ +void GDALDatasetReleaseResultSet( GDALDatasetH hDS, OGRLayerH hLayer ) + +{ + VALIDATE_POINTER0( hDS, "GDALDatasetReleaseResultSet" ); + + ((GDALDataset *) hDS)->ReleaseResultSet( (OGRLayer *) hLayer ); +} + +/************************************************************************/ +/* GDALDatasetTestCapability() */ +/************************************************************************/ + +/** + \brief Test if capability is available. + + One of the following dataset capability names can be passed into this + function, and a TRUE or FALSE value will be returned indicating whether or not + the capability is available for this object. + +
      +
    • ODsCCreateLayer: True if this datasource can create new layers.

      +

    • ODsCDeleteLayer: True if this datasource can delete existing layers.

      +

    • ODsCCreateGeomFieldAfterCreateLayer: True if the layers of this + datasource support CreateGeomField() just after layer creation.

      +

    + + The \#define macro forms of the capability names should be used in preference + to the strings themselves to avoid mispelling. + + This function is the same as the C++ method GDALDataset::TestCapability() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param pszCapability the capability to test. + + @return TRUE if capability available otherwise FALSE. + +*/ +int GDALDatasetTestCapability( GDALDatasetH hDS, const char *pszCap ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetTestCapability", 0 ); + VALIDATE_POINTER1( pszCap, "GDALDatasetTestCapability", 0 ); + + return ((GDALDataset *) hDS)->TestCapability( pszCap ); +} + +/************************************************************************/ +/* GDALDatasetGetLayerCount() */ +/************************************************************************/ + +/** + \brief Get the number of layers in this dataset. + + This function is the same as the C++ method GDALDataset::GetLayerCount() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @return layer count. +*/ + +int GDALDatasetGetLayerCount( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetH", 0 ); + + return ((GDALDataset *)hDS)->GetLayerCount(); +} + +/************************************************************************/ +/* GDALDatasetGetLayer() */ +/************************************************************************/ + +/** + \brief Fetch a layer by index. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This function is the same as the C++ method GDALDataset::GetLayer() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param iLayer a layer number between 0 and GetLayerCount()-1. + + @return the layer, or NULL if iLayer is out of range or an error occurs. +*/ + +OGRLayerH GDALDatasetGetLayer( GDALDatasetH hDS, int iLayer ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetGetLayer", NULL ); + + return (OGRLayerH) ((GDALDataset*)hDS)->GetLayer( iLayer ); +} + +/************************************************************************/ +/* GDALDatasetGetLayerByName() */ +/************************************************************************/ + +/** + \brief Fetch a layer by name. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This function is the same as the C++ method GDALDataset::GetLayerByName() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param pszLayerName the layer name of the layer to fetch. + + @return the layer, or NULL if Layer is not found or an error occurs. +*/ + +OGRLayerH GDALDatasetGetLayerByName( GDALDatasetH hDS, const char *pszName ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetGetLayerByName", NULL ); + + return (OGRLayerH) ((GDALDataset *) hDS)->GetLayerByName( pszName ); +} + +/************************************************************************/ +/* GDALDatasetDeleteLayer() */ +/************************************************************************/ + +/** + \brief Delete the indicated layer from the datasource. + + If this function is supported + the ODsCDeleteLayer capability will test TRUE on the GDALDataset. + + This method is the same as the C++ method GDALDataset::DeleteLayer(). + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param iLayer the index of the layer to delete. + + @return OGRERR_NONE on success, or OGRERR_UNSUPPORTED_OPERATION if deleting + layers is not supported for this datasource. + +*/ +OGRErr GDALDatasetDeleteLayer( GDALDatasetH hDS, int iLayer ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetH", OGRERR_INVALID_HANDLE ); + + return ((GDALDataset *) hDS)->DeleteLayer( iLayer ); +} + +/************************************************************************/ +/* CreateLayer() */ +/************************************************************************/ + +/** +\brief This method attempts to create a new layer on the dataset with the indicated name, coordinate system, geometry type. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + + In GDAL 2.0, drivers should extend the ICreateLayer() method and not CreateLayer(). + CreateLayer() adds validation of layer creation options, before delegating the + actual work to ICreateLayer(). + + This method is the same as the C function GDALDatasetCreateLayer() and the + deprecated OGR_DS_CreateLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param hDS the dataset handle + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param poSpatialRef the coordinate system to use for the new layer, or NULL if +no coordinate system is available. + @param eGType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific. + + @return NULL is returned on failure, or a new OGRLayer handle on success. + +Example: + +\code +#include "gdal.h" +#include "cpl_string.h" + +... + + OGRLayer *poLayer; + char **papszOptions; + + if( !poDS->TestCapability( ODsCCreateLayer ) ) + { + ... + } + + papszOptions = CSLSetNameValue( papszOptions, "DIM", "2" ); + poLayer = poDS->CreateLayer( "NewLayer", NULL, wkbUnknown, + papszOptions ); + CSLDestroy( papszOptions ); + + if( poLayer == NULL ) + { + ... + } +\endcode +*/ + +OGRLayer *GDALDataset::CreateLayer( const char * pszName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + char **papszOptions ) + +{ + ValidateLayerCreationOptions( papszOptions ); + return ICreateLayer(pszName, poSpatialRef, eGType, papszOptions); +} + +/************************************************************************/ +/* GDALDatasetCreateLayer() */ +/************************************************************************/ + +/** +\brief This function attempts to create a new layer on the dataset with the indicated name, coordinate system, geometry type. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + + This method is the same as the C++ method GDALDataset::CreateLayer(). + + @since GDAL 2.0 + + @param hDS the dataset handle + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param poSpatialRef the coordinate system to use for the new layer, or NULL if +no coordinate system is available. + @param eGType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific. + + @return NULL is returned on failure, or a new OGRLayer handle on success. + +Example: + +\code +#include "gdal.h" +#include "cpl_string.h" + +... + + OGRLayer *poLayer; + char **papszOptions; + + if( !poDS->TestCapability( ODsCCreateLayer ) ) + { + ... + } + + papszOptions = CSLSetNameValue( papszOptions, "DIM", "2" ); + poLayer = poDS->CreateLayer( "NewLayer", NULL, wkbUnknown, + papszOptions ); + CSLDestroy( papszOptions ); + + if( poLayer == NULL ) + { + ... + } +\endcode +*/ + +OGRLayerH GDALDatasetCreateLayer( GDALDatasetH hDS, + const char * pszName, + OGRSpatialReferenceH hSpatialRef, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetCreateLayer", NULL ); + + if (pszName == NULL) + { + CPLError ( CE_Failure, CPLE_ObjectNull, "Name was NULL in GDALDatasetCreateLayer"); + return 0; + } + return (OGRLayerH) ((GDALDataset *)hDS)->CreateLayer( + pszName, (OGRSpatialReference *) hSpatialRef, eType, papszOptions ); +} + + +/************************************************************************/ +/* GDALDatasetCopyLayer() */ +/************************************************************************/ + +/** + \brief Duplicate an existing layer. + + This function creates a new layer, duplicate the field definitions of the + source layer and then duplicate each features of the source layer. + The papszOptions argument + can be used to control driver specific creation options. These options are + normally documented in the format specific documentation. + The source layer may come from another dataset. + + This method is the same as the C++ method GDALDataset::CopyLayer() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param hSrcLayer source layer. + @param pszNewName the name of the layer to create. + @param papszOptions a StringList of name=value options. Options are driver + specific. + + @return an handle to the layer, or NULL if an error occurs. +*/ +OGRLayerH GDALDatasetCopyLayer( GDALDatasetH hDS, + OGRLayerH hSrcLayer, const char *pszNewName, + char **papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_CopyGDALDatasetCopyLayerLayer", NULL ); + VALIDATE_POINTER1( hSrcLayer, "GDALDatasetCopyLayer", NULL ); + VALIDATE_POINTER1( pszNewName, "GDALDatasetCopyLayer", NULL ); + + return (OGRLayerH) + ((GDALDataset *) hDS)->CopyLayer( (OGRLayer *) hSrcLayer, + pszNewName, papszOptions ); +} + +/************************************************************************/ +/* GDALDatasetExecuteSQL() */ +/************************************************************************/ + +/** + \brief Execute an SQL statement against the data store. + + The result of an SQL query is either NULL for statements that are in error, + or that have no results set, or an OGRLayer pointer representing a results + set from the query. Note that this OGRLayer is in addition to the layers + in the data store and must be destroyed with + ReleaseResultSet() before the dataset is closed + (destroyed). + + This method is the same as the C++ method GDALDataset::ExecuteSQL() + + For more information on the SQL dialect supported internally by OGR + review the OGR SQL document. Some drivers (ie. + Oracle and PostGIS) pass the SQL directly through to the underlying RDBMS. + + Starting with OGR 1.10, the SQLITE dialect + can also be used. + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param pszStatement the SQL statement to execute. + @param hSpatialFilter geometry which represents a spatial filter. Can be NULL. + @param pszDialect allows control of the statement dialect. If set to NULL, the +OGR SQL engine will be used, except for RDBMS drivers that will use their dedicated SQL engine, +unless OGRSQL is explicitely passed as the dialect. Starting with OGR 1.10, the SQLITE dialect +can also be used. + + @return an OGRLayer containing the results of the query. Deallocate with + ReleaseResultSet(). + +*/ + +OGRLayerH GDALDatasetExecuteSQL( GDALDatasetH hDS, + const char *pszStatement, + OGRGeometryH hSpatialFilter, + const char *pszDialect ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetExecuteSQL", NULL ); + + return (OGRLayerH) + ((GDALDataset *)hDS)->ExecuteSQL( pszStatement, + (OGRGeometry *) hSpatialFilter, + pszDialect ); +} + +/************************************************************************/ +/* GDALDatasetGetStyleTable() */ +/************************************************************************/ + +/** + \brief Returns dataset style table. + + This function is the same as the C++ method GDALDataset::GetStyleTable() + + @since GDAL 2.0 + + @param hDS the dataset handle + @return handle to a style table which should not be modified or freed by the + caller. +*/ + +OGRStyleTableH GDALDatasetGetStyleTable( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetStyleTable", NULL ); + + return (OGRStyleTableH) ((GDALDataset *) hDS)->GetStyleTable( ); +} + +/************************************************************************/ +/* GDALDatasetSetStyleTableDirectly() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This function operate exactly as GDALDatasetSetStyleTable() except that it + assumes ownership of the passed table. + + This function is the same as the C++ method GDALDataset::SetStyleTableDirectly() + + @since GDAL 2.0 + + @param hDS the dataset handle + @param hStyleTable style table handle to set + +*/ + +void GDALDatasetSetStyleTableDirectly( GDALDatasetH hDS, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTableDirectly" ); + + ((GDALDataset *) hDS)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* GDALDatasetSetStyleTable() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This function operate exactly as GDALDatasetSetStyleTableDirectly() except that it + assumes ownership of the passed table. + + This function is the same as the C++ method GDALDataset::SetStyleTable() + + @since GDAL 2.0 + + @param hDS the dataset handle + @param hStyleTable style table handle to set + +*/ + +void GDALDatasetSetStyleTable( GDALDatasetH hDS, OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTable" ); + VALIDATE_POINTER0( hStyleTable, "OGR_DS_SetStyleTable" ); + + ((GDALDataset *) hDS)->SetStyleTable( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* ValidateLayerCreationOptions() */ +/************************************************************************/ + +int GDALDataset::ValidateLayerCreationOptions( const char* const* papszLCO ) +{ + const char *pszOptionList = GetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST ); + if( pszOptionList == NULL && poDriver != NULL ) + { + pszOptionList = + poDriver->GetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST ); + } + CPLString osDataset; + osDataset.Printf("dataset %s", GetDescription()); + return GDALValidateOptions( pszOptionList, papszLCO, + "layer creation option", + osDataset ); +} + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +/** + \fn OGRErr OGRDataSource::Release(); + +\brief Drop a reference to this dataset, and if the reference count drops to one close (destroy) the dataset. + +This method is the same as the C function OGRReleaseDataSource(). + +@deprecated. In GDAL 2, use GDALClose() instead + +@return OGRERR_NONE on success or an error code. +*/ + +OGRErr GDALDataset::Release() + +{ + GDALClose( (GDALDatasetH) this ); + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetRefCount() */ +/************************************************************************/ + +/** +\brief Fetch reference count. + +This method is the same as the C function OGR_DS_GetRefCount(). + +In GDAL 1.X, this method used to be in the OGRDataSource class. + +@return the current reference count for the datasource object itself. +*/ + + +int GDALDataset::GetRefCount() const + +{ + return nRefCount; +} + +/************************************************************************/ +/* GetSummaryRefCount() */ +/************************************************************************/ + +/** +\brief Fetch reference count of datasource and all owned layers. + +This method is the same as the C function OGR_DS_GetSummaryRefCount(). + +In GDAL 1.X, this method used to be in the OGRDataSource class. + +@deprecated + +@return the current summary reference count for the datasource and its layers. +*/ + +int GDALDataset::GetSummaryRefCount() const + +{ + CPLMutexHolderD( (void **) &m_hMutex ); + int nSummaryCount = nRefCount; + int iLayer; + GDALDataset *poUseThis = (GDALDataset *) this; + + for( iLayer=0; iLayer < poUseThis->GetLayerCount(); iLayer++ ) + nSummaryCount += poUseThis->GetLayer( iLayer )->GetRefCount(); + + return nSummaryCount; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +/** +\brief This method attempts to create a new layer on the dataset with the indicated name, coordinate system, geometry type. + +This method is reserved to implementation by drivers. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param poSpatialRef the coordinate system to use for the new layer, or NULL if +no coordinate system is available. + @param eGType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific. + + @return NULL is returned on failure, or a new OGRLayer handle on success. + + @since GDAL 2.0 +*/ + +OGRLayer *GDALDataset::ICreateLayer( const char * pszName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + char **papszOptions ) + +{ + (void) eGType; + (void) poSpatialRef; + (void) pszName; + (void) papszOptions; + + CPLError( CE_Failure, CPLE_NotSupported, + "CreateLayer() not supported by this dataset." ); + + return NULL; +} + +/************************************************************************/ +/* CopyLayer() */ +/************************************************************************/ + +/** + \brief Duplicate an existing layer. + + This method creates a new layer, duplicate the field definitions of the + source layer and then duplicate each features of the source layer. + The papszOptions argument + can be used to control driver specific creation options. These options are + normally documented in the format specific documentation. + The source layer may come from another dataset. + + This method is the same as the C function GDALDatasetCopyLayer() and the + deprecated OGR_DS_CopyLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poSrcLayer source layer. + @param pszNewName the name of the layer to create. + @param papszOptions a StringList of name=value options. Options are driver + specific. + + @return an handle to the layer, or NULL if an error occurs. +*/ + +OGRLayer *GDALDataset::CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions ) + +{ + OGRFeatureDefn *poSrcDefn = poSrcLayer->GetLayerDefn(); + OGRLayer *poDstLayer = NULL; + +/* -------------------------------------------------------------------- */ +/* Create the layer. */ +/* -------------------------------------------------------------------- */ + if( !TestCapability( ODsCCreateLayer ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "This datasource does not support creation of layers." ); + return NULL; + } + + CPLErrorReset(); + if( poSrcDefn->GetGeomFieldCount() > 1 && + TestCapability(ODsCCreateGeomFieldAfterCreateLayer) ) + { + poDstLayer =ICreateLayer( pszNewName, NULL, wkbNone, papszOptions ); + } + else + { + poDstLayer =ICreateLayer( pszNewName, poSrcLayer->GetSpatialRef(), + poSrcDefn->GetGeomType(), papszOptions ); + } + + if( poDstLayer == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Add fields. Default to copy all fields, and make sure to */ +/* establish a mapping between indices, rather than names, in */ +/* case the target datasource has altered it (e.g. Shapefile */ +/* limited to 10 char field names). */ +/* -------------------------------------------------------------------- */ + int nSrcFieldCount = poSrcDefn->GetFieldCount(); + int nDstFieldCount = 0; + int iField, *panMap; + + // Initialize the index-to-index map to -1's + panMap = (int *) CPLMalloc( sizeof(int) * nSrcFieldCount ); + for( iField=0; iField < nSrcFieldCount; iField++) + panMap[iField] = -1; + + /* Caution : at the time of writing, the MapInfo driver */ + /* returns NULL until a field has been added */ + OGRFeatureDefn* poDstFDefn = poDstLayer->GetLayerDefn(); + if (poDstFDefn) + nDstFieldCount = poDstFDefn->GetFieldCount(); + for( iField = 0; iField < nSrcFieldCount; iField++ ) + { + OGRFieldDefn* poSrcFieldDefn = poSrcDefn->GetFieldDefn(iField); + OGRFieldDefn oFieldDefn( poSrcFieldDefn ); + + /* The field may have been already created at layer creation */ + int iDstField = -1; + if (poDstFDefn) + iDstField = poDstFDefn->GetFieldIndex(oFieldDefn.GetNameRef()); + if (iDstField >= 0) + { + panMap[iField] = iDstField; + } + else if (poDstLayer->CreateField( &oFieldDefn ) == OGRERR_NONE) + { + /* now that we've created a field, GetLayerDefn() won't return NULL */ + if (poDstFDefn == NULL) + poDstFDefn = poDstLayer->GetLayerDefn(); + + /* Sanity check : if it fails, the driver is buggy */ + if (poDstFDefn != NULL && + poDstFDefn->GetFieldCount() != nDstFieldCount + 1) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The output driver has claimed to have added the %s field, but it did not!", + oFieldDefn.GetNameRef() ); + } + else + { + panMap[iField] = nDstFieldCount; + nDstFieldCount ++; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create geometry fields. */ +/* -------------------------------------------------------------------- */ + if( poSrcDefn->GetGeomFieldCount() > 1 && + TestCapability(ODsCCreateGeomFieldAfterCreateLayer) ) + { + int nSrcGeomFieldCount = poSrcDefn->GetGeomFieldCount(); + for( iField = 0; iField < nSrcGeomFieldCount; iField++ ) + { + poDstLayer->CreateGeomField( poSrcDefn->GetGeomFieldDefn(iField) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Check if the destination layer supports transactions and set a */ +/* default number of features in a single transaction. */ +/* -------------------------------------------------------------------- */ + int nGroupTransactions = 0; + if( poDstLayer->TestCapability( OLCTransactions ) ) + nGroupTransactions = 128; + +/* -------------------------------------------------------------------- */ +/* Transfer features. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poSrcLayer->ResetReading(); + + if( nGroupTransactions <= 0 ) + { + while( TRUE ) + { + OGRFeature *poDstFeature = NULL; + + poFeature = poSrcLayer->GetNextFeature(); + + if( poFeature == NULL ) + break; + + CPLErrorReset(); + poDstFeature = OGRFeature::CreateFeature( poDstLayer->GetLayerDefn() ); + + if( poDstFeature->SetFrom( poFeature, panMap, TRUE ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to translate feature %ld from layer %s.\n", + poFeature->GetFID(), poSrcDefn->GetName() ); + OGRFeature::DestroyFeature( poFeature ); + CPLFree(panMap); + return poDstLayer; + } + + poDstFeature->SetFID( poFeature->GetFID() ); + + OGRFeature::DestroyFeature( poFeature ); + + CPLErrorReset(); + if( poDstLayer->CreateFeature( poDstFeature ) != OGRERR_NONE ) + { + OGRFeature::DestroyFeature( poDstFeature ); + CPLFree(panMap); + return poDstLayer; + } + + OGRFeature::DestroyFeature( poDstFeature ); + } + } + else + { + int i, bStopTransfer = FALSE, bStopTransaction = FALSE; + int nFeatCount = 0; // Number of features in the temporary array + int nFeaturesToAdd = 0; + OGRFeature **papoDstFeature = + (OGRFeature **)CPLCalloc(sizeof(OGRFeature *), nGroupTransactions); + while( !bStopTransfer ) + { +/* -------------------------------------------------------------------- */ +/* Fill the array with features */ +/* -------------------------------------------------------------------- */ + for( nFeatCount = 0; nFeatCount < nGroupTransactions; nFeatCount++ ) + { + poFeature = poSrcLayer->GetNextFeature(); + + if( poFeature == NULL ) + { + bStopTransfer = 1; + break; + } + + CPLErrorReset(); + papoDstFeature[nFeatCount] = + OGRFeature::CreateFeature( poDstLayer->GetLayerDefn() ); + + if( papoDstFeature[nFeatCount]->SetFrom( poFeature, panMap, TRUE ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to translate feature %ld from layer %s.\n", + poFeature->GetFID(), poSrcDefn->GetName() ); + OGRFeature::DestroyFeature( poFeature ); + bStopTransfer = TRUE; + break; + } + + papoDstFeature[nFeatCount]->SetFID( poFeature->GetFID() ); + + OGRFeature::DestroyFeature( poFeature ); + } + nFeaturesToAdd = nFeatCount; + + CPLErrorReset(); + bStopTransaction = FALSE; + while( !bStopTransaction ) + { + bStopTransaction = TRUE; + poDstLayer->StartTransaction(); + for( i = 0; i < nFeaturesToAdd; i++ ) + { + if( poDstLayer->CreateFeature( papoDstFeature[i] ) != OGRERR_NONE ) + { + nFeaturesToAdd = i; + bStopTransfer = TRUE; + bStopTransaction = FALSE; + } + } + if( bStopTransaction ) + poDstLayer->CommitTransaction(); + else + poDstLayer->RollbackTransaction(); + } + + for( i = 0; i < nFeatCount; i++ ) + OGRFeature::DestroyFeature( papoDstFeature[i] ); + } + CPLFree(papoDstFeature); + } + + CPLFree(panMap); + + return poDstLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +/** + \brief Delete the indicated layer from the datasource. + + If this method is supported + the ODsCDeleteLayer capability will test TRUE on the GDALDataset. + + This method is the same as the C function GDALDatasetDeleteLayer() and the + deprecated OGR_DS_DeleteLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param iLayer the index of the layer to delete. + + @return OGRERR_NONE on success, or OGRERR_UNSUPPORTED_OPERATION if deleting + layers is not supported for this datasource. + +*/ +OGRErr GDALDataset::DeleteLayer( int iLayer ) + +{ + (void) iLayer; + CPLError( CE_Failure, CPLE_NotSupported, + "DeleteLayer() not supported by this dataset." ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +/** + \brief Fetch a layer by name. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This method is the same as the C function GDALDatasetGetLayerByName() and the + deprecated OGR_DS_GetLayerByName(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param pszLayerName the layer name of the layer to fetch. + + @return the layer, or NULL if Layer is not found or an error occurs. +*/ + +OGRLayer *GDALDataset::GetLayerByName( const char *pszName ) + +{ + CPLMutexHolderD( &m_hMutex ); + + if ( ! pszName ) + return NULL; + + int i; + + /* first a case sensitive check */ + for( i = 0; i < GetLayerCount(); i++ ) + { + OGRLayer *poLayer = GetLayer(i); + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + return poLayer; + } + + /* then case insensitive */ + for( i = 0; i < GetLayerCount(); i++ ) + { + OGRLayer *poLayer = GetLayer(i); + + if( EQUAL( pszName, poLayer->GetName() ) ) + return poLayer; + } + + return NULL; +} + +/************************************************************************/ +/* ProcessSQLCreateIndex() */ +/* */ +/* The correct syntax for creating an index in our dialect of */ +/* SQL is: */ +/* */ +/* CREATE INDEX ON USING */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLCreateIndex( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszTokens) != 6 + || !EQUAL(papszTokens[0],"CREATE") + || !EQUAL(papszTokens[1],"INDEX") + || !EQUAL(papszTokens[2],"ON") + || !EQUAL(papszTokens[4],"USING") ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in CREATE INDEX command.\n" + "Was '%s'\n" + "Should be of form 'CREATE INDEX ON USING '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + int i; + OGRLayer *poLayer = NULL; + + { + CPLMutexHolderD( &m_hMutex ); + + for( i = 0; i < GetLayerCount(); i++ ) + { + poLayer = GetLayer(i); + + if( EQUAL(poLayer->GetName(),papszTokens[3]) ) + break; + } + + if( i >= GetLayerCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CREATE INDEX ON failed, no such layer as `%s'.", + papszTokens[3] ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Does this layer even support attribute indexes? */ +/* -------------------------------------------------------------------- */ + if( poLayer->GetIndex() == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CREATE INDEX ON not supported by this driver." ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + if( EQUAL(papszTokens[5], + poLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()) ) + break; + } + + CSLDestroy( papszTokens ); + + if( i >= poLayer->GetLayerDefn()->GetFieldCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "`%s' failed, field not found.", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Attempt to create the index. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + eErr = poLayer->GetIndex()->CreateIndex( i ); + if( eErr == OGRERR_NONE ) + eErr = poLayer->GetIndex()->IndexAllFeatures( i ); + else + { + if( strlen(CPLGetLastErrorMsg()) == 0 ) + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot '%s'", pszSQLCommand); + } + + return eErr; +} + +/************************************************************************/ +/* ProcessSQLDropIndex() */ +/* */ +/* The correct syntax for droping one or more indexes in */ +/* the OGR SQL dialect is: */ +/* */ +/* DROP INDEX ON [USING ] */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLDropIndex( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + if( (CSLCount(papszTokens) != 4 && CSLCount(papszTokens) != 6) + || !EQUAL(papszTokens[0],"DROP") + || !EQUAL(papszTokens[1],"INDEX") + || !EQUAL(papszTokens[2],"ON") + || (CSLCount(papszTokens) == 6 && !EQUAL(papszTokens[4],"USING")) ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in DROP INDEX command.\n" + "Was '%s'\n" + "Should be of form 'DROP INDEX ON
    [USING ]'", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + int i; + OGRLayer *poLayer=NULL; + + { + CPLMutexHolderD( &m_hMutex ); + + for( i = 0; i < GetLayerCount(); i++ ) + { + poLayer = GetLayer(i); + + if( EQUAL(poLayer->GetName(),papszTokens[3]) ) + break; + } + + if( i >= GetLayerCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CREATE INDEX ON failed, no such layer as `%s'.", + papszTokens[3] ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Does this layer even support attribute indexes? */ +/* -------------------------------------------------------------------- */ + if( poLayer->GetIndex() == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Indexes not supported by this driver." ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* If we weren't given a field name, drop all indexes. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + if( CSLCount(papszTokens) == 4 ) + { + for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + OGRAttrIndex *poAttrIndex; + + poAttrIndex = poLayer->GetIndex()->GetFieldIndex(i); + if( poAttrIndex != NULL ) + { + eErr = poLayer->GetIndex()->DropIndex( i ); + if( eErr != OGRERR_NONE ) + { + CSLDestroy(papszTokens); + return eErr; + } + } + } + + CSLDestroy(papszTokens); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + if( EQUAL(papszTokens[5], + poLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()) ) + break; + } + + CSLDestroy( papszTokens ); + + if( i >= poLayer->GetLayerDefn()->GetFieldCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "`%s' failed, field not found.", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Attempt to drop the index. */ +/* -------------------------------------------------------------------- */ + eErr = poLayer->GetIndex()->DropIndex( i ); + + return eErr; +} + +/************************************************************************/ +/* ProcessSQLDropTable() */ +/* */ +/* The correct syntax for dropping a table (layer) in the OGR SQL */ +/* dialect is: */ +/* */ +/* DROP TABLE */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLDropTable( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszTokens) != 3 + || !EQUAL(papszTokens[0],"DROP") + || !EQUAL(papszTokens[1],"TABLE") ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in DROP TABLE command.\n" + "Was '%s'\n" + "Should be of form 'DROP TABLE
    '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + int i; + OGRLayer *poLayer=NULL; + + for( i = 0; i < GetLayerCount(); i++ ) + { + poLayer = GetLayer(i); + + if( EQUAL(poLayer->GetName(),papszTokens[2]) ) + break; + } + + if( i >= GetLayerCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DROP TABLE failed, no such layer as `%s'.", + papszTokens[2] ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Delete it. */ +/* -------------------------------------------------------------------- */ + + return DeleteLayer( i ); +} + +/************************************************************************/ +/* GDALDatasetParseSQLType() */ +/************************************************************************/ + +/* All arguments will be altered */ +static OGRFieldType GDALDatasetParseSQLType(char* pszType, int& nWidth, int &nPrecision) +{ + char* pszParenthesis = strchr(pszType, '('); + if (pszParenthesis) + { + nWidth = atoi(pszParenthesis + 1); + *pszParenthesis = '\0'; + char* pszComma = strchr(pszParenthesis + 1, ','); + if (pszComma) + nPrecision = atoi(pszComma + 1); + } + + OGRFieldType eType = OFTString; + if (EQUAL(pszType, "INTEGER")) + eType = OFTInteger; + else if (EQUAL(pszType, "INTEGER[]")) + eType = OFTIntegerList; + else if (EQUAL(pszType, "FLOAT") || + EQUAL(pszType, "NUMERIC") || + EQUAL(pszType, "DOUBLE") /* unofficial alias */ || + EQUAL(pszType, "REAL") /* unofficial alias */) + eType = OFTReal; + else if (EQUAL(pszType, "FLOAT[]") || + EQUAL(pszType, "NUMERIC[]") || + EQUAL(pszType, "DOUBLE[]") /* unofficial alias */ || + EQUAL(pszType, "REAL[]") /* unofficial alias */) + eType = OFTRealList; + else if (EQUAL(pszType, "CHARACTER") || + EQUAL(pszType, "TEXT") /* unofficial alias */ || + EQUAL(pszType, "STRING") /* unofficial alias */ || + EQUAL(pszType, "VARCHAR") /* unofficial alias */) + eType = OFTString; + else if (EQUAL(pszType, "TEXT[]") || + EQUAL(pszType, "STRING[]") /* unofficial alias */|| + EQUAL(pszType, "VARCHAR[]") /* unofficial alias */) + eType = OFTStringList; + else if (EQUAL(pszType, "DATE")) + eType = OFTDate; + else if (EQUAL(pszType, "TIME")) + eType = OFTTime; + else if (EQUAL(pszType, "TIMESTAMP") || + EQUAL(pszType, "DATETIME") /* unofficial alias */ ) + eType = OFTDateTime; + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported column type '%s'. Defaulting to VARCHAR", + pszType); + } + return eType; +} + +/************************************************************************/ +/* ProcessSQLAlterTableAddColumn() */ +/* */ +/* The correct syntax for adding a column in the OGR SQL */ +/* dialect is: */ +/* */ +/* ALTER TABLE ADD [COLUMN] */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableAddColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszColumnName = NULL; + char* pszType = NULL; + int iTypeIndex = 0; + int nTokens = CSLCount(papszTokens); + + if( nTokens >= 7 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ADD") + && EQUAL(papszTokens[4],"COLUMN")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[5]; + iTypeIndex = 6; + } + else if( nTokens >= 6 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ADD")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[4]; + iTypeIndex = 5; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE ADD COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE ADD [COLUMN] '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Merge type components into a single string if there were split */ +/* with spaces */ +/* -------------------------------------------------------------------- */ + CPLString osType; + for(int i=iTypeIndex;iCreateField( &oFieldDefn ); +} + +/************************************************************************/ +/* ProcessSQLAlterTableDropColumn() */ +/* */ +/* The correct syntax for droping a column in the OGR SQL */ +/* dialect is: */ +/* */ +/* ALTER TABLE DROP [COLUMN] */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableDropColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszColumnName = NULL; + if( CSLCount(papszTokens) == 6 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"DROP") + && EQUAL(papszTokens[4],"COLUMN")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[5]; + } + else if( CSLCount(papszTokens) == 5 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"DROP")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[4]; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE DROP COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE DROP [COLUMN] '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + OGRLayer *poLayer = GetLayerByName(pszLayerName); + if( poLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such layer as `%s'.", + pszSQLCommand, + pszLayerName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the field. */ +/* -------------------------------------------------------------------- */ + + int nFieldIndex = poLayer->GetLayerDefn()->GetFieldIndex(pszColumnName); + if( nFieldIndex < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such field as `%s'.", + pszSQLCommand, + pszColumnName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + + +/* -------------------------------------------------------------------- */ +/* Remove it. */ +/* -------------------------------------------------------------------- */ + + CSLDestroy( papszTokens ); + + return poLayer->DeleteField( nFieldIndex ); +} + +/************************************************************************/ +/* ProcessSQLAlterTableRenameColumn() */ +/* */ +/* The correct syntax for renaming a column in the OGR SQL */ +/* dialect is: */ +/* */ +/* ALTER TABLE RENAME [COLUMN] TO */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableRenameColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszOldColName = NULL; + const char* pszNewColName = NULL; + if( CSLCount(papszTokens) == 8 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"RENAME") + && EQUAL(papszTokens[4],"COLUMN") + && EQUAL(papszTokens[6],"TO")) + { + pszLayerName = papszTokens[2]; + pszOldColName = papszTokens[5]; + pszNewColName = papszTokens[7]; + } + else if( CSLCount(papszTokens) == 7 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"RENAME") + && EQUAL(papszTokens[5],"TO")) + { + pszLayerName = papszTokens[2]; + pszOldColName = papszTokens[4]; + pszNewColName = papszTokens[6]; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE RENAME COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE RENAME [COLUMN] TO '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + OGRLayer *poLayer = GetLayerByName(pszLayerName); + if( poLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such layer as `%s'.", + pszSQLCommand, + pszLayerName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the field. */ +/* -------------------------------------------------------------------- */ + + int nFieldIndex = poLayer->GetLayerDefn()->GetFieldIndex(pszOldColName); + if( nFieldIndex < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such field as `%s'.", + pszSQLCommand, + pszOldColName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Rename column. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn* poOldFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(nFieldIndex); + OGRFieldDefn oNewFieldDefn(poOldFieldDefn); + oNewFieldDefn.SetName(pszNewColName); + + CSLDestroy( papszTokens ); + + return poLayer->AlterFieldDefn( nFieldIndex, &oNewFieldDefn, ALTER_NAME_FLAG ); +} + +/************************************************************************/ +/* ProcessSQLAlterTableAlterColumn() */ +/* */ +/* The correct syntax for altering the type of a column in the */ +/* OGR SQL dialect is: */ +/* */ +/* ALTER TABLE ALTER [COLUMN] TYPE */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableAlterColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszColumnName = NULL; + char* pszType = NULL; + int iTypeIndex = 0; + int nTokens = CSLCount(papszTokens); + + if( nTokens >= 8 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ALTER") + && EQUAL(papszTokens[4],"COLUMN") + && EQUAL(papszTokens[6],"TYPE")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[5]; + iTypeIndex = 7; + } + else if( nTokens >= 7 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ALTER") + && EQUAL(papszTokens[5],"TYPE")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[4]; + iTypeIndex = 6; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE ALTER COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE ALTER [COLUMN] TYPE '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Merge type components into a single string if there were split */ +/* with spaces */ +/* -------------------------------------------------------------------- */ + CPLString osType; + for(int i=iTypeIndex;iGetLayerDefn()->GetFieldIndex(pszColumnName); + if( nFieldIndex < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such field as `%s'.", + pszSQLCommand, + pszColumnName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Alter column. */ +/* -------------------------------------------------------------------- */ + + OGRFieldDefn* poOldFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(nFieldIndex); + OGRFieldDefn oNewFieldDefn(poOldFieldDefn); + + int nWidth = 0, nPrecision = 0; + OGRFieldType eType = GDALDatasetParseSQLType(pszType, nWidth, nPrecision); + oNewFieldDefn.SetType(eType); + oNewFieldDefn.SetWidth(nWidth); + oNewFieldDefn.SetPrecision(nPrecision); + + int nFlags = 0; + if (poOldFieldDefn->GetType() != oNewFieldDefn.GetType()) + nFlags |= ALTER_TYPE_FLAG; + if (poOldFieldDefn->GetWidth() != oNewFieldDefn.GetWidth() || + poOldFieldDefn->GetPrecision() != oNewFieldDefn.GetPrecision()) + nFlags |= ALTER_WIDTH_PRECISION_FLAG; + + CSLDestroy( papszTokens ); + + if (nFlags == 0) + return OGRERR_NONE; + else + return poLayer->AlterFieldDefn( nFieldIndex, &oNewFieldDefn, nFlags ); +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +/** + \brief Execute an SQL statement against the data store. + + The result of an SQL query is either NULL for statements that are in error, + or that have no results set, or an OGRLayer pointer representing a results + set from the query. Note that this OGRLayer is in addition to the layers + in the data store and must be destroyed with + ReleaseResultSet() before the dataset is closed + (destroyed). + + This method is the same as the C function GDALDatasetExecuteSQL() and the + deprecated OGR_DS_ExecuteSQL(). + + For more information on the SQL dialect supported internally by OGR + review the OGR SQL document. Some drivers (ie. + Oracle and PostGIS) pass the SQL directly through to the underlying RDBMS. + + Starting with OGR 1.10, the SQLITE dialect + can also be used. + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param pszStatement the SQL statement to execute. + @param poSpatialFilter geometry which represents a spatial filter. Can be NULL. + @param pszDialect allows control of the statement dialect. If set to NULL, the +OGR SQL engine will be used, except for RDBMS drivers that will use their dedicated SQL engine, +unless OGRSQL is explicitely passed as the dialect. Starting with OGR 1.10, the SQLITE dialect +can also be used. + + @return an OGRLayer containing the results of the query. Deallocate with + ReleaseResultSet(). + +*/ + +OGRLayer * GDALDataset::ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + swq_select *psSelectInfo = NULL; + + if( pszDialect != NULL && EQUAL(pszDialect, "SQLite") ) + { +#ifdef SQLITE_ENABLED + return OGRSQLiteExecuteSQL( this, pszStatement, poSpatialFilter, pszDialect ); +#else + CPLError(CE_Failure, CPLE_NotSupported, + "The SQLite driver needs to be compiled to support the SQLite SQL dialect"); + return NULL; +#endif + } + +/* -------------------------------------------------------------------- */ +/* Handle CREATE INDEX statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"CREATE INDEX",12) ) + { + ProcessSQLCreateIndex( pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle DROP INDEX statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"DROP INDEX",10) ) + { + ProcessSQLDropIndex( pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle DROP TABLE statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"DROP TABLE",10) ) + { + ProcessSQLDropTable( pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle ALTER TABLE statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"ALTER TABLE",11) ) + { + char **papszTokens = CSLTokenizeString( pszStatement ); + if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"ADD") ) + { + ProcessSQLAlterTableAddColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"DROP") ) + { + ProcessSQLAlterTableDropColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"RENAME") ) + { + ProcessSQLAlterTableRenameColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"ALTER") ) + { + ProcessSQLAlterTableAlterColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ALTER TABLE command : %s", + pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Preparse the SQL statement. */ +/* -------------------------------------------------------------------- */ + psSelectInfo = new swq_select(); + if( psSelectInfo->preparse( pszStatement ) != CPLE_None ) + { + delete psSelectInfo; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If there is no UNION ALL, build result layer. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->poOtherSelect == NULL ) + { + return BuildLayerFromSelectInfo(psSelectInfo, + poSpatialFilter, + pszDialect); + } + +/* -------------------------------------------------------------------- */ +/* Build result union layer. */ +/* -------------------------------------------------------------------- */ + int nSrcLayers = 0; + OGRLayer** papoSrcLayers = NULL; + + do + { + swq_select* psNextSelectInfo = psSelectInfo->poOtherSelect; + psSelectInfo->poOtherSelect = NULL; + + OGRLayer* poLayer = BuildLayerFromSelectInfo(psSelectInfo, + poSpatialFilter, + pszDialect); + if( poLayer == NULL ) + { + /* Each source layer owns an independant select info */ + for(int i=0;itable_count; iTable++ ) + { + swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; + OGRLayer *poSrcLayer; + GDALDataset *poTableDS = this; + + if( psTableDef->data_source != NULL ) + { + poTableDS = (GDALDataset *) + OGROpenShared( psTableDef->data_source, FALSE, NULL ); + if( poTableDS == NULL ) + { + if( strlen(CPLGetLastErrorMsg()) == 0 ) + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open secondary datasource\n" + "`%s' required by JOIN.", + psTableDef->data_source ); + + delete psSelectInfo; + goto end; + } + + /* Keep in an array to release at the end of this function */ + papoExtraDS = (GDALDataset** )CPLRealloc(papoExtraDS, + sizeof(GDALDataset*) * (nExtraDSCount + 1)); + papoExtraDS[nExtraDSCount++] = poTableDS; + } + + poSrcLayer = poTableDS->GetLayerByName( psTableDef->table_name ); + + if( poSrcLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECT from table %s failed, no such table/featureclass.", + psTableDef->table_name ); + delete psSelectInfo; + goto end; + } + + nFieldCount += poSrcLayer->GetLayerDefn()->GetFieldCount(); + if( iTable == 0 ) + nFieldCount += poSrcLayer->GetLayerDefn()->GetGeomFieldCount(); + } + +/* -------------------------------------------------------------------- */ +/* Build the field list for all indicated tables. */ +/* -------------------------------------------------------------------- */ + + sFieldList.table_count = psSelectInfo->table_count; + sFieldList.table_defs = psSelectInfo->table_defs; + + sFieldList.count = 0; + sFieldList.names = (char **) CPLMalloc( sizeof(char *) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + sFieldList.types = (swq_field_type *) + CPLMalloc( sizeof(swq_field_type) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + sFieldList.table_ids = (int *) + CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + sFieldList.ids = (int *) + CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + + for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; + GDALDataset *poTableDS = this; + OGRLayer *poSrcLayer; + + if( psTableDef->data_source != NULL ) + { + poTableDS = (GDALDataset *) + OGROpenShared( psTableDef->data_source, FALSE, NULL ); + CPLAssert( poTableDS != NULL ); + poTableDS->Dereference(); + } + + poSrcLayer = poTableDS->GetLayerByName( psTableDef->table_name ); + + for( iField = 0; + iField < poSrcLayer->GetLayerDefn()->GetFieldCount(); + iField++ ) + { + OGRFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetFieldDefn(iField); + int iOutField = sFieldList.count++; + sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef(); + if( poFDefn->GetType() == OFTInteger ) + sFieldList.types[iOutField] = SWQ_INTEGER; + else if( poFDefn->GetType() == OFTReal ) + sFieldList.types[iOutField] = SWQ_FLOAT; + else if( poFDefn->GetType() == OFTString ) + sFieldList.types[iOutField] = SWQ_STRING; + else if( poFDefn->GetType() == OFTTime ) + sFieldList.types[iOutField] = SWQ_TIME; + else if( poFDefn->GetType() == OFTDate ) + sFieldList.types[iOutField] = SWQ_DATE; + else if( poFDefn->GetType() == OFTDateTime ) + sFieldList.types[iOutField] = SWQ_TIMESTAMP; + else + sFieldList.types[iOutField] = SWQ_OTHER; + + sFieldList.table_ids[iOutField] = iTable; + sFieldList.ids[iOutField] = iField; + } + + if( iTable == 0 ) + { + nFIDIndex = sFieldList.count; + + for( iField = 0; + iField < poSrcLayer->GetLayerDefn()->GetGeomFieldCount(); + iField++ ) + { + OGRGeomFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetGeomFieldDefn(iField); + int iOutField = sFieldList.count++; + sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef(); + if( *sFieldList.names[iOutField] == '\0' ) + sFieldList.names[iOutField] = (char*) OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME; + sFieldList.types[iOutField] = SWQ_GEOMETRY; + + sFieldList.table_ids[iOutField] = iTable; + sFieldList.ids[iOutField] = + GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poSrcLayer->GetLayerDefn(), iField); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Expand '*' in 'SELECT *' now before we add the pseudo fields */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->expand_wildcard( &sFieldList ) != CE_None ) + { + delete psSelectInfo; + goto end; + } + + for (iField = 0; iField < SPECIAL_FIELD_COUNT; iField++) + { + sFieldList.names[sFieldList.count] = (char*) SpecialFieldNames[iField]; + sFieldList.types[sFieldList.count] = SpecialFieldTypes[iField]; + sFieldList.table_ids[sFieldList.count] = 0; + sFieldList.ids[sFieldList.count] = nFIDIndex + iField; + sFieldList.count++; + } + +/* -------------------------------------------------------------------- */ +/* Finish the parse operation. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->parse( &sFieldList, 0 ) != CE_None ) + { + delete psSelectInfo; + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Extract the WHERE expression to use separately. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->where_expr != NULL ) + { + if (EQUAL(GetDriverName(), "PostgreSQL") || + EQUAL(GetDriverName(), "FileGDB" ) ) + pszWHERE = psSelectInfo->where_expr->Unparse( &sFieldList, '"' ); + else + pszWHERE = psSelectInfo->where_expr->Unparse( &sFieldList, '\'' ); + //CPLDebug( "OGR", "Unparse() -> %s", pszWHERE ); + } + +/* -------------------------------------------------------------------- */ +/* Everything seems OK, try to instantiate a results layer. */ +/* -------------------------------------------------------------------- */ + + poResults = new OGRGenSQLResultsLayer( this, psSelectInfo, + poSpatialFilter, + pszWHERE, + pszDialect ); + + CPLFree( pszWHERE ); + + // Eventually, we should keep track of layers to cleanup. + +end: + CPLFree( sFieldList.names ); + CPLFree( sFieldList.types ); + CPLFree( sFieldList.table_ids ); + CPLFree( sFieldList.ids ); + + /* Release the datasets we have opened with OGROpenShared() */ + /* It is safe to do that as the 'new OGRGenSQLResultsLayer' itself */ + /* has taken a reference on them, which it will release in its */ + /* destructor */ + for(iEDS = 0; iEDS < nExtraDSCount; iEDS++) + GDALClose( (GDALDatasetH)papoExtraDS[iEDS] ); + CPLFree(papoExtraDS); + + return poResults; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +/** + \brief Release results of ExecuteSQL(). + + This method should only be used to deallocate OGRLayers resulting from + an ExecuteSQL() call on the same GDALDataset. Failure to deallocate a + results set before destroying the GDALDataset may cause errors. + + This method is the same as the C function GDALDatasetReleaseResultSet() and the + deprecated OGR_DS_ReleaseResultSet(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poResultsSet the result of a previous ExecuteSQL() call. + +*/ +void GDALDataset::ReleaseResultSet( OGRLayer * poResultsSet ) + +{ + delete poResultsSet; +} + +/************************************************************************/ +/* GetStyleTable() */ +/************************************************************************/ + +/** + \brief Returns dataset style table. + + This method is the same as the C function GDALDatasetGetStyleTable() and the + deprecated OGR_DS_GetStyleTable(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @return pointer to a style table which should not be modified or freed by the + caller. +*/ + +OGRStyleTable *GDALDataset::GetStyleTable() +{ + return m_poStyleTable; +} + +/************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This method operate exactly as SetStyleTable() except that it + assumes ownership of the passed table. + + This method is the same as the C function GDALDatasetSetStyleTableDirectly() and + the deprecated OGR_DS_SetStyleTableDirectly(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poStyleTable pointer to style table to set + +*/ +void GDALDataset::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = poStyleTable; +} + +/************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This method operate exactly as SetStyleTableDirectly() except + that it does not assume ownership of the passed table. + + This method is the same as the C function GDALDatasetSetStyleTable() and the + deprecated OGR_DS_SetStyleTable(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poStyleTable pointer to style table to set + +*/ + +void GDALDataset::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + if ( poStyleTable ) + m_poStyleTable = poStyleTable->Clone(); +} + +/************************************************************************/ +/* IsGenericSQLDialect() */ +/************************************************************************/ + +int GDALDataset::IsGenericSQLDialect(const char* pszDialect) +{ + return ( pszDialect != NULL && (EQUAL(pszDialect,"OGRSQL") || + EQUAL(pszDialect,"SQLITE")) ); + +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +/** + \brief Get the number of layers in this dataset. + + This method is the same as the C function GDALDatasetGetLayerCount(), + and the deprecated OGR_DS_GetLayerCount(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @return layer count. +*/ + +int GDALDataset::GetLayerCount() +{ + return 0; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +/** + \brief Fetch a layer by index. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This method is the same as the C function GDALDatasetGetLayer() and the + deprecated OGR_DS_GetLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param iLayer a layer number between 0 and GetLayerCount()-1. + + @return the layer, or NULL if iLayer is out of range or an error occurs. +*/ + +OGRLayer* GDALDataset::GetLayer(int iLayer) +{ + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + + +/** + \brief Test if capability is available. + + One of the following dataset capability names can be passed into this + method, and a TRUE or FALSE value will be returned indicating whether or not + the capability is available for this object. + +
      +
    • ODsCCreateLayer: True if this datasource can create new layers.

      +

    • ODsCDeleteLayer: True if this datasource can delete existing layers.

      +

    • ODsCCreateGeomFieldAfterCreateLayer: True if the layers of this + datasource support CreateGeomField() just after layer creation.

      +

    + + The \#define macro forms of the capability names should be used in preference + to the strings themselves to avoid mispelling. + + This method is the same as the C function GDALDatasetTestCapability() and the + deprecated OGR_DS_TestCapability(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param pszCapability the capability to test. + + @return TRUE if capability available otherwise FALSE. + +*/ + +int GDALDataset::TestCapability( const char * pszCap ) +{ + return FALSE; +} diff --git a/ogr/gdaldefaultasync.cpp b/ogr/gdaldefaultasync.cpp new file mode 100644 index 0000000..bf72e8d --- /dev/null +++ b/ogr/gdaldefaultasync.cpp @@ -0,0 +1,320 @@ +/****************************************************************************** + * $Id: gdaldataset.cpp 16796 2009-04-17 23:35:04Z normanb $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALDefaultAsyncReader and the + * GDALAsyncReader base class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdaldataset.cpp 16796 2009-04-17 23:35:04Z normanb $"); + +CPL_C_START +GDALAsyncReader * +GDALGetDefaultAsyncReader( GDALDataset* poDS, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions ); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GDALAsyncReader */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALAsyncReader() */ +/************************************************************************/ + +GDALAsyncReader::GDALAsyncReader() +{ +} + +/************************************************************************/ +/* ~GDALAsyncReader() */ +/************************************************************************/ +GDALAsyncReader::~GDALAsyncReader() +{ +} + +/************************************************************************/ +/* GetNextUpdatedRegion() */ +/************************************************************************/ + +/** + * \fn GDALAsyncStatusType GDALAsyncReader::GetNextUpdatedRegion( double dfTimeout, int* pnBufXOff, int* pnBufYOff, int* pnBufXSize, int* pnBufXSize) = 0; + * + * \brief Get async IO update + * + * Provide an opportunity for an asynchronous IO request to update the + * image buffer and return an indication of the area of the buffer that + * has been updated. + * + * The dfTimeout parameter can be used to wait for additional data to + * become available. The timeout does not limit the amount + * of time this method may spend actually processing available data. + * + * The following return status are possible. + * - GARIO_PENDING: No imagery was altered in the buffer, but there is still + * activity pending, and the application should continue to call + * GetNextUpdatedRegion() as time permits. + * - GARIO_UPDATE: Some of the imagery has been updated, but there is still + * activity pending. + * - GARIO_ERROR: Something has gone wrong. The asynchronous request should + * be ended. + * - GARIO_COMPLETE: An update has occured and there is no more pending work + * on this request. The request should be ended and the buffer used. + * + * @param dfTimeout the number of seconds to wait for additional updates. Use + * -1 to wait indefinately, or zero to not wait at all if there is no data + * available. + * @param pnBufXOff location to return the X offset of the area of the + * request buffer that has been updated. + * @param pnBufYOff location to return the Y offset of the area of the + * request buffer that has been updated. + * @param pnBufXSize location to return the X size of the area of the + * request buffer that has been updated. + * @param pnBufYSize location to return the Y size of the area of the + * request buffer that has been updated. + * + * @return GARIO_ status, details described above. + */ + +/************************************************************************/ +/* GDALARGetNextUpdatedRegion() */ +/************************************************************************/ + +GDALAsyncStatusType CPL_STDCALL +GDALARGetNextUpdatedRegion(GDALAsyncReaderH hARIO, double timeout, + int* pnxbufoff, int* pnybufoff, + int* pnxbufsize, int* pnybufsize) +{ + VALIDATE_POINTER1(hARIO, "GDALARGetNextUpdatedRegion", GARIO_ERROR); + return ((GDALAsyncReader *)hARIO)->GetNextUpdatedRegion( + timeout, pnxbufoff, pnybufoff, pnxbufsize, pnybufsize); +} + +/************************************************************************/ +/* LockBuffer() */ +/************************************************************************/ + +/** + * \brief Lock image buffer. + * + * Locks the image buffer passed into GDALDataset::BeginAsyncReader(). + * This is useful to ensure the image buffer is not being modified while + * it is being used by the application. UnlockBuffer() should be used + * to release this lock when it is no longer needed. + * + * @param dfTimeout the time in seconds to wait attempting to lock the buffer. + * -1.0 to wait indefinately and 0 to not wait at all if it can't be + * acquired immediately. Default is -1.0 (infinite wait). + * + * @return TRUE if successful, or FALSE on an error. + */ + +int GDALAsyncReader::LockBuffer( double dfTimeout ) + +{ + return TRUE; +} + + +/************************************************************************/ +/* GDALARLockBuffer() */ +/************************************************************************/ +int CPL_STDCALL GDALARLockBuffer(GDALAsyncReaderH hARIO, double dfTimeout ) +{ + VALIDATE_POINTER1(hARIO, "GDALARLockBuffer",FALSE); + return ((GDALAsyncReader *)hARIO)->LockBuffer( dfTimeout ); +} + +/************************************************************************/ +/* UnlockBuffer() */ +/************************************************************************/ + +/** + * \brief Unlock image buffer. + * + * Releases a lock on the image buffer previously taken with LockBuffer(). + */ + +void GDALAsyncReader::UnlockBuffer() + +{ +} + +/************************************************************************/ +/* GDALARUnlockBuffer() */ +/************************************************************************/ +void CPL_STDCALL GDALARUnlockBuffer(GDALAsyncReaderH hARIO) +{ + VALIDATE_POINTER0(hARIO, "GDALARUnlockBuffer"); + ((GDALAsyncReader *)hARIO)->UnlockBuffer(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALDefaultAsyncReader */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALDefaultAsyncReader : public GDALAsyncReader +{ + private: + char ** papszOptions; + + public: + GDALDefaultAsyncReader(GDALDataset* poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions); + ~GDALDefaultAsyncReader(); + + virtual GDALAsyncStatusType GetNextUpdatedRegion(double dfTimeout, + int* pnBufXOff, + int* pnBufYOff, + int* pnBufXSize, + int* pnBufYSize); +}; + +/************************************************************************/ +/* GDALGetDefaultAsyncReader() */ +/************************************************************************/ + +GDALAsyncReader * +GDALGetDefaultAsyncReader( GDALDataset* poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions) + +{ + return new GDALDefaultAsyncReader( poDS, + nXOff, nYOff, nXSize, nYSize, + pBuf, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + papszOptions ); +} + +/************************************************************************/ +/* GDALDefaultAsyncReader() */ +/************************************************************************/ + +GDALDefaultAsyncReader:: +GDALDefaultAsyncReader( GDALDataset* poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions) + +{ + this->poDS = poDS; + this->nXOff = nXOff; + this->nYOff = nYOff; + this->nXSize = nXSize; + this->nYSize = nYSize; + this->pBuf = pBuf; + this->nBufXSize = nBufXSize; + this->nBufYSize = nBufYSize; + this->eBufType = eBufType; + this->nBandCount = nBandCount; + this->panBandMap = (int *) CPLMalloc(sizeof(int)*nBandCount); + + if( panBandMap != NULL ) + memcpy( this->panBandMap, panBandMap, sizeof(int)*nBandCount ); + else + { + for( int i = 0; i < nBandCount; i++ ) + this->panBandMap[i] = i+1; + } + + this->nPixelSpace = nPixelSpace; + this->nLineSpace = nLineSpace; + this->nBandSpace = nBandSpace; + + this->papszOptions = CSLDuplicate(papszOptions); +} + +/************************************************************************/ +/* ~GDALDefaultAsyncReader() */ +/************************************************************************/ + +GDALDefaultAsyncReader::~GDALDefaultAsyncReader() + +{ + CPLFree( panBandMap ); + CSLDestroy( papszOptions ); +} + +/************************************************************************/ +/* GetNextUpdatedRegion() */ +/************************************************************************/ + +GDALAsyncStatusType +GDALDefaultAsyncReader::GetNextUpdatedRegion(double dfTimeout, + int* pnBufXOff, + int* pnBufYOff, + int* pnBufXSize, + int* pnBufYSize ) + +{ + CPLErr eErr; + + eErr = poDS->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize, + pBuf, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + + *pnBufXOff = 0; + *pnBufYOff = 0; + *pnBufXSize = nBufXSize; + *pnBufYSize = nBufYSize; + + if( eErr == CE_None ) + return GARIO_COMPLETE; + else + return GARIO_ERROR; +} + diff --git a/ogr/gdaldefaultoverviews.cpp b/ogr/gdaldefaultoverviews.cpp new file mode 100644 index 0000000..0d87ff3 --- /dev/null +++ b/ogr/gdaldefaultoverviews.cpp @@ -0,0 +1,1059 @@ +/****************************************************************************** + * $Id: gdaldefaultoverviews.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GDAL Core + * Purpose: Helper code to implement overview and mask support for many + * drivers with no inherent format support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, 2007, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdaldefaultoverviews.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* GDALDefaultOverviews() */ +/************************************************************************/ + +GDALDefaultOverviews::GDALDefaultOverviews() + +{ + poDS = NULL; + poODS = NULL; + bOvrIsAux = FALSE; + + bCheckedForMask = FALSE; + bCheckedForOverviews = FALSE; + + poMaskDS = NULL; + + bOwnMaskDS = FALSE; + poBaseDS = NULL; + + papszInitSiblingFiles = NULL; + pszInitName = NULL; + bInitNameIsOVR = FALSE; +} + +/************************************************************************/ +/* ~GDALDefaultOverviews() */ +/************************************************************************/ + +GDALDefaultOverviews::~GDALDefaultOverviews() + +{ + CPLFree( pszInitName ); + CSLDestroy( papszInitSiblingFiles ); + + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int GDALDefaultOverviews::CloseDependentDatasets() +{ + int bHasDroppedRef = FALSE; + if( poODS != NULL ) + { + bHasDroppedRef = TRUE; + poODS->FlushCache(); + GDALClose( poODS ); + poODS = NULL; + } + + if( poMaskDS != NULL ) + { + if( bOwnMaskDS ) + { + bHasDroppedRef = TRUE; + poMaskDS->FlushCache(); + GDALClose( poMaskDS ); + } + poMaskDS = NULL; + } + + return bHasDroppedRef; +} + +/************************************************************************/ +/* IsInitialized() */ +/* */ +/* Returns TRUE if we are initialized. */ +/************************************************************************/ + +int GDALDefaultOverviews::IsInitialized() + +{ + OverviewScan(); + return poDS != NULL; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void GDALDefaultOverviews::Initialize( GDALDataset *poDSIn, + const char * pszBasename, + char **papszSiblingFiles, + int bNameIsOVR ) + +{ + poDS = poDSIn; + +/* -------------------------------------------------------------------- */ +/* If we were already initialized, destroy the old overview */ +/* file handle. */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL ) + { + GDALClose( poODS ); + poODS = NULL; + + CPLDebug( "GDAL", "GDALDefaultOverviews::Initialize() called twice - this is odd and perhaps dangerous!" ); + } + +/* -------------------------------------------------------------------- */ +/* Store the initialization information for later use in */ +/* OverviewScan() */ +/* -------------------------------------------------------------------- */ + bCheckedForOverviews = FALSE; + + CPLFree( pszInitName ); + pszInitName = NULL; + if( pszBasename != NULL ) + pszInitName = CPLStrdup(pszBasename); + bInitNameIsOVR = bNameIsOVR; + + CSLDestroy( papszInitSiblingFiles ); + papszInitSiblingFiles = NULL; + if( papszSiblingFiles != NULL ) + papszInitSiblingFiles = CSLDuplicate(papszSiblingFiles); +} + +/************************************************************************/ +/* OverviewScan() */ +/* */ +/* This is called to scan for overview files when a first */ +/* request is made with regard to overviews. It uses the */ +/* pszInitName, bInitNameIsOVR and papszInitSiblingFiles */ +/* information that was stored at Initialization() time. */ +/************************************************************************/ + +void GDALDefaultOverviews::OverviewScan() + +{ + if( bCheckedForOverviews || poDS == NULL ) + return; + + bCheckedForOverviews = true; + + CPLDebug( "GDAL", "GDALDefaultOverviews::OverviewScan()" ); + +/* -------------------------------------------------------------------- */ +/* Open overview dataset if it exists. */ +/* -------------------------------------------------------------------- */ + int bExists; + + if( pszInitName == NULL ) + pszInitName = CPLStrdup(poDS->GetDescription()); + + if( !EQUAL(pszInitName,":::VIRTUAL:::") ) + { + if( bInitNameIsOVR ) + osOvrFilename = pszInitName; + else + osOvrFilename.Printf( "%s.ovr", pszInitName ); + + bExists = CPLCheckForFile( (char *) osOvrFilename.c_str(), + papszInitSiblingFiles ); + +#if !defined(WIN32) + if( !bInitNameIsOVR && !bExists && !papszInitSiblingFiles ) + { + osOvrFilename.Printf( "%s.OVR", pszInitName ); + bExists = CPLCheckForFile( (char *) osOvrFilename.c_str(), + papszInitSiblingFiles ); + if( !bExists ) + osOvrFilename.Printf( "%s.ovr", pszInitName ); + } +#endif + + if( bExists ) + { + poODS = (GDALDataset*) GDALOpenEx( osOvrFilename, + GDAL_OF_RASTER | + ((poDS->GetAccess() == GA_Update) ? GDAL_OF_UPDATE : 0), + NULL, NULL, papszInitSiblingFiles ); + } + } + +/* -------------------------------------------------------------------- */ +/* We didn't find that, so try and find a corresponding aux */ +/* file. Check that we are the dependent file of the aux */ +/* file. */ +/* */ +/* We only use the .aux file for overviews if they already have */ +/* overviews existing, or if USE_RRD is set true. */ +/* -------------------------------------------------------------------- */ + if( !poODS && !EQUAL(pszInitName,":::VIRTUAL:::") ) + { + int bTryFindAssociatedAuxFile = TRUE; + if( papszInitSiblingFiles ) + { + CPLString osAuxFilename = CPLResetExtension( pszInitName, "aux"); + int iSibling = CSLFindString( papszInitSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + { + osAuxFilename = pszInitName; + osAuxFilename += ".aux"; + iSibling = CSLFindString( papszInitSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + bTryFindAssociatedAuxFile = FALSE; + } + } + + if (bTryFindAssociatedAuxFile) + { + poODS = GDALFindAssociatedAuxFile( pszInitName, poDS->GetAccess(), + poDS ); + } + + if( poODS ) + { + int bUseRRD = CSLTestBoolean(CPLGetConfigOption("USE_RRD","NO")); + + bOvrIsAux = TRUE; + if( GetOverviewCount(1) == 0 && !bUseRRD ) + { + bOvrIsAux = FALSE; + GDALClose( poODS ); + poODS = NULL; + } + else + { + osOvrFilename = poODS->GetDescription(); + } + } + } + +/* -------------------------------------------------------------------- */ +/* If we still don't have an overview, check to see if we have */ +/* overview metadata referencing a remote (ie. proxy) or local */ +/* subdataset overview dataset. */ +/* -------------------------------------------------------------------- */ + if( poODS == NULL ) + { + const char *pszProxyOvrFilename = + poDS->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" ); + + if( pszProxyOvrFilename != NULL ) + { + if( EQUALN(pszProxyOvrFilename,":::BASE:::",10) ) + { + CPLString osPath = CPLGetPath(poDS->GetDescription()); + + osOvrFilename = + CPLFormFilename( osPath, pszProxyOvrFilename+10, NULL ); + } + else + osOvrFilename = pszProxyOvrFilename; + + CPLPushErrorHandler(CPLQuietErrorHandler); + poODS = (GDALDataset *) GDALOpen(osOvrFilename,poDS->GetAccess()); + CPLPopErrorHandler(); + } + } + +/* -------------------------------------------------------------------- */ +/* If we have an overview dataset, then mark all the overviews */ +/* with the base dataset Used later for finding overviews */ +/* masks. Uggg. */ +/* -------------------------------------------------------------------- */ + if( poODS ) + { + int nOverviewCount = GetOverviewCount(1); + int iOver; + + for( iOver = 0; iOver < nOverviewCount; iOver++ ) + { + GDALRasterBand *poBand = GetOverview( 1, iOver ); + GDALDataset *poOverDS = NULL; + + if( poBand != NULL ) + poOverDS = poBand->GetDataset(); + + if( poOverDS != NULL ) + { + poOverDS->oOvManager.poBaseDS = poDS; + poOverDS->oOvManager.poDS = poOverDS; + } + } + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int GDALDefaultOverviews::GetOverviewCount( int nBand ) + +{ + GDALRasterBand * poBand; + + if( poODS == NULL || nBand < 1 || nBand > poODS->GetRasterCount() ) + return 0; + + poBand = poODS->GetRasterBand( nBand ); + if( poBand == NULL ) + return 0; + else + { + if( bOvrIsAux ) + return poBand->GetOverviewCount(); + else + return poBand->GetOverviewCount() + 1; + } +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand * +GDALDefaultOverviews::GetOverview( int nBand, int iOverview ) + +{ + GDALRasterBand * poBand; + + if( poODS == NULL || nBand < 1 || nBand > poODS->GetRasterCount() ) + return NULL; + + poBand = poODS->GetRasterBand( nBand ); + if( poBand == NULL ) + return NULL; + + if( bOvrIsAux ) + return poBand->GetOverview( iOverview ); + + else // TIFF case, base is overview 0. + { + if( iOverview == 0 ) + return poBand; + else if( iOverview-1 >= poBand->GetOverviewCount() ) + return NULL; + else + return poBand->GetOverview( iOverview-1 ); + } +} + +/************************************************************************/ +/* GDALOvLevelAdjust() */ +/* */ +/* Some overview levels cannot be achieved closely enough to be */ +/* recognised as the desired overview level. This function */ +/* will adjust an overview level to one that is achievable on */ +/* the given raster size. */ +/* */ +/* For instance a 1200x1200 image on which a 256 level overview */ +/* is request will end up generating a 5x5 overview. However, */ +/* this will appear to the system be a level 240 overview. */ +/* This function will adjust 256 to 240 based on knowledge of */ +/* the image size. */ +/************************************************************************/ + +int GDALOvLevelAdjust( int nOvLevel, int nXSize ) + +{ + int nOXSize = (nXSize + nOvLevel - 1) / nOvLevel; + + return (int) (0.5 + nXSize / (double) nOXSize); +} + +/************************************************************************/ +/* CleanOverviews() */ +/* */ +/* Remove all existing overviews. */ +/************************************************************************/ + +CPLErr GDALDefaultOverviews::CleanOverviews() + +{ + // Anything to do? + if( poODS == NULL ) + return CE_None; + + // Delete the overview file(s). + GDALDriver *poOvrDriver; + + poOvrDriver = poODS->GetDriver(); + GDALClose( poODS ); + poODS = NULL; + + CPLErr eErr; + if( poOvrDriver != NULL ) + eErr = poOvrDriver->Delete( osOvrFilename ); + else + eErr = CE_None; + + // Reset the saved overview filename. + if( !EQUAL(poDS->GetDescription(),":::VIRTUAL:::") ) + { + int bUseRRD = CSLTestBoolean(CPLGetConfigOption("USE_RRD","NO")); + + if( bUseRRD ) + osOvrFilename = CPLResetExtension( poDS->GetDescription(), "aux" ); + else + osOvrFilename.Printf( "%s.ovr", poDS->GetDescription() ); + } + else + osOvrFilename = ""; + + return eErr; +} + +/************************************************************************/ +/* BuildOverviewsSubDataset() */ +/************************************************************************/ + +CPLErr +GDALDefaultOverviews::BuildOverviewsSubDataset( + const char * pszPhysicalFile, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData) + +{ + if( osOvrFilename.length() == 0 && nOverviews > 0 ) + { + int iSequence = 0; + VSIStatBufL sStatBuf; + + for( iSequence = 0; iSequence < 100; iSequence++ ) + { + osOvrFilename.Printf( "%s_%d.ovr", pszPhysicalFile, iSequence ); + if( VSIStatExL( osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + CPLString osAdjustedOvrFilename; + + if( poDS->GetMOFlags() & GMO_PAM_CLASS ) + { + osAdjustedOvrFilename.Printf( ":::BASE:::%s_%d.ovr", + CPLGetFilename(pszPhysicalFile), + iSequence ); + } + else + osAdjustedOvrFilename = osOvrFilename; + + poDS->SetMetadataItem( "OVERVIEW_FILE", + osAdjustedOvrFilename, + "OVERVIEWS" ); + break; + } + } + + if( iSequence == 100 ) + osOvrFilename = ""; + } + + return BuildOverviews( NULL, pszResampling, nOverviews, panOverviewList, + nBands, panBandList, pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +CPLErr +GDALDefaultOverviews::BuildOverviews( + const char * pszBasename, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData) + +{ + GDALRasterBand **pahBands; + CPLErr eErr; + int i; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( nOverviews == 0 ) + return CleanOverviews(); + +/* -------------------------------------------------------------------- */ +/* If we don't already have an overview file, we need to decide */ +/* what format to use. */ +/* -------------------------------------------------------------------- */ + if( poODS == NULL ) + { + bOvrIsAux = CSLTestBoolean(CPLGetConfigOption( "USE_RRD", "NO" )); + if( bOvrIsAux ) + { + VSIStatBufL sStatBuf; + + osOvrFilename = CPLResetExtension(poDS->GetDescription(),"aux"); + + if( VSIStatExL( osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + osOvrFilename.Printf( "%s.aux", poDS->GetDescription() ); + } + } +/* -------------------------------------------------------------------- */ +/* If we already have the overviews open, but they are */ +/* read-only, then try and reopen them read-write. */ +/* -------------------------------------------------------------------- */ + else if( poODS->GetAccess() == GA_ReadOnly ) + { + GDALClose( poODS ); + poODS = (GDALDataset *) GDALOpen( osOvrFilename, GA_Update ); + if( poODS == NULL ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Our TIFF overview support currently only works safely if all */ +/* bands are handled at the same time. */ +/* -------------------------------------------------------------------- */ + if( !bOvrIsAux && nBands != poDS->GetRasterCount() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Generation of overviews in external TIFF currently only" + " supported when operating on all bands.\n" + "Operation failed.\n" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If a basename is provided, use it to override the internal */ +/* overview filename. */ +/* -------------------------------------------------------------------- */ + if( pszBasename == NULL && osOvrFilename.length() == 0 ) + pszBasename = poDS->GetDescription(); + + if( pszBasename != NULL ) + { + if( bOvrIsAux ) + osOvrFilename.Printf( "%s.aux", pszBasename ); + else + osOvrFilename.Printf( "%s.ovr", pszBasename ); + } + +/* -------------------------------------------------------------------- */ +/* Establish which of the overview levels we already have, and */ +/* which are new. We assume that band 1 of the file is */ +/* representative. */ +/* -------------------------------------------------------------------- */ + int nNewOverviews, *panNewOverviewList = NULL; + GDALRasterBand *poBand = poDS->GetRasterBand( 1 ); + + nNewOverviews = 0; + panNewOverviewList = (int *) CPLCalloc(sizeof(int),nOverviews); + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + if (poOverview == NULL) + continue; + + nOvFactor = (int) + (0.5 + poBand->GetXSize() / (double) poOverview->GetXSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust( panOverviewList[i], + poBand->GetXSize() ) ) + panOverviewList[i] *= -1; + } + + if( panOverviewList[i] > 0 ) + panNewOverviewList[nNewOverviews++] = panOverviewList[i]; + } + +/* -------------------------------------------------------------------- */ +/* Build band list. */ +/* -------------------------------------------------------------------- */ + pahBands = (GDALRasterBand **) CPLCalloc(sizeof(GDALRasterBand *),nBands); + for( i = 0; i < nBands; i++ ) + pahBands[i] = poDS->GetRasterBand( panBandList[i] ); + +/* -------------------------------------------------------------------- */ +/* Build new overviews - Imagine. Keep existing file open if */ +/* we have it. But mark all overviews as in need of */ +/* regeneration, since HFAAuxBuildOverviews() doesn't actually */ +/* produce the imagery. */ +/* -------------------------------------------------------------------- */ + +#ifndef WIN32CE + + if( bOvrIsAux ) + { + if( nNewOverviews == 0 ) + { + /* if we call HFAAuxBuildOverviews() with nNewOverviews == 0 */ + /* because that there's no new, this will wipe existing */ + /* overviews (#4831) */ + eErr = CE_None; + } + else + eErr = HFAAuxBuildOverviews( osOvrFilename, poDS, &poODS, + nBands, panBandList, + nNewOverviews, panNewOverviewList, + pszResampling, + pfnProgress, pProgressData ); + + int j; + + for( j = 0; j < nOverviews; j++ ) + { + if( panOverviewList[j] > 0 ) + panOverviewList[j] *= -1; + } + } + +/* -------------------------------------------------------------------- */ +/* Build new overviews - TIFF. Close TIFF files while we */ +/* operate on it. */ +/* -------------------------------------------------------------------- */ + else +#endif /* WIN32CE */ + { + if( poODS != NULL ) + { + delete poODS; + poODS = NULL; + } + + eErr = GTIFFBuildOverviews( osOvrFilename, nBands, pahBands, + nNewOverviews, panNewOverviewList, + pszResampling, pfnProgress, pProgressData ); + + // Probe for proxy overview filename. + if( eErr == CE_Failure ) + { + const char *pszProxyOvrFilename = + poDS->GetMetadataItem("FILENAME","ProxyOverviewRequest"); + + if( pszProxyOvrFilename != NULL ) + { + osOvrFilename = pszProxyOvrFilename; + eErr = GTIFFBuildOverviews( osOvrFilename, nBands, pahBands, + nNewOverviews, panNewOverviewList, + pszResampling, + pfnProgress, pProgressData ); + } + } + + if( eErr == CE_None ) + { + poODS = (GDALDataset *) GDALOpen( osOvrFilename, GA_Update ); + if( poODS == NULL ) + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Refresh old overviews that were listed. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand **papoOverviewBands; + + papoOverviewBands = (GDALRasterBand **) + CPLCalloc(sizeof(void*),nOverviews); + + for( int iBand = 0; iBand < nBands && eErr == CE_None; iBand++ ) + { + poBand = poDS->GetRasterBand( panBandList[iBand] ); + + nNewOverviews = 0; + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + if (poOverview == NULL) + continue; + + int bHasNoData; + double noDataValue = poBand->GetNoDataValue(&bHasNoData); + + if (bHasNoData) + poOverview->SetNoDataValue(noDataValue); + + nOvFactor = (int) + (0.5 + poBand->GetXSize() / (double) poOverview->GetXSize()); + + if( nOvFactor == - panOverviewList[i] + || (panOverviewList[i] < 0 && + nOvFactor == GDALOvLevelAdjust( -panOverviewList[i], + poBand->GetXSize() )) ) + { + papoOverviewBands[nNewOverviews++] = poOverview; + break; + } + } + } + + if( nNewOverviews > 0 ) + { + eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBand, + nNewOverviews, + (GDALRasterBandH*)papoOverviewBands, + pszResampling, + pfnProgress, pProgressData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( papoOverviewBands ); + CPLFree( panNewOverviewList ); + CPLFree( pahBands ); + +/* -------------------------------------------------------------------- */ +/* If we have a mask file, we need to build it's overviews */ +/* too. */ +/* -------------------------------------------------------------------- */ + if( HaveMaskFile() && poMaskDS ) + { + /* Some config option are not compatible with mask overviews */ + /* so unset them, and define more sensible values */ + int bJPEG = EQUAL(CPLGetConfigOption("COMPRESS_OVERVIEW", ""), "JPEG"); + int bPHOTOMETRIC_YCBCR = EQUAL(CPLGetConfigOption("PHOTOMETRIC_OVERVIEW", ""), "YCBCR"); + if( bJPEG ) + CPLSetThreadLocalConfigOption("COMPRESS_OVERVIEW", "DEFLATE"); + if( bPHOTOMETRIC_YCBCR ) + CPLSetThreadLocalConfigOption("PHOTOMETRIC_OVERVIEW", ""); + + poMaskDS->BuildOverviews( pszResampling, nOverviews, panOverviewList, + 0, NULL, pfnProgress, pProgressData ); + + /* Restore config option */ + if( bJPEG ) + CPLSetThreadLocalConfigOption("COMPRESS_OVERVIEW", "JPEG"); + if( bPHOTOMETRIC_YCBCR ) + CPLSetThreadLocalConfigOption("PHOTOMETRIC_OVERVIEW", "YCBCR"); + + if( bOwnMaskDS ) + GDALClose( poMaskDS ); + + // force next request to reread mask file. + poMaskDS = NULL; + bOwnMaskDS = FALSE; + bCheckedForMask = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we have an overview dataset, then mark all the overviews */ +/* with the base dataset Used later for finding overviews */ +/* masks. Uggg. */ +/* -------------------------------------------------------------------- */ + if( poODS ) + { + int nOverviewCount = GetOverviewCount(1); + int iOver; + + for( iOver = 0; iOver < nOverviewCount; iOver++ ) + { + GDALRasterBand *poBand = GetOverview( 1, iOver ); + GDALDataset *poOverDS = NULL; + + if( poBand != NULL ) + poOverDS = poBand->GetDataset(); + + if (poOverDS != NULL) + { + poOverDS->oOvManager.poBaseDS = poDS; + poOverDS->oOvManager.poDS = poOverDS; + } + } + } + + return eErr; +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GDALDefaultOverviews::CreateMaskBand( int nFlags, int nBand ) + +{ + if( nBand < 1 ) + nFlags |= GMF_PER_DATASET; + +/* -------------------------------------------------------------------- */ +/* ensure existing file gets opened if there is one. */ +/* -------------------------------------------------------------------- */ + HaveMaskFile(); + +/* -------------------------------------------------------------------- */ +/* Try creating the mask file. */ +/* -------------------------------------------------------------------- */ + if( poMaskDS == NULL ) + { + CPLString osMskFilename; + GDALDriver *poDr = (GDALDriver *) GDALGetDriverByName( "GTiff" ); + char **papszOpt = NULL; + int nBX, nBY; + int nBands; + + if( poDr == NULL ) + return CE_Failure; + + GDALRasterBand *poTBand = poDS->GetRasterBand(1); + if( poTBand == NULL ) + return CE_Failure; + + if( nFlags & GMF_PER_DATASET ) + nBands = 1; + else + nBands = poDS->GetRasterCount(); + + + papszOpt = CSLSetNameValue( papszOpt, "COMPRESS", "DEFLATE" ); + papszOpt = CSLSetNameValue( papszOpt, "INTERLEAVE", "BAND" ); + + poTBand->GetBlockSize( &nBX, &nBY ); + + // try to create matching tile size if legal in TIFF. + if( (nBX % 16) == 0 && (nBY % 16) == 0 ) + { + papszOpt = CSLSetNameValue( papszOpt, "TILED", "YES" ); + papszOpt = CSLSetNameValue( papszOpt, "BLOCKXSIZE", + CPLString().Printf("%d",nBX) ); + papszOpt = CSLSetNameValue( papszOpt, "BLOCKYSIZE", + CPLString().Printf("%d",nBY) ); + } + + osMskFilename.Printf( "%s.msk", poDS->GetDescription() ); + poMaskDS = poDr->Create( osMskFilename, + poDS->GetRasterXSize(), + poDS->GetRasterYSize(), + nBands, GDT_Byte, papszOpt ); + CSLDestroy( papszOpt ); + + if( poMaskDS == NULL ) // presumably error already issued. + return CE_Failure; + + bOwnMaskDS = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Save the mask flags for this band. */ +/* -------------------------------------------------------------------- */ + if( nBand > poMaskDS->GetRasterCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create a mask band for band %d of %s,\n" + "but the .msk file has a PER_DATASET mask.", + nBand, poDS->GetDescription() ); + return CE_Failure; + } + + int iBand; + + for( iBand = 0; iBand < poDS->GetRasterCount(); iBand++ ) + { + // we write only the info for this band, unless we are + // using PER_DATASET in which case we write for all. + if( nBand != iBand + 1 && !(nFlags | GMF_PER_DATASET) ) + continue; + + poMaskDS->SetMetadataItem( + CPLString().Printf("INTERNAL_MASK_FLAGS_%d", iBand+1 ), + CPLString().Printf("%d", nFlags ) ); + } + + return CE_None; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GDALDefaultOverviews::GetMaskBand( int nBand ) + +{ + int nFlags = GetMaskFlags( nBand ); + + if( nFlags == 0x8000 ) // secret code meaning we don't handle this band. + return NULL; + + if( nFlags & GMF_PER_DATASET ) + return poMaskDS->GetRasterBand(1); + + if( nBand > 0 ) + return poMaskDS->GetRasterBand( nBand ); + else + return NULL; +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GDALDefaultOverviews::GetMaskFlags( int nBand ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch this band's metadata entry. They are of the form: */ +/* INTERNAL_MASK_FLAGS_n: flags */ +/* -------------------------------------------------------------------- */ + if( !HaveMaskFile() ) + return 0; + + const char *pszValue = + poMaskDS->GetMetadataItem( + CPLString().Printf( "INTERNAL_MASK_FLAGS_%d", MAX(nBand,1)) ); + + if( pszValue == NULL ) + return 0x8000; + else + return atoi(pszValue); +} + +/************************************************************************/ +/* HaveMaskFile() */ +/* */ +/* Check for a mask file if we haven't already done so. */ +/* Returns TRUE if we have one, otherwise FALSE. */ +/************************************************************************/ + +int GDALDefaultOverviews::HaveMaskFile( char ** papszSiblingFiles, + const char *pszBasename ) + +{ +/* -------------------------------------------------------------------- */ +/* Have we already checked for masks? */ +/* -------------------------------------------------------------------- */ + if( bCheckedForMask ) + return poMaskDS != NULL; + + if( papszSiblingFiles == NULL ) + papszSiblingFiles = papszInitSiblingFiles; + +/* -------------------------------------------------------------------- */ +/* Are we an overview? If so we need to find the corresponding */ +/* overview in the base files mask file (if there is one). */ +/* -------------------------------------------------------------------- */ + if( poBaseDS != NULL && poBaseDS->oOvManager.HaveMaskFile() ) + { + int iOver, nOverviewCount = 0; + GDALRasterBand *poBaseBand = poBaseDS->GetRasterBand(1); + GDALRasterBand *poBaseMask = NULL; + + if( poBaseBand != NULL ) + poBaseMask = poBaseBand->GetMaskBand(); + if( poBaseMask ) + nOverviewCount = poBaseMask->GetOverviewCount(); + + for( iOver = 0; iOver < nOverviewCount; iOver++ ) + { + GDALRasterBand *poOverBand = poBaseMask->GetOverview( iOver ); + if (poOverBand == NULL) + continue; + + if( poOverBand->GetXSize() == poDS->GetRasterXSize() + && poOverBand->GetYSize() == poDS->GetRasterYSize() ) + { + poMaskDS = poOverBand->GetDataset(); + break; + } + } + + bCheckedForMask = TRUE; + bOwnMaskDS = FALSE; + + CPLAssert( poMaskDS != poDS ); + + return poMaskDS != NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are we even initialized? If not, we apparently don't want */ +/* to support overviews and masks. */ +/* -------------------------------------------------------------------- */ + if( poDS == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Check for .msk file. */ +/* -------------------------------------------------------------------- */ + CPLString osMskFilename; + bCheckedForMask = TRUE; + + if( pszBasename == NULL ) + pszBasename = poDS->GetDescription(); + + // Don't bother checking for masks of masks. + if( EQUAL(CPLGetExtension(pszBasename),"msk") ) + return FALSE; + + osMskFilename.Printf( "%s.msk", pszBasename ); + + int bExists = CPLCheckForFile( (char *) osMskFilename.c_str(), + papszSiblingFiles ); + +#if !defined(WIN32) + if( !bExists && !papszSiblingFiles ) + { + osMskFilename.Printf( "%s.MSK", pszBasename ); + bExists = CPLCheckForFile( (char *) osMskFilename.c_str(), + papszSiblingFiles ); + } +#endif + + if( !bExists ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + poMaskDS = (GDALDataset *) GDALOpenEx( osMskFilename, + GDAL_OF_RASTER | + ((poDS->GetAccess() == GA_Update) ? GDAL_OF_UPDATE : 0), + NULL, NULL, papszInitSiblingFiles ); + CPLAssert( poMaskDS != poDS ); + + if( poMaskDS == NULL ) + return FALSE; + + bOwnMaskDS = TRUE; + + return TRUE; +} diff --git a/ogr/gdaldriver.cpp b/ogr/gdaldriver.cpp new file mode 100644 index 0000000..a05e5c2 --- /dev/null +++ b/ogr/gdaldriver.cpp @@ -0,0 +1,1759 @@ +/****************************************************************************** + * $Id: gdaldriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALDriver class (and C wrappers) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2000, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: gdaldriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +const char* GDALClientDatasetGetFilename(const char* pszFilename); +CPL_C_END + +/************************************************************************/ +/* GDALDriver() */ +/************************************************************************/ + +GDALDriver::GDALDriver() + +{ + pfnOpen = NULL; + pfnCreate = NULL; + pfnDelete = NULL; + pfnCreateCopy = NULL; + pfnUnloadDriver = NULL; + pDriverData = NULL; + pfnIdentify = NULL; + pfnRename = NULL; + pfnCopyFiles = NULL; + pfnOpenWithDriverArg = NULL; + pfnCreateVectorOnly = NULL; + pfnDeleteDataSource = NULL; +} + +/************************************************************************/ +/* ~GDALDriver() */ +/************************************************************************/ + +GDALDriver::~GDALDriver() + +{ + if( pfnUnloadDriver != NULL ) + pfnUnloadDriver( this ); +} + +/************************************************************************/ +/* GDALDestroyDriver() */ +/************************************************************************/ + +/** + * \brief Destroy a GDALDriver. + * + * This is roughly equivelent to deleting the driver, but is guaranteed + * to take place in the GDAL heap. It is important this that function + * not be called on a driver that is registered with the GDALDriverManager. + * + * @param hDriver the driver to destroy. + */ + +void CPL_STDCALL GDALDestroyDriver( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER0( hDriver, "GDALDestroyDriver" ); + + delete ((GDALDriver *) hDriver); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +/** + * \brief Create a new dataset with this driver. + * + * What argument values are legal for particular drivers is driver specific, + * and there is no way to query in advance to establish legal values. + * + * That function will try to validate the creation option list passed to the driver + * with the GDALValidateCreationOptions() method. This check can be disabled + * by defining the configuration option GDAL_VALIDATE_CREATION_OPTIONS=NO. + * + * After you have finished working with the returned dataset, it is required + * to close it with GDALClose(). This does not only close the file handle, but + * also ensures that all the data and metadata has been written to the dataset + * (GDALFlushCache() is not sufficient for that purpose). + * + * In some situations, the new dataset can be created in another process through the + * \ref gdal_api_proxy mechanism. + * + * In GDAL 2, the arguments nXSize, nYSize and nBands can be passed to 0 when + * creating a vector-only dataset for a compatible driver. + * + * Equivelent of the C function GDALCreate(). + * + * @param pszFilename the name of the dataset to create. UTF-8 encoded. + * @param nXSize width of created raster in pixels. + * @param nYSize height of created raster in pixels. + * @param nBands number of bands. + * @param eType type of raster. + * @param papszOptions list of driver specific control parameters. + * + * @return NULL on failure, or a new GDALDataset. + */ + +GDALDataset * GDALDriver::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ) + +{ + CPLLocaleC oLocaleForcer; + +/* -------------------------------------------------------------------- */ +/* Does this format support creation. */ +/* -------------------------------------------------------------------- */ + if( pfnCreate == NULL && pfnCreateVectorOnly == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GDALDriver::Create() ... no create method implemented" + " for this format.\n" ); + + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Do some rudimentary argument checking. */ +/* -------------------------------------------------------------------- */ + if (nBands < 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create dataset with %d bands is illegal," + "Must be >= 0.", + nBands ); + return NULL; + } + + if( GetMetadataItem(GDAL_DCAP_RASTER) != NULL && + GetMetadataItem(GDAL_DCAP_VECTOR) == NULL && + (nXSize < 1 || nYSize < 1) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create %dx%d dataset is illegal," + "sizes must be larger than zero.", + nXSize, nYSize ); + return NULL; + } + + const char* pszClientFilename = GDALClientDatasetGetFilename(pszFilename); + if( pszClientFilename != NULL && !EQUAL(GetDescription(), "MEM") && + !EQUAL(GetDescription(), "VRT") ) + { + GDALDriver* poAPIPROXYDriver = GDALGetAPIPROXYDriver(); + if( poAPIPROXYDriver != this ) + { + if( poAPIPROXYDriver == NULL || poAPIPROXYDriver->pfnCreate == NULL ) + return NULL; + char** papszOptionsDup = CSLDuplicate(papszOptions); + papszOptionsDup = CSLAddNameValue(papszOptionsDup, "SERVER_DRIVER", + GetDescription()); + GDALDataset* poDstDS = poAPIPROXYDriver->pfnCreate( + pszClientFilename, nXSize, nYSize, nBands, + eType, papszOptionsDup); + + CSLDestroy(papszOptionsDup); + + if( poDstDS != NULL ) + { + if( poDstDS->GetDescription() == NULL + || strlen(poDstDS->GetDescription()) == 0 ) + poDstDS->SetDescription( pszFilename ); + + if( poDstDS->poDriver == NULL ) + poDstDS->poDriver = poAPIPROXYDriver; + } + + if( poDstDS != NULL || CPLGetLastErrorNo() != CPLE_NotSupported ) + return poDstDS; + } + } + +/* -------------------------------------------------------------------- */ +/* Make sure we cleanup if there is an existing dataset of this */ +/* name. But even if that seems to fail we will continue since */ +/* it might just be a corrupt file or something. */ +/* -------------------------------------------------------------------- */ + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) ) + QuietDelete( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Validate creation options. */ +/* -------------------------------------------------------------------- */ + if (CSLTestBoolean(CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES"))) + GDALValidateCreationOptions( this, papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Proceed with creation. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS; + + CPLDebug( "GDAL", "GDALDriver::Create(%s,%s,%d,%d,%d,%s,%p)", + GetDescription(), pszFilename, nXSize, nYSize, nBands, + GDALGetDataTypeName( eType ), + papszOptions ); + + if( pfnCreate != NULL ) + { + poDS = pfnCreate( pszFilename, nXSize, nYSize, nBands, eType, + papszOptions ); + } + else + { + if( nBands > 0 ) + poDS = NULL; + else + poDS = pfnCreateVectorOnly( this, pszFilename, papszOptions ); + } + + if( poDS != NULL ) + { + if( poDS->GetDescription() == NULL + || strlen(poDS->GetDescription()) == 0 ) + poDS->SetDescription( pszFilename ); + + if( poDS->poDriver == NULL ) + poDS->poDriver = this; + } + + return poDS; +} + +/************************************************************************/ +/* GDALCreate() */ +/************************************************************************/ + +/** + * \brief Create a new dataset with this driver. + * + * @see GDALDriver::Create() + */ + +GDALDatasetH CPL_DLL CPL_STDCALL +GDALCreate( GDALDriverH hDriver, const char * pszFilename, + int nXSize, int nYSize, int nBands, GDALDataType eBandType, + char ** papszOptions ) + +{ + VALIDATE_POINTER1( hDriver, "GDALCreate", NULL ); + + return( ((GDALDriver *) hDriver)->Create( pszFilename, + nXSize, nYSize, nBands, + eBandType, papszOptions ) ); +} + +/************************************************************************/ +/* DefaultCopyMasks() */ +/************************************************************************/ + +CPLErr GDALDriver::DefaultCopyMasks( GDALDataset *poSrcDS, + GDALDataset *poDstDS, + int bStrict ) + +{ + CPLErr eErr = CE_None; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + return CE_None; + + const char* papszOptions[2] = { "COMPRESSED=YES", NULL }; + +/* -------------------------------------------------------------------- */ +/* Try to copy mask if it seems appropriate. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; + eErr == CE_None && iBand < nBands; + iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + + int nMaskFlags = poSrcBand->GetMaskFlags(); + if( eErr == CE_None + && !(nMaskFlags & (GMF_ALL_VALID|GMF_PER_DATASET|GMF_ALPHA|GMF_NODATA) ) ) + { + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand+1 ); + if (poDstBand != NULL) + { + eErr = poDstBand->CreateMaskBand( nMaskFlags ); + if( eErr == CE_None ) + { + eErr = GDALRasterBandCopyWholeRaster( + poSrcBand->GetMaskBand(), + poDstBand->GetMaskBand(), + (char**)papszOptions, + GDALDummyProgress, NULL); + } + else if( !bStrict ) + eErr = CE_None; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to copy a per-dataset mask if we have one. */ +/* -------------------------------------------------------------------- */ + int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags(); + if( eErr == CE_None + && !(nMaskFlags & (GMF_ALL_VALID|GMF_ALPHA|GMF_NODATA) ) + && (nMaskFlags & GMF_PER_DATASET) ) + { + eErr = poDstDS->CreateMaskBand( nMaskFlags ); + if( eErr == CE_None ) + { + eErr = GDALRasterBandCopyWholeRaster( + poSrcDS->GetRasterBand(1)->GetMaskBand(), + poDstDS->GetRasterBand(1)->GetMaskBand(), + (char**)papszOptions, + GDALDummyProgress, NULL); + } + else if( !bStrict ) + eErr = CE_None; + } + + return eErr; +} + +/************************************************************************/ +/* DefaultCreateCopy() */ +/************************************************************************/ + +GDALDataset *GDALDriver::DefaultCreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + CPLErrorReset(); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Validate that we can create the output as requested. */ +/* -------------------------------------------------------------------- */ + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nBands = poSrcDS->GetRasterCount(); + + CPLDebug( "GDAL", "Using default GDALDriver::CreateCopy implementation." ); + + int nLayerCount = poSrcDS->GetLayerCount(); + if (nBands == 0 && nLayerCount == 0 && GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GDALDriver::DefaultCreateCopy does not support zero band" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Propogate some specific structural metadata as options if it */ +/* appears to be supported by the target driver and the caller */ +/* didn't provide values. */ +/* -------------------------------------------------------------------- */ + char **papszCreateOptions = CSLDuplicate( papszOptions ); + int iOptItem; + static const char *apszOptItems[] = { + "NBITS", "IMAGE_STRUCTURE", + "PIXELTYPE", "IMAGE_STRUCTURE", + NULL }; + + for( iOptItem = 0; nBands > 0 && apszOptItems[iOptItem] != NULL; iOptItem += 2 ) + { + // does the source have this metadata item on the first band? + const char *pszValue = + poSrcDS->GetRasterBand(1)->GetMetadataItem( + apszOptItems[iOptItem], apszOptItems[iOptItem+1] ); + + if( pszValue == NULL ) + continue; + + // do not override provided value. + if( CSLFetchNameValue( papszCreateOptions, pszValue ) != NULL ) + continue; + + // Does this appear to be a supported creation option on this driver? + const char *pszOptionList = + GetMetadataItem( GDAL_DMD_CREATIONDATATYPES ); + + if( pszOptionList == NULL + || strstr(pszOptionList,apszOptItems[iOptItem]) != NULL ) + continue; + + papszCreateOptions = CSLSetNameValue( papszCreateOptions, + apszOptItems[iOptItem], + pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Create destination dataset. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDstDS; + GDALDataType eType = GDT_Unknown; + CPLErr eErr = CE_None; + + if( nBands > 0 ) + eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + poDstDS = Create( pszFilename, nXSize, nYSize, + nBands, eType, papszCreateOptions ); + + CSLDestroy(papszCreateOptions); + + if( poDstDS == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try setting the projection and geotransform if it seems */ +/* suitable. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + if( eErr == CE_None + && poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None + && (adfGeoTransform[0] != 0.0 + || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 + || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 + || adfGeoTransform[5] != 1.0) ) + { + eErr = poDstDS->SetGeoTransform( adfGeoTransform ); + if( !bStrict ) + eErr = CE_None; + } + + if( eErr == CE_None + && poSrcDS->GetProjectionRef() != NULL + && strlen(poSrcDS->GetProjectionRef()) > 0 ) + { + eErr = poDstDS->SetProjection( poSrcDS->GetProjectionRef() ); + if( !bStrict ) + eErr = CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Copy GCPs. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetGCPCount() > 0 && eErr == CE_None ) + { + eErr = poDstDS->SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + poSrcDS->GetGCPProjection() ); + if( !bStrict ) + eErr = CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Copy metadata. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetMetadata() != NULL ) + poDstDS->SetMetadata( poSrcDS->GetMetadata() ); + +/* -------------------------------------------------------------------- */ +/* Copy transportable special domain metadata (RPCs). It would */ +/* be nice to copy geolocation, but is is pretty fragile. */ +/* -------------------------------------------------------------------- */ + char **papszMD = poSrcDS->GetMetadata( "RPC" ); + if( papszMD ) + poDstDS->SetMetadata( papszMD, "RPC" ); + +/* -------------------------------------------------------------------- */ +/* Loop copying bands. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; + eErr == CE_None && iBand < nBands; + iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand+1 ); + +/* -------------------------------------------------------------------- */ +/* Do we need to copy a colortable. */ +/* -------------------------------------------------------------------- */ + GDALColorTable *poCT; + int bSuccess; + double dfValue; + + poCT = poSrcBand->GetColorTable(); + if( poCT != NULL ) + poDstBand->SetColorTable( poCT ); + +/* -------------------------------------------------------------------- */ +/* Do we need to copy other metadata? Most of this is */ +/* non-critical, so lets not bother folks if it fails are we */ +/* are not in strict mode. */ +/* -------------------------------------------------------------------- */ + if( !bStrict ) + CPLPushErrorHandler( CPLQuietErrorHandler ); + + if( strlen(poSrcBand->GetDescription()) > 0 ) + poDstBand->SetDescription( poSrcBand->GetDescription() ); + + if( CSLCount(poSrcBand->GetMetadata()) > 0 ) + poDstBand->SetMetadata( poSrcBand->GetMetadata() ); + + dfValue = poSrcBand->GetOffset( &bSuccess ); + if( bSuccess && dfValue != 0.0 ) + poDstBand->SetOffset( dfValue ); + + dfValue = poSrcBand->GetScale( &bSuccess ); + if( bSuccess && dfValue != 1.0 ) + poDstBand->SetScale( dfValue ); + + dfValue = poSrcBand->GetNoDataValue( &bSuccess ); + if( bSuccess ) + poDstBand->SetNoDataValue( dfValue ); + + if( poSrcBand->GetColorInterpretation() != GCI_Undefined + && poSrcBand->GetColorInterpretation() + != poDstBand->GetColorInterpretation() ) + poDstBand->SetColorInterpretation( + poSrcBand->GetColorInterpretation() ); + + char** papszCatNames; + papszCatNames = poSrcBand->GetCategoryNames(); + if (0 != papszCatNames) + poDstBand->SetCategoryNames( papszCatNames ); + + if( !bStrict ) + { + CPLPopErrorHandler(); + CPLErrorReset(); + } + else + eErr = CPLGetLastErrorType(); + } + +/* -------------------------------------------------------------------- */ +/* Copy image data. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && nBands > 0 ) + eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS, + (GDALDatasetH) poDstDS, + NULL, pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Should we copy some masks over? */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && nBands > 0 ) + eErr = DefaultCopyMasks( poSrcDS, poDstDS, eErr ); + +/* -------------------------------------------------------------------- */ +/* Copy vector layers */ +/* -------------------------------------------------------------------- */ + + if( eErr == CE_None ) + { + if( nLayerCount > 0 && poDstDS->TestCapability(ODsCCreateLayer) ) + { + for( int iLayer = 0; iLayer < nLayerCount; iLayer++ ) + { + OGRLayer *poLayer = poSrcDS->GetLayer(iLayer); + + if( poLayer == NULL ) + continue; + + poDstDS->CopyLayer( poLayer, poLayer->GetName(), NULL ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to cleanup the output dataset if the translation failed. */ +/* -------------------------------------------------------------------- */ + if( eErr != CE_None ) + { + delete poDstDS; + Delete( pszFilename ); + return NULL; + } + else + CPLErrorReset(); + + return poDstDS; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +/** + * \brief Create a copy of a dataset. + * + * This method will attempt to create a copy of a raster dataset with the + * indicated filename, and in this drivers format. Band number, size, + * type, projection, geotransform and so forth are all to be copied from + * the provided template dataset. + * + * Note that many sequential write once formats (such as JPEG and PNG) don't + * implement the Create() method but do implement this CreateCopy() method. + * If the driver doesn't implement CreateCopy(), but does implement Create() + * then the default CreateCopy() mechanism built on calling Create() will + * be used. + * + * It is intended that CreateCopy() will often be used with a source dataset + * which is a virtual dataset allowing configuration of band types, and + * other information without actually duplicating raster data (see the VRT driver). + * This is what is done by the gdal_translate utility for example. + * + * That function will try to validate the creation option list passed to the driver + * with the GDALValidateCreationOptions() method. This check can be disabled + * by defining the configuration option GDAL_VALIDATE_CREATION_OPTIONS=NO. + * + * After you have finished working with the returned dataset, it is required + * to close it with GDALClose(). This does not only close the file handle, but + * also ensures that all the data and metadata has been written to the dataset + * (GDALFlushCache() is not sufficient for that purpose). + * + * In some situations, the new dataset can be created in another process through the + * \ref gdal_api_proxy mechanism. + * + * @param pszFilename the name for the new dataset. UTF-8 encoded. + * @param poSrcDS the dataset being duplicated. + * @param bStrict TRUE if the copy must be strictly equivelent, or more + * normally FALSE indicating that the copy may adapt as needed for the + * output format. + * @param papszOptions additional format dependent options controlling + * creation of the output file. + * @param pfnProgress a function to be used to report progress of the copy. + * @param pProgressData application data passed into progress function. + * + * @return a pointer to the newly created dataset (may be read-only access). + */ + +GDALDataset *GDALDriver::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + CPLLocaleC oLocaleForcer; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + const char* pszClientFilename = GDALClientDatasetGetFilename(pszFilename); + if( pszClientFilename != NULL && !EQUAL(GetDescription(), "MEM") && + !EQUAL(GetDescription(), "VRT") ) + { + GDALDriver* poAPIPROXYDriver = GDALGetAPIPROXYDriver(); + if( poAPIPROXYDriver != this ) + { + if( poAPIPROXYDriver->pfnCreateCopy == NULL ) + return NULL; + char** papszOptionsDup = CSLDuplicate(papszOptions); + papszOptionsDup = CSLAddNameValue(papszOptionsDup, "SERVER_DRIVER", + GetDescription()); + GDALDataset* poDstDS = poAPIPROXYDriver->pfnCreateCopy( + pszClientFilename, poSrcDS, bStrict, papszOptionsDup, + pfnProgress, pProgressData); + if( poDstDS != NULL ) + { + if( poDstDS->GetDescription() == NULL + || strlen(poDstDS->GetDescription()) == 0 ) + poDstDS->SetDescription( pszFilename ); + + if( poDstDS->poDriver == NULL ) + poDstDS->poDriver = poAPIPROXYDriver; + } + + CSLDestroy(papszOptionsDup); + if( poDstDS != NULL || CPLGetLastErrorNo() != CPLE_NotSupported ) + return poDstDS; + } + } + +/* -------------------------------------------------------------------- */ +/* Make sure we cleanup if there is an existing dataset of this */ +/* name. But even if that seems to fail we will continue since */ +/* it might just be a corrupt file or something. */ +/* -------------------------------------------------------------------- */ + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) && + CSLFetchBoolean(papszOptions, "QUIET_DELETE_ON_CREATE_COPY", TRUE) ) + QuietDelete( pszFilename ); + + int iIdxQuietDeleteOnCreateCopy = + CSLPartialFindString(papszOptions, "QUIET_DELETE_ON_CREATE_COPY="); + char** papszOptionsToDelete = NULL; + if( iIdxQuietDeleteOnCreateCopy >= 0 ) + { + papszOptions = CSLRemoveStrings(CSLDuplicate(papszOptions), iIdxQuietDeleteOnCreateCopy, 1, NULL); + papszOptionsToDelete = papszOptions; + } + +/* -------------------------------------------------------------------- */ +/* Validate creation options. */ +/* -------------------------------------------------------------------- */ + if (CSLTestBoolean(CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES"))) + GDALValidateCreationOptions( this, papszOptions); + +/* -------------------------------------------------------------------- */ +/* If the format provides a CreateCopy() method use that, */ +/* otherwise fallback to the internal implementation using the */ +/* Create() method. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDstDS; + if( pfnCreateCopy != NULL && !CSLTestBoolean(CPLGetConfigOption("GDAL_DEFAULT_CREATE_COPY", "NO")) ) + { + poDstDS = pfnCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions, + pfnProgress, pProgressData ); + if( poDstDS != NULL ) + { + if( poDstDS->GetDescription() == NULL + || strlen(poDstDS->GetDescription()) == 0 ) + poDstDS->SetDescription( pszFilename ); + + if( poDstDS->poDriver == NULL ) + poDstDS->poDriver = this; + } + } + else + poDstDS = DefaultCreateCopy( pszFilename, poSrcDS, bStrict, + papszOptions, pfnProgress, pProgressData ); + + CSLDestroy(papszOptionsToDelete); + return poDstDS; +} + +/************************************************************************/ +/* GDALCreateCopy() */ +/************************************************************************/ + +/** + * \brief Create a copy of a dataset. + * + * @see GDALDriver::CreateCopy() + */ + +GDALDatasetH CPL_STDCALL GDALCreateCopy( GDALDriverH hDriver, + const char * pszFilename, + GDALDatasetH hSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + VALIDATE_POINTER1( hDriver, "GDALCreateCopy", NULL ); + VALIDATE_POINTER1( hSrcDS, "GDALCreateCopy", NULL ); + + return (GDALDatasetH) ((GDALDriver *) hDriver)-> + CreateCopy( pszFilename, (GDALDataset *) hSrcDS, bStrict, papszOptions, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* QuietDelete() */ +/************************************************************************/ + +/** + * \brief Delete dataset if found. + * + * This is a helper method primarily used by Create() and + * CreateCopy() to predelete any dataset of the name soon to be + * created. It will attempt to delete the named dataset if + * one is found, otherwise it does nothing. An error is only + * returned if the dataset is found but the delete fails. + * + * This is a static method and it doesn't matter what driver instance + * it is invoked on. It will attempt to discover the correct driver + * using Identify(). + * + * @param pszName the dataset name to try and delete. + * @return CE_None if the dataset does not exist, or is deleted without issues. + */ + +CPLErr GDALDriver::QuietDelete( const char *pszName ) + +{ + CPLPushErrorHandler(CPLQuietErrorHandler); + GDALDriver *poDriver = (GDALDriver*) GDALIdentifyDriver( pszName, NULL ); + CPLPopErrorHandler(); + + if( poDriver == NULL ) + return CE_None; + + VSIStatBufL sStat; + if( VSIStatExL(pszName, &sStat, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 && + VSI_ISDIR(sStat.st_mode) ) + { + /* It is not desirable to remove directories quietly */ + /* Necessary to avoid ogr_mitab_12 to destroy file created at ogr_mitab_7 */ + return CE_None; + } + + CPLDebug( "GDAL", "QuietDelete(%s) invoking Delete()", pszName ); + + return poDriver->Delete( pszName ); +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +/** + * \brief Delete named dataset. + * + * The driver will attempt to delete the named dataset in a driver specific + * fashion. Full featured drivers will delete all associated files, + * database objects, or whatever is appropriate. The default behaviour when + * no driver specific behaviour is provided is to attempt to delete the + * passed name as a single file. + * + * It is unwise to have open dataset handles on this dataset when it is + * deleted. + * + * Equivelent of the C function GDALDeleteDataset(). + * + * @param pszFilename name of dataset to delete. + * + * @return CE_None on success, or CE_Failure if the operation fails. + */ + +CPLErr GDALDriver::Delete( const char * pszFilename ) + +{ + if( pfnDelete != NULL ) + return pfnDelete( pszFilename ); + else if( pfnDeleteDataSource != NULL ) + return pfnDeleteDataSource( this, pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Collect file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = (GDALDataset *) GDALOpenEx(pszFilename,0,NULL,NULL,NULL); + + if( hDS == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s to obtain file list.", pszFilename ); + + return CE_Failure; + } + + char **papszFileList = GDALGetFileList( hDS ); + + GDALClose( hDS ); + + if( CSLCount( papszFileList ) == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to determine files associated with %s,\n" + "delete fails.", pszFilename ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Delete all files. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( VSIUnlink( papszFileList[i] ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Deleting %s failed:\n%s", + papszFileList[i], + VSIStrerror(errno) ); + CSLDestroy( papszFileList ); + return CE_Failure; + } + } + + CSLDestroy( papszFileList ); + + return CE_None; +} + +/************************************************************************/ +/* GDALDeleteDataset() */ +/************************************************************************/ + +/** + * \brief Delete named dataset. + * + * @see GDALDriver::Delete() + */ + +CPLErr CPL_STDCALL GDALDeleteDataset( GDALDriverH hDriver, const char * pszFilename ) + +{ + if( hDriver == NULL ) + hDriver = GDALIdentifyDriver( pszFilename, NULL ); + + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No identifiable driver for %s.", + pszFilename ); + return CE_Failure; + } + + return ((GDALDriver *) hDriver)->Delete( pszFilename ); +} + +/************************************************************************/ +/* DefaultRename() */ +/* */ +/* The generic implementation based on the file list used when */ +/* there is no format specific implementation. */ +/************************************************************************/ + +CPLErr GDALDriver::DefaultRename( const char * pszNewName, + const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Collect file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = (GDALDataset *) GDALOpen(pszOldName,GA_ReadOnly); + + if( hDS == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s to obtain file list.", pszOldName ); + + return CE_Failure; + } + + char **papszFileList = GDALGetFileList( hDS ); + + GDALClose( hDS ); + + if( CSLCount( papszFileList ) == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to determine files associated with %s,\n" + "rename fails.", pszOldName ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Produce a list of new filenames that correspond to the old */ +/* names. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int i; + char **papszNewFileList = + CPLCorrespondingPaths( pszOldName, pszNewName, papszFileList ); + + if( papszNewFileList == NULL ) + return CE_Failure; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( CPLMoveFile( papszNewFileList[i], papszFileList[i] ) != 0 ) + { + eErr = CE_Failure; + // Try to put the ones we moved back. + for( --i; i >= 0; i-- ) + CPLMoveFile( papszFileList[i], papszNewFileList[i] ); + break; + } + } + + CSLDestroy( papszNewFileList ); + CSLDestroy( papszFileList ); + + return eErr; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +/** + * \brief Rename a dataset. + * + * Rename a dataset. This may including moving the dataset to a new directory + * or even a new filesystem. + * + * It is unwise to have open dataset handles on this dataset when it is + * being renamed. + * + * Equivelent of the C function GDALRenameDataset(). + * + * @param pszNewName new name for the dataset. + * @param pszOldName old name for the dataset. + * + * @return CE_None on success, or CE_Failure if the operation fails. + */ + +CPLErr GDALDriver::Rename( const char * pszNewName, const char *pszOldName ) + +{ + if( pfnRename != NULL ) + return pfnRename( pszNewName, pszOldName ); + else + return DefaultRename( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* GDALRenameDataset() */ +/************************************************************************/ + +/** + * \brief Rename a dataset. + * + * @see GDALDriver::Rename() + */ + +CPLErr CPL_STDCALL GDALRenameDataset( GDALDriverH hDriver, + const char * pszNewName, + const char * pszOldName ) + +{ + if( hDriver == NULL ) + hDriver = GDALIdentifyDriver( pszOldName, NULL ); + + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No identifiable driver for %s.", + pszOldName ); + return CE_Failure; + } + + return ((GDALDriver *) hDriver)->Rename( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* DefaultCopyFiles() */ +/* */ +/* The default implementation based on file lists used when */ +/* there is no format specific implementation. */ +/************************************************************************/ + +CPLErr GDALDriver::DefaultCopyFiles( const char * pszNewName, + const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Collect file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = (GDALDataset *) GDALOpen(pszOldName,GA_ReadOnly); + + if( hDS == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s to obtain file list.", pszOldName ); + + return CE_Failure; + } + + char **papszFileList = GDALGetFileList( hDS ); + + GDALClose( hDS ); + + if( CSLCount( papszFileList ) == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to determine files associated with %s,\n" + "rename fails.", pszOldName ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Produce a list of new filenames that correspond to the old */ +/* names. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int i; + char **papszNewFileList = + CPLCorrespondingPaths( pszOldName, pszNewName, papszFileList ); + + if( papszNewFileList == NULL ) + return CE_Failure; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( CPLCopyFile( papszNewFileList[i], papszFileList[i] ) != 0 ) + { + eErr = CE_Failure; + // Try to put the ones we moved back. + for( --i; i >= 0; i-- ) + VSIUnlink( papszNewFileList[i] ); + break; + } + } + + CSLDestroy( papszNewFileList ); + CSLDestroy( papszFileList ); + + return eErr; +} + +/************************************************************************/ +/* CopyFiles() */ +/************************************************************************/ + +/** + * \brief Copy the files of a dataset. + * + * Copy all the files associated with a dataset. + * + * Equivelent of the C function GDALCopyDatasetFiles(). + * + * @param pszNewName new name for the dataset. + * @param pszOldName old name for the dataset. + * + * @return CE_None on success, or CE_Failure if the operation fails. + */ + +CPLErr GDALDriver::CopyFiles( const char * pszNewName, const char *pszOldName ) + +{ + if( pfnCopyFiles != NULL ) + return pfnCopyFiles( pszNewName, pszOldName ); + else + return DefaultCopyFiles( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* GDALCopyDatasetFiles() */ +/************************************************************************/ + +/** + * \brief Copy the files of a dataset. + * + * @see GDALDriver::CopyFiles() + */ + +CPLErr CPL_STDCALL GDALCopyDatasetFiles( GDALDriverH hDriver, + const char * pszNewName, + const char * pszOldName ) + +{ + if( hDriver == NULL ) + hDriver = GDALIdentifyDriver( pszOldName, NULL ); + + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No identifiable driver for %s.", + pszOldName ); + return CE_Failure; + } + + return ((GDALDriver *) hDriver)->CopyFiles( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* GDALGetDriverShortName() */ +/************************************************************************/ + +/** + * \brief Return the short name of a driver + * + * This is the string that can be + * passed to the GDALGetDriverByName() function. + * + * For the GeoTIFF driver, this is "GTiff" + * + * @param hDriver the handle of the driver + * @return the short name of the driver. The + * returned string should not be freed and is owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverShortName( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverShortName", NULL ); + + return ((GDALDriver *) hDriver)->GetDescription(); +} + +/************************************************************************/ +/* GDALGetDriverLongName() */ +/************************************************************************/ + +/** + * \brief Return the long name of a driver + * + * For the GeoTIFF driver, this is "GeoTIFF" + * + * @param hDriver the handle of the driver + * @return the long name of the driver or empty string. The + * returned string should not be freed and is owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverLongName( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverLongName", NULL ); + + const char *pszLongName = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_LONGNAME ); + + if( pszLongName == NULL ) + return ""; + else + return pszLongName; +} + +/************************************************************************/ +/* GDALGetDriverHelpTopic() */ +/************************************************************************/ + +/** + * \brief Return the URL to the help that describes the driver + * + * That URL is relative to the GDAL documentation directory. + * + * For the GeoTIFF driver, this is "frmt_gtiff.html" + * + * @param hDriver the handle of the driver + * @return the URL to the help that describes the driver or NULL. The + * returned string should not be freed and is owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverHelpTopic( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverHelpTopic", NULL ); + + return ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_HELPTOPIC ); +} + +/************************************************************************/ +/* GDALGetDriverCreationOptionList() */ +/************************************************************************/ + +/** + * \brief Return the list of creation options of the driver + * + * Return the list of creation options of the driver used by Create() and + * CreateCopy() as an XML string + * + * @param hDriver the handle of the driver + * @return an XML string that describes the list of creation options or + * empty string. The returned string should not be freed and is + * owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverCreationOptionList( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverCreationOptionList", NULL ); + + const char *pszOptionList = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST ); + + if( pszOptionList == NULL ) + return ""; + else + return pszOptionList; +} + +/************************************************************************/ +/* GDALValidateCreationOptions() */ +/************************************************************************/ + +/** + * \brief Validate the list of creation options that are handled by a driver + * + * This is a helper method primarily used by Create() and + * CreateCopy() to validate that the passed in list of creation options + * is compatible with the GDAL_DMD_CREATIONOPTIONLIST metadata item defined + * by some drivers. @see GDALGetDriverCreationOptionList() + * + * If the GDAL_DMD_CREATIONOPTIONLIST metadata item is not defined, this + * function will return TRUE. Otherwise it will check that the keys and values + * in the list of creation options are compatible with the capabilities declared + * by the GDAL_DMD_CREATIONOPTIONLIST metadata item. In case of incompatibility + * a (non fatal) warning will be emited and FALSE will be returned. + * + * @param hDriver the handle of the driver with whom the lists of creation option + * must be validated + * @param papszCreationOptions the list of creation options. An array of strings, + * whose last element is a NULL pointer + * @return TRUE if the list of creation options is compatible with the Create() + * and CreateCopy() method of the driver, FALSE otherwise. + */ + +int CPL_STDCALL GDALValidateCreationOptions( GDALDriverH hDriver, + char** papszCreationOptions) +{ + VALIDATE_POINTER1( hDriver, "GDALValidateCreationOptions", FALSE ); + const char *pszOptionList = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST ); + CPLString osDriver; + osDriver.Printf("driver %s", ((GDALDriver *) hDriver)->GetDescription()); + return GDALValidateOptions( pszOptionList, + (const char* const* )papszCreationOptions, + "creation option", + osDriver); +} + +/************************************************************************/ +/* GDALValidateOpenOptions() */ +/************************************************************************/ + +int GDALValidateOpenOptions( GDALDriverH hDriver, + const char* const* papszOpenOptions) +{ + VALIDATE_POINTER1( hDriver, "GDALValidateOpenOptions", FALSE ); + const char *pszOptionList = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_OPENOPTIONLIST ); + CPLString osDriver; + osDriver.Printf("driver %s", ((GDALDriver *) hDriver)->GetDescription()); + return GDALValidateOptions( pszOptionList, papszOpenOptions, + "open option", + osDriver); +} + +/************************************************************************/ +/* GDALValidateOptions() */ +/************************************************************************/ + +int GDALValidateOptions( const char* pszOptionList, + const char* const* papszOptionsToValidate, + const char* pszErrorMessageOptionType, + const char* pszErrorMessageContainerName) +{ + int bRet = TRUE; + + if( papszOptionsToValidate == NULL || *papszOptionsToValidate == NULL) + return TRUE; + if( pszOptionList == NULL ) + return TRUE; + + CPLXMLNode* psNode = CPLParseXMLString(pszOptionList); + if (psNode == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Could not parse %s list of %s. Assuming options are valid.", + pszErrorMessageOptionType, pszErrorMessageContainerName); + return TRUE; + } + + while(*papszOptionsToValidate) + { + char* pszKey = NULL; + const char* pszValue = CPLParseNameValue(*papszOptionsToValidate, &pszKey); + if (pszKey == NULL) + { + CPLError(CE_Warning, CPLE_NotSupported, + "%s '%s' is not formatted with the key=value format", + pszErrorMessageOptionType, + *papszOptionsToValidate); + bRet = FALSE; + + papszOptionsToValidate ++; + continue; + } + + CPLXMLNode* psChildNode = psNode->psChild; + while(psChildNode) + { + if (EQUAL(psChildNode->pszValue, "OPTION")) + { + const char* pszOptionName = CPLGetXMLValue(psChildNode, "name", ""); + /* For option names terminated by wildcard (NITF BLOCKA option names for example) */ + if (strlen(pszOptionName) > 0 && + pszOptionName[strlen(pszOptionName) - 1] == '*' && + EQUALN(pszOptionName, pszKey, strlen(pszOptionName) - 1)) + { + break; + } + + /* For option names beginning by a wildcard */ + if( pszOptionName[0] == '*' && + strlen(pszKey) > strlen(pszOptionName) && + EQUAL( pszKey + strlen(pszKey) - strlen(pszOptionName + 1), pszOptionName + 1 ) ) + { + break; + } + + if (EQUAL(pszOptionName, pszKey) || + EQUAL(CPLGetXMLValue(psChildNode, "alias", ""), pszKey)) + { + break; + } + } + psChildNode = psChildNode->psNext; + } + if (psChildNode == NULL) + { + CPLError(CE_Warning, CPLE_NotSupported, + "%s does not support %s %s", + pszErrorMessageContainerName, + pszErrorMessageOptionType, + pszKey); + CPLFree(pszKey); + bRet = FALSE; + + papszOptionsToValidate ++; + continue; + } + const char* pszType = CPLGetXMLValue(psChildNode, "type", NULL); + const char* pszMin = CPLGetXMLValue(psChildNode, "min", NULL); + const char* pszMax = CPLGetXMLValue(psChildNode, "max", NULL); + if (pszType != NULL) + { + if (EQUAL(pszType, "INT") || EQUAL(pszType, "INTEGER")) + { + const char* pszValueIter = pszValue; + while (*pszValueIter) + { + if (!((*pszValueIter >= '0' && *pszValueIter <= '9') || + *pszValueIter == '+' || *pszValueIter == '-')) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type int.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + break; + } + pszValueIter++; + } + if( *pszValueIter == '0' ) + { + if( pszMin && atoi(pszValue) < atoi(pszMin) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be >= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMin); + break; + } + if( pszMax && atoi(pszValue) > atoi(pszMax) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be <= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMax); + break; + } + } + } + else if (EQUAL(pszType, "UNSIGNED INT")) + { + const char* pszValueIter = pszValue; + while (*pszValueIter) + { + if (!((*pszValueIter >= '0' && *pszValueIter <= '9') || + *pszValueIter == '+')) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type unsigned int.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + break; + } + pszValueIter++; + if( *pszValueIter == '0' ) + { + if( pszMin && atoi(pszValue) < atoi(pszMin) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be >= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMin); + break; + } + if( pszMax && atoi(pszValue) > atoi(pszMax) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be <= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMax); + break; + } + } + } + } + else if (EQUAL(pszType, "FLOAT")) + { + char* endPtr = NULL; + double dfVal = CPLStrtod(pszValue, &endPtr); + if ( !(endPtr == NULL || *endPtr == '\0') ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type float.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + } + else + { + if( pszMin && dfVal < CPLAtof(pszMin) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be >= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMin); + break; + } + if( pszMax && dfVal > CPLAtof(pszMax) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be <= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMax); + break; + } + } + } + else if (EQUAL(pszType, "BOOLEAN")) + { + if (!(EQUAL(pszValue, "ON") || EQUAL(pszValue, "TRUE") || EQUAL(pszValue, "YES") || + EQUAL(pszValue, "OFF") || EQUAL(pszValue, "FALSE") || EQUAL(pszValue, "NO"))) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type boolean.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + } + } + else if (EQUAL(pszType, "STRING-SELECT")) + { + int bMatchFound = FALSE; + CPLXMLNode* psStringSelect = psChildNode->psChild; + while(psStringSelect) + { + if (psStringSelect->eType == CXT_Element && + EQUAL(psStringSelect->pszValue, "Value")) + { + CPLXMLNode* psOptionNode = psStringSelect->psChild; + while(psOptionNode) + { + if (psOptionNode->eType == CXT_Text && + EQUAL(psOptionNode->pszValue, pszValue)) + { + bMatchFound = TRUE; + break; + } + psOptionNode = psOptionNode->psNext; + } + if (bMatchFound) + break; + } + psStringSelect = psStringSelect->psNext; + } + if (!bMatchFound) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type string-select.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + } + } + else if (EQUAL(pszType, "STRING")) + { + const char* pszMaxSize = CPLGetXMLValue(psChildNode, "maxsize", NULL); + if (pszMaxSize != NULL) + { + if ((int)strlen(pszValue) > atoi(pszMaxSize)) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is of size %d, whereas maximum size for %s %s is %d.", + pszValue, (int)strlen(pszValue), pszKey, + pszErrorMessageOptionType, atoi(pszMaxSize)); + bRet = FALSE; + } + } + } + else + { + /* Driver error */ + CPLError(CE_Warning, CPLE_NotSupported, + "%s : type '%s' for %s %s is not recognized.", + pszErrorMessageContainerName, + pszType, + pszKey, + pszErrorMessageOptionType); + } + } + else + { + /* Driver error */ + CPLError(CE_Warning, CPLE_NotSupported, + "%s : no type for %s %s.", + pszErrorMessageContainerName, + pszKey, + pszErrorMessageOptionType); + } + CPLFree(pszKey); + papszOptionsToValidate++; + } + + CPLDestroyXMLNode(psNode); + return bRet; +} + +/************************************************************************/ +/* GDALIdentifyDriver() */ +/************************************************************************/ + +/** + * \brief Identify the driver that can open a raster file. + * + * This function will try to identify the driver that can open the passed file + * name by invoking the Identify method of each registered GDALDriver in turn. + * The first driver that successful identifies the file name will be returned. + * If all drivers fail then NULL is returned. + * + * In order to reduce the need for such searches touch the operating system + * file system machinery, it is possible to give an optional list of files. + * This is the list of all files at the same level in the file system as the + * target file, including the target file. The filenames will not include any + * path components, are an essentially just the output of CPLReadDir() on the + * parent directory. If the target object does not have filesystem semantics + * then the file list should be NULL. + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. + * + * @param papszFileList an array of strings, whose last element is the NULL pointer. + * These strings are filenames that are auxiliary to the main filename. The passed + * value may be NULL. + * + * @return A GDALDriverH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDriver *. + */ + + +GDALDriverH CPL_STDCALL +GDALIdentifyDriver( const char * pszFilename, + char **papszFileList ) + +{ + int iDriver; + GDALDriverManager *poDM = GetGDALDriverManager(); + GDALOpenInfo oOpenInfo( pszFilename, GA_ReadOnly, papszFileList ); + CPLLocaleC oLocaleForcer; + + CPLErrorReset(); + CPLAssert( NULL != poDM ); + + int nDriverCount = poDM->GetDriverCount(); + + // First pass: only use drivers that have a pfnIdentify implementation + for( iDriver = -1; iDriver < nDriverCount; iDriver++ ) + { + GDALDriver *poDriver; + + if( iDriver < 0 ) + poDriver = GDALGetAPIPROXYDriver(); + else + poDriver = poDM->GetDriver( iDriver ); + + VALIDATE_POINTER1( poDriver, "GDALIdentifyDriver", NULL ); + + if( poDriver->pfnIdentify != NULL ) + { + if( poDriver->pfnIdentify( &oOpenInfo ) > 0 ) + return (GDALDriverH) poDriver; + } + } + + // Second pass: slow method + for( iDriver = -1; iDriver < nDriverCount; iDriver++ ) + { + GDALDriver *poDriver; + GDALDataset *poDS; + + if( iDriver < 0 ) + poDriver = GDALGetAPIPROXYDriver(); + else + poDriver = poDM->GetDriver( iDriver ); + + VALIDATE_POINTER1( poDriver, "GDALIdentifyDriver", NULL ); + + if( poDriver->pfnIdentify != NULL ) + { + if( poDriver->pfnIdentify( &oOpenInfo ) == 0 ) + continue; + } + + if( poDriver->pfnOpen != NULL ) + { + poDS = poDriver->pfnOpen( &oOpenInfo ); + if( poDS != NULL ) + { + delete poDS; + return (GDALDriverH) poDriver; + } + + if( CPLGetLastErrorNo() != 0 ) + return NULL; + } + else if( poDriver->pfnOpenWithDriverArg != NULL ) + { + poDS = poDriver->pfnOpenWithDriverArg( poDriver, &oOpenInfo ); + if( poDS != NULL ) + { + delete poDS; + return (GDALDriverH) poDriver; + } + + if( CPLGetLastErrorNo() != 0 ) + return NULL; + } + } + + return NULL; +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALDriver::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) + +{ + if( pszDomain == NULL || pszDomain[0] == '\0' ) + { + /* Automatically sets GDAL_DMD_EXTENSIONS from GDAL_DMD_EXTENSION */ + if( EQUAL(pszName, GDAL_DMD_EXTENSION) && + GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSIONS) == NULL ) + { + GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSIONS, pszValue); + } + } + return GDALMajorObject::SetMetadataItem(pszName, pszValue, pszDomain); +} diff --git a/ogr/gdaldrivermanager.cpp b/ogr/gdaldrivermanager.cpp new file mode 100644 index 0000000..787da63 --- /dev/null +++ b/ogr/gdaldrivermanager.cpp @@ -0,0 +1,848 @@ +/****************************************************************************** + * $Id: gdaldrivermanager.cpp 27487 2014-07-02 17:13:13Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALDriverManager class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "ogr_srs_api.h" +#include "cpl_multiproc.h" +#include "gdal_pam.h" +#include "gdal_alg_priv.h" + +#ifdef _MSC_VER +# ifdef MSVC_USE_VLD +# include +# include +# endif +#endif + +CPL_CVSID("$Id: gdaldrivermanager.cpp 27487 2014-07-02 17:13:13Z rouault $"); + +static const char *pszUpdatableINST_DATA = +"__INST_DATA_TARGET: "; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALDriverManager */ +/* ==================================================================== */ +/************************************************************************/ + +static volatile GDALDriverManager *poDM = NULL; +static void *hDMMutex = NULL; + +void** GDALGetphDMMutex() { return &hDMMutex; } + +/************************************************************************/ +/* GetGDALDriverManager() */ +/* */ +/* A freestanding function to get the only instance of the */ +/* GDALDriverManager. */ +/************************************************************************/ + +/** + * \brief Fetch the global GDAL driver manager. + * + * This function fetches the pointer to the singleton global driver manager. + * If the driver manager doesn't exist it is automatically created. + * + * @return pointer to the global driver manager. This should not be able + * to fail. + */ + +GDALDriverManager * GetGDALDriverManager() + +{ + if( poDM == NULL ) + { + CPLMutexHolderD( &hDMMutex ); + + if( poDM == NULL ) + poDM = new GDALDriverManager(); + } + + CPLAssert( NULL != poDM ); + + return const_cast( poDM ); +} + +/************************************************************************/ +/* GDALDriverManager() */ +/************************************************************************/ + +GDALDriverManager::GDALDriverManager() + +{ + nDrivers = 0; + papoDrivers = NULL; + + CPLAssert( poDM == NULL ); + +/* -------------------------------------------------------------------- */ +/* We want to push a location to search for data files */ +/* supporting GDAL/OGR such as EPSG csv files, S-57 definition */ +/* files, and so forth. The static pszUpdateableINST_DATA */ +/* string can be updated within the shared library or */ +/* executable during an install to point installed data */ +/* directory. If it isn't burned in here then we use the */ +/* INST_DATA macro (setup at configure time) if */ +/* available. Otherwise we don't push anything and we hope */ +/* other mechanisms such as environment variables will have */ +/* been employed. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "GDAL_DATA", NULL ) != NULL ) + { + // this one is picked up automatically by finder initialization. + } + else if( pszUpdatableINST_DATA[19] != ' ' ) + { + CPLPushFinderLocation( pszUpdatableINST_DATA + 19 ); + } + else + { +#ifdef INST_DATA + CPLPushFinderLocation( INST_DATA ); +#endif + } +} + +/************************************************************************/ +/* ~GDALDriverManager() */ +/************************************************************************/ + +void GDALDatasetPoolPreventDestroy(); /* keep that in sync with gdalproxypool.cpp */ +void GDALDatasetPoolForceDestroy(); /* keep that in sync with gdalproxypool.cpp */ + +GDALDriverManager::~GDALDriverManager() + +{ +/* -------------------------------------------------------------------- */ +/* Cleanup any open datasets. */ +/* -------------------------------------------------------------------- */ + int i, nDSCount; + GDALDataset **papoDSList; + + /* First begin by requesting each reamining dataset to drop any reference */ + /* to other datasets */ + int bHasDroppedRef; + + /* We have to prevent the destroying of the dataset pool during this first */ + /* phase, otherwise it cause crashes with a VRT B referencing a VRT A, and if */ + /* CloseDependentDatasets() is called first on VRT A. */ + /* If we didn't do this nasty trick, due to the refCountOfDisableRefCount */ + /* mechanism that cheats the real refcount of the dataset pool, we might */ + /* destroy the dataset pool too early, leading the VRT A to */ + /* destroy itself indirectly ... Ok, I am aware this explanation does */ + /* not make any sense unless you try it under a debugger ... */ + /* When people just manipulate "top-level" dataset handles, we luckily */ + /* don't need this horrible hack, but GetOpenDatasets() expose "low-level" */ + /* datasets, which defeat some "design" of the proxy pool */ + GDALDatasetPoolPreventDestroy(); + + do + { + papoDSList = GDALDataset::GetOpenDatasets(&nDSCount); + /* If a dataset has dropped a reference, the list might have become */ + /* invalid, so go out of the loop and try again with the new valid */ + /* list */ + bHasDroppedRef = FALSE; + for(i=0;iGetDescription() ); + bHasDroppedRef = papoDSList[i]->CloseDependentDatasets(); + } + } while(bHasDroppedRef); + + /* Now let's destroy the dataset pool. Nobody shoud use it afterwards */ + /* if people have well released their dependent datasets above */ + GDALDatasetPoolForceDestroy(); + + /* Now close the stand-alone datasets */ + papoDSList = GDALDataset::GetOpenDatasets(&nDSCount); + for(i=0;iGetDescription(), papoDSList[i] ); + /* Destroy with delete operator rather than GDALClose() to force deletion of */ + /* datasets with multiple reference count */ + /* We could also iterate while GetOpenDatasets() returns a non NULL list */ + delete papoDSList[i]; + } + +/* -------------------------------------------------------------------- */ +/* Destroy the existing drivers. */ +/* -------------------------------------------------------------------- */ + while( GetDriverCount() > 0 ) + { + GDALDriver *poDriver = GetDriver(0); + + DeregisterDriver(poDriver); + delete poDriver; + } + + delete GDALGetAPIPROXYDriver(); + +/* -------------------------------------------------------------------- */ +/* Cleanup local memory. */ +/* -------------------------------------------------------------------- */ + VSIFree( papoDrivers ); + +/* -------------------------------------------------------------------- */ +/* Cleanup any Proxy related memory. */ +/* -------------------------------------------------------------------- */ + PamCleanProxyDB(); + +/* -------------------------------------------------------------------- */ +/* Blow away all the finder hints paths. We really shouldn't */ +/* be doing all of them, but it is currently hard to keep track */ +/* of those that actually belong to us. */ +/* -------------------------------------------------------------------- */ + CPLFinderClean(); + CPLFreeConfig(); + CPLCleanupSharedFileMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup any memory allocated by the OGRSpatialReference */ +/* related subsystem. */ +/* -------------------------------------------------------------------- */ + OSRCleanup(); + +/* -------------------------------------------------------------------- */ +/* Cleanup VSIFileManager. */ +/* -------------------------------------------------------------------- */ + VSICleanupFileManager(); + +/* -------------------------------------------------------------------- */ +/* Cleanup thread local storage ... I hope the program is all */ +/* done with GDAL/OGR! */ +/* -------------------------------------------------------------------- */ + CPLCleanupTLS(); + +/* -------------------------------------------------------------------- */ +/* Cleanup our mutex. */ +/* -------------------------------------------------------------------- */ + if( hDMMutex ) + { + CPLDestroyMutex( hDMMutex ); + hDMMutex = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup dataset list mutex */ +/* -------------------------------------------------------------------- */ + if ( *GDALGetphDLMutex() != NULL ) + { + CPLDestroyMutex( *GDALGetphDLMutex() ); + *GDALGetphDLMutex() = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup raster block mutex */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock::DestroyRBMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup gdaltransformer.cpp mutex */ +/* -------------------------------------------------------------------- */ + GDALCleanupTransformDeserializerMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup cpl_error.cpp mutex */ +/* -------------------------------------------------------------------- */ + CPLCleanupErrorMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup CPLsetlocale mutex */ +/* -------------------------------------------------------------------- */ + CPLCleanupSetlocaleMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup the master CPL mutex, which governs the creation */ +/* of all other mutexes. */ +/* -------------------------------------------------------------------- */ + CPLCleanupMasterMutex(); + +/* -------------------------------------------------------------------- */ +/* Ensure the global driver manager pointer is NULLed out. */ +/* -------------------------------------------------------------------- */ + if( poDM == this ) + poDM = NULL; +} + +/************************************************************************/ +/* GetDriverCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of registered drivers. + * + * This C analog to this is GDALGetDriverCount(). + * + * @return the number of registered drivers. + */ + +int GDALDriverManager::GetDriverCount() + +{ + return( nDrivers ); +} + +/************************************************************************/ +/* GDALGetDriverCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of registered drivers. + * + * @see GDALDriverManager::GetDriverCount() + */ + +int CPL_STDCALL GDALGetDriverCount() + +{ + return GetGDALDriverManager()->GetDriverCount(); +} + +/************************************************************************/ +/* GetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch driver by index. + * + * This C analog to this is GDALGetDriver(). + * + * @param iDriver the driver index from 0 to GetDriverCount()-1. + * + * @return the driver identified by the index or NULL if the index is invalid + */ + +GDALDriver * GDALDriverManager::GetDriver( int iDriver ) + +{ + CPLMutexHolderD( &hDMMutex ); + + if( iDriver < 0 || iDriver >= nDrivers ) + return NULL; + else + return papoDrivers[iDriver]; +} + +/************************************************************************/ +/* GDALGetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch driver by index. + * + * @see GDALDriverManager::GetDriver() + */ + +GDALDriverH CPL_STDCALL GDALGetDriver( int iDriver ) + +{ + return (GDALDriverH) GetGDALDriverManager()->GetDriver(iDriver); +} + +/************************************************************************/ +/* RegisterDriver() */ +/************************************************************************/ + +/** + * \brief Register a driver for use. + * + * The C analog is GDALRegisterDriver(). + * + * Normally this method is used by format specific C callable registration + * entry points such as GDALRegister_GTiff() rather than being called + * directly by application level code. + * + * If this driver (based on the object pointer, not short name) is already + * registered, then no change is made, and the index of the existing driver + * is returned. Otherwise the driver list is extended, and the new driver + * is added at the end. + * + * @param poDriver the driver to register. + * + * @return the index of the new installed driver. + */ + +int GDALDriverManager::RegisterDriver( GDALDriver * poDriver ) + +{ + CPLMutexHolderD( &hDMMutex ); + +/* -------------------------------------------------------------------- */ +/* If it is already registered, just return the existing */ +/* index. */ +/* -------------------------------------------------------------------- */ + if( GetDriverByName( poDriver->GetDescription() ) != NULL ) + { + int i; + + for( i = 0; i < nDrivers; i++ ) + { + if( papoDrivers[i] == poDriver ) + { + return i; + } + } + + CPLAssert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise grow the list to hold the new entry. */ +/* -------------------------------------------------------------------- */ + papoDrivers = (GDALDriver **) + VSIRealloc(papoDrivers, sizeof(GDALDriver *) * (nDrivers+1)); + + papoDrivers[nDrivers] = poDriver; + nDrivers++; + + if( poDriver->pfnOpen != NULL || + poDriver->pfnOpenWithDriverArg != NULL ) + poDriver->SetMetadataItem( GDAL_DCAP_OPEN, "YES" ); + + if( poDriver->pfnCreate != NULL ) + poDriver->SetMetadataItem( GDAL_DCAP_CREATE, "YES" ); + + if( poDriver->pfnCreateCopy != NULL ) + poDriver->SetMetadataItem( GDAL_DCAP_CREATECOPY, "YES" ); + + /* Backward compability for GDAL raster out-of-tree drivers: */ + /* if a driver hasn't explicitely set a vector capability, assume it is */ + /* a raster driver (legacy OGR drivers will have DCAP_VECTOR set before */ + /* calling RegisterDriver() ) */ + if( poDriver->GetMetadataItem( GDAL_DCAP_RASTER ) == NULL && + poDriver->GetMetadataItem( GDAL_DCAP_VECTOR ) == NULL ) + { + CPLDebug("GDAL", "Assuming DCAP_RASTER for driver %s. Please fix it.", + poDriver->GetDescription() ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + } + + if( poDriver->GetMetadataItem( GDAL_DMD_OPENOPTIONLIST ) != NULL && + poDriver->pfnIdentify == NULL ) + { + CPLDebug("GDAL", "Driver %s that defines GDAL_DMD_OPENOPTIONLIST must also " + "implement Identify(), so that it can be used", + poDriver->GetDescription() ); + } + + oMapNameToDrivers[CPLString(poDriver->GetDescription()).toupper()] = poDriver; + + int iResult = nDrivers - 1; + + return iResult; +} + +/************************************************************************/ +/* GDALRegisterDriver() */ +/************************************************************************/ + +/** + * \brief Register a driver for use. + * + * @see GDALDriverManager::GetRegisterDriver() + */ + +int CPL_STDCALL GDALRegisterDriver( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALRegisterDriver", 0 ); + + return GetGDALDriverManager()->RegisterDriver( (GDALDriver *) hDriver ); +} + + +/************************************************************************/ +/* DeregisterDriver() */ +/************************************************************************/ + +/** + * \brief Deregister the passed driver. + * + * If the driver isn't found no change is made. + * + * The C analog is GDALDeregisterDriver(). + * + * @param poDriver the driver to deregister. + */ + +void GDALDriverManager::DeregisterDriver( GDALDriver * poDriver ) + +{ + int i; + CPLMutexHolderD( &hDMMutex ); + + for( i = 0; i < nDrivers; i++ ) + { + if( papoDrivers[i] == poDriver ) + break; + } + + if( i == nDrivers ) + return; + + oMapNameToDrivers.erase(CPLString(poDriver->GetDescription()).toupper()); + while( i < nDrivers-1 ) + { + papoDrivers[i] = papoDrivers[i+1]; + i++; + } + nDrivers--; +} + +/************************************************************************/ +/* GDALDeregisterDriver() */ +/************************************************************************/ + +/** + * \brief Deregister the passed driver. + * + * @see GDALDriverManager::GetDeregisterDriver() + */ + +void CPL_STDCALL GDALDeregisterDriver( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER0( hDriver, "GDALDeregisterDriver" ); + + GetGDALDriverManager()->DeregisterDriver( (GDALDriver *) hDriver ); +} + + +/************************************************************************/ +/* GetDriverByName() */ +/************************************************************************/ + +/** + * \brief Fetch a driver based on the short name. + * + * The C analog is the GDALGetDriverByName() function. + * + * @param pszName the short name, such as GTiff, being searched for. + * + * @return the identified driver, or NULL if no match is found. + */ + +GDALDriver * GDALDriverManager::GetDriverByName( const char * pszName ) + +{ + CPLMutexHolderD( &hDMMutex ); + + return oMapNameToDrivers[CPLString(pszName).toupper()]; +} + +/************************************************************************/ +/* GDALGetDriverByName() */ +/************************************************************************/ + +/** + * \brief Fetch a driver based on the short name. + * + * @see GDALDriverManager::GetDriverByName() + */ + +GDALDriverH CPL_STDCALL GDALGetDriverByName( const char * pszName ) + +{ + VALIDATE_POINTER1( pszName, "GDALGetDriverByName", NULL ); + + return( GetGDALDriverManager()->GetDriverByName( pszName ) ); +} + +/************************************************************************/ +/* AutoSkipDrivers() */ +/************************************************************************/ + +/** + * \brief This method unload undesirable drivers. + * + * All drivers specified in the comma delimited list in the GDAL_SKIP + * environment variable) will be deregistered and destroyed. This method + * should normally be called after registration of standard drivers to allow + * the user a way of unloading undesired drivers. The GDALAllRegister() + * function already invokes AutoSkipDrivers() at the end, so if that functions + * is called, it should not be necessary to call this method from application + * code. + * + * Note: space separator is also accepted for backward compatibility, but some + * vector formats have spaces in their names, so it is encouraged to use comma + * to avoid issues. + */ + +void GDALDriverManager::AutoSkipDrivers() + +{ + char **apapszList[2] = { NULL, NULL }; + const char* pszGDAL_SKIP = CPLGetConfigOption( "GDAL_SKIP", NULL ); + if( pszGDAL_SKIP != NULL ) + { + /* Favour comma as a separator. If not found, then use space */ + const char* pszSep = (strchr(pszGDAL_SKIP, ',') != NULL) ? "," : " "; + apapszList[0] = CSLTokenizeStringComplex( pszGDAL_SKIP, pszSep, FALSE, FALSE); + } + const char* pszOGR_SKIP = CPLGetConfigOption( "OGR_SKIP", NULL ); + if( pszOGR_SKIP != NULL ) + { + /* OGR has always used comma as a separator */ + apapszList[1] = CSLTokenizeStringComplex(pszOGR_SKIP, ",", FALSE, FALSE); + } + + for( int j = 0; j < 2; j++ ) + { + for( int i = 0; apapszList[j] != NULL && apapszList[j][i] != NULL; i++ ) + { + GDALDriver *poDriver = GetDriverByName( apapszList[j][i] ); + + if( poDriver == NULL ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to find driver %s to unload from GDAL_SKIP environment variable.", + apapszList[j][i] ); + else + { + CPLDebug( "GDAL", "AutoSkipDriver(%s)", apapszList[j][i] ); + DeregisterDriver( poDriver ); + delete poDriver; + } + } + } + + CSLDestroy( apapszList[0] ); + CSLDestroy( apapszList[1] ); +} + +/************************************************************************/ +/* AutoLoadDrivers() */ +/************************************************************************/ + +/** + * \brief Auto-load GDAL drivers from shared libraries. + * + * This function will automatically load drivers from shared libraries. It + * searches the "driver path" for .so (or .dll) files that start with the + * prefix "gdal_X.so". It then tries to load them and then tries to call a + * function within them called GDALRegister_X() where the 'X' is the same as + * the remainder of the shared library basename ('X' is case sensitive), or + * failing that to call GDALRegisterMe(). + * + * There are a few rules for the driver path. If the GDAL_DRIVER_PATH + * environment variable it set, it is taken to be a list of directories to + * search separated by colons on UNIX, or semi-colons on Windows. Otherwise + * the /usr/local/lib/gdalplugins directory, and (if known) the + * lib/gdalplugins subdirectory of the gdal home directory are searched on + * UNIX and $(BINDIR)\gdalplugins on Windows. + * + * Auto loading can be completely disabled by setting the GDAL_DRIVER_PATH + * config option to "disable". + */ + +void GDALDriverManager::AutoLoadDrivers() + +{ + char **papszSearchPath = NULL; + const char *pszGDAL_DRIVER_PATH = + CPLGetConfigOption( "GDAL_DRIVER_PATH", NULL ); + if( pszGDAL_DRIVER_PATH == NULL ) + pszGDAL_DRIVER_PATH = CPLGetConfigOption( "OGR_DRIVER_PATH", NULL ); + +/* -------------------------------------------------------------------- */ +/* Allow applications to completely disable this search by */ +/* setting the driver path to the special string "disable". */ +/* -------------------------------------------------------------------- */ + if( pszGDAL_DRIVER_PATH != NULL && EQUAL(pszGDAL_DRIVER_PATH,"disable")) + { + CPLDebug( "GDAL", "GDALDriverManager::AutoLoadDrivers() disabled." ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Where should we look for stuff? */ +/* -------------------------------------------------------------------- */ + if( pszGDAL_DRIVER_PATH != NULL ) + { +#ifdef WIN32 + papszSearchPath = + CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ";", TRUE, FALSE ); +#else + papszSearchPath = + CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ":", TRUE, FALSE ); +#endif + } + else + { +#ifdef GDAL_PREFIX + papszSearchPath = CSLAddString( papszSearchPath, + #ifdef MACOSX_FRAMEWORK + GDAL_PREFIX "/PlugIns"); + #else + GDAL_PREFIX "/lib/gdalplugins" ); + #endif +#else + char szExecPath[1024]; + + if( CPLGetExecPath( szExecPath, sizeof(szExecPath) ) ) + { + char szPluginDir[sizeof(szExecPath)+50]; + strcpy( szPluginDir, CPLGetDirname( szExecPath ) ); + strcat( szPluginDir, "\\gdalplugins" ); + papszSearchPath = CSLAddString( papszSearchPath, szPluginDir ); + } + else + { + papszSearchPath = CSLAddString( papszSearchPath, + "/usr/local/lib/gdalplugins" ); + } +#endif + + #ifdef MACOSX_FRAMEWORK + #define num2str(x) str(x) + #define str(x) #x + papszSearchPath = CSLAddString( papszSearchPath, + "/Library/Application Support/GDAL/" + num2str(GDAL_VERSION_MAJOR) "." + num2str(GDAL_VERSION_MINOR) "/PlugIns" ); + #endif + + } + +/* -------------------------------------------------------------------- */ +/* Format the ABI version specific subdirectory to look in. */ +/* -------------------------------------------------------------------- */ + CPLString osABIVersion; + + osABIVersion.Printf( "%d.%d", GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR ); + +/* -------------------------------------------------------------------- */ +/* Scan each directory looking for files starting with gdal_ */ +/* -------------------------------------------------------------------- */ + for( int iDir = 0; iDir < CSLCount(papszSearchPath); iDir++ ) + { + char **papszFiles = NULL; + VSIStatBufL sStatBuf; + CPLString osABISpecificDir = + CPLFormFilename( papszSearchPath[iDir], osABIVersion, NULL ); + + if( VSIStatL( osABISpecificDir, &sStatBuf ) != 0 ) + osABISpecificDir = papszSearchPath[iDir]; + + papszFiles = CPLReadDir( osABISpecificDir ); + int nFileCount = CSLCount(papszFiles); + + for( int iFile = 0; iFile < nFileCount; iFile++ ) + { + char *pszFuncName; + const char *pszFilename; + const char *pszExtension = CPLGetExtension( papszFiles[iFile] ); + void *pRegister; + + if( !EQUAL(pszExtension,"dll") + && !EQUAL(pszExtension,"so") + && !EQUAL(pszExtension,"dylib") ) + continue; + + if( EQUALN(papszFiles[iFile],"gdal_",strlen("gdal_")) ) + { + pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1); + sprintf( pszFuncName, "GDALRegister_%s", + CPLGetBasename(papszFiles[iFile]) + strlen("gdal_") ); + } + else if ( EQUALN(papszFiles[iFile],"ogr_",strlen("ogr_")) ) + { + pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1); + sprintf( pszFuncName, "RegisterOGR%s", + CPLGetBasename(papszFiles[iFile]) + strlen("ogr_") ); + } + else + continue; + + pszFilename = + CPLFormFilename( osABISpecificDir, + papszFiles[iFile], NULL ); + + CPLErrorReset(); + CPLPushErrorHandler(CPLQuietErrorHandler); + pRegister = CPLGetSymbol( pszFilename, pszFuncName ); + CPLPopErrorHandler(); + if( pRegister == NULL ) + { + CPLString osLastErrorMsg(CPLGetLastErrorMsg()); + strcpy( pszFuncName, "GDALRegisterMe" ); + pRegister = CPLGetSymbol( pszFilename, pszFuncName ); + if( pRegister == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", osLastErrorMsg.c_str() ); + } + } + + if( pRegister != NULL ) + { + CPLDebug( "GDAL", "Auto register %s using %s.", + pszFilename, pszFuncName ); + + ((void (*)()) pRegister)(); + } + + CPLFree( pszFuncName ); + } + + CSLDestroy( papszFiles ); + } + + CSLDestroy( papszSearchPath ); +} + +/************************************************************************/ +/* GDALDestroyDriverManager() */ +/************************************************************************/ + +/** + * \brief Destroy the driver manager. + * + * Incidently unloads all managed drivers. + * + * NOTE: This function is not thread safe. It should not be called while + * other threads are actively using GDAL. + */ + +void CPL_STDCALL GDALDestroyDriverManager( void ) + +{ + // THREADSAFETY: We would like to lock the mutex here, but it + // needs to be reacquired within the destructor during driver + // deregistration. + if( poDM != NULL ) + delete poDM; +} + + diff --git a/ogr/gdalmajorobject.cpp b/ogr/gdalmajorobject.cpp new file mode 100644 index 0000000..a94e6d9 --- /dev/null +++ b/ogr/gdalmajorobject.cpp @@ -0,0 +1,424 @@ +/****************************************************************************** + * $Id: gdalmajorobject.cpp 27110 2014-03-28 21:29:20Z rouault $ + * + * Project: GDAL Core + * Purpose: Base class for objects with metadata, etc. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalmajorobject.cpp 27110 2014-03-28 21:29:20Z rouault $"); + +/************************************************************************/ +/* GDALMajorObject() */ +/************************************************************************/ + +GDALMajorObject::GDALMajorObject() + +{ + nFlags = GMO_VALID; +} + +/************************************************************************/ +/* ~GDALMajorObject() */ +/************************************************************************/ + +GDALMajorObject::~GDALMajorObject() + +{ + if( (nFlags & GMO_VALID) == 0 ) + CPLDebug( "GDAL", "In ~GDALMajorObject on invalid object" ); + + nFlags &= ~GMO_VALID; +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +/** + * \brief Fetch object description. + * + * The semantics of the returned description are specific to the derived + * type. For GDALDatasets it is the dataset name. For GDALRasterBands + * it is actually a description (if supported) or "". + * + * This method is the same as the C function GDALGetDescription(). + * + * @return non-null pointer to internal description string. + */ + +const char *GDALMajorObject::GetDescription() const + +{ + return sDescription.c_str(); +} + +/************************************************************************/ +/* GDALGetDescription() */ +/************************************************************************/ + +/** + * \brief Fetch object description. + * + * @see GDALMajorObject::GetDescription() + */ + +const char * CPL_STDCALL GDALGetDescription( GDALMajorObjectH hObject ) + +{ + VALIDATE_POINTER1( hObject, "GDALGetDescription", NULL ); + + return ((GDALMajorObject *) hObject)->GetDescription(); +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +/** + * \brief Set object description. + * + * The semantics of the description are specific to the derived + * type. For GDALDatasets it is the dataset name. For GDALRasterBands + * it is actually a description (if supported) or "". + * + * Normally application code should not set the "description" for + * GDALDatasets. It is handled internally. + * + * This method is the same as the C function GDALSetDescription(). + */ + +void GDALMajorObject::SetDescription( const char * pszNewDesc ) + +{ + sDescription = pszNewDesc; +} + +/************************************************************************/ +/* GDALSetDescription() */ +/************************************************************************/ + +/** + * \brief Set object description. + * + * @see GDALMajorObject::SetDescription() + */ + +void CPL_STDCALL GDALSetDescription( GDALMajorObjectH hObject, const char *pszNewDesc ) + +{ + VALIDATE_POINTER0( hObject, "GDALSetDescription" ); + + ((GDALMajorObject *) hObject)->SetDescription( pszNewDesc ); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +/** + * \brief Fetch list of metadata domains. + * + * The returned string list is the list of (non-empty) metadata domains. + * + * This method does the same thing as the C function GDALGetMetadataDomainList(). + * + * @return NULL or a string list. Must be freed with CSLDestroy() + * + * @since GDAL 1.11 + */ + +char **GDALMajorObject::GetMetadataDomainList() +{ + return CSLDuplicate(oMDMD.GetDomainList()); +} + +/************************************************************************/ +/* BuildMetadataDomainList() */ +/************************************************************************/ + +/** + * \brief Helper function for custom implementations of GetMetadataDomainList() + * + * + * @param papszList initial list of domains. May be NULL. Will become invalid + * after function call (use return value) + * @param bCheckNonEmpty if TRUE, each candidate domain will be tested to be non + * empty + * @param ... NULL terminated variadic list of candidate domains. + * + * @return NULL or a string list. Must be freed with CSLDestroy() + * + * @since GDAL 1.11 + */ + +char **GDALMajorObject::BuildMetadataDomainList(char** papszList, int bCheckNonEmpty, ...) +{ + va_list args; + const char* pszDomain; + va_start(args, bCheckNonEmpty); + + while( (pszDomain = va_arg(args, const char*)) != NULL ) + { + if( CSLFindString(papszList, pszDomain) < 0 && + (!bCheckNonEmpty || GetMetadata(pszDomain) != NULL) ) + { + papszList = CSLAddString(papszList, pszDomain); + } + } + + va_end(args); + + return papszList; +} + +/************************************************************************/ +/* GDALGetMetadataDomainList() */ +/************************************************************************/ + +/** + * \brief Fetch list of metadata domains. + * + * @see GDALMajorObject::GetMetadataDomainList() + * + * @since GDAL 1.11 + */ + +char ** CPL_STDCALL +GDALGetMetadataDomainList( GDALMajorObjectH hObject) + +{ + VALIDATE_POINTER1( hObject, "GetMetadataDomainList", NULL ); + + return ((GDALMajorObject *) hObject)->GetMetadataDomainList(); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +/** + * \brief Fetch metadata. + * + * The returned string list is owned by the object, and may change at + * any time. It is formated as a "Name=value" list with the last pointer + * value being NULL. Use the the CPL StringList functions such as + * CSLFetchNameValue() to manipulate it. + * + * Note that relatively few formats return any metadata at this time. + * + * This method does the same thing as the C function GDALGetMetadata(). + * + * @param pszDomain the domain of interest. Use "" or NULL for the default + * domain. + * + * @return NULL or a string list. + */ + +char **GDALMajorObject::GetMetadata( const char * pszDomain ) + +{ + return oMDMD.GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GDALGetMetadata() */ +/************************************************************************/ + +/** + * \brief Fetch metadata. + * + * @see GDALMajorObject::GetMetadata() + */ + +char ** CPL_STDCALL +GDALGetMetadata( GDALMajorObjectH hObject, const char * pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALGetMetadata", NULL ); + + return ((GDALMajorObject *) hObject)->GetMetadata(pszDomain); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +/** + * \brief Set metadata. + * + * The C function GDALSetMetadata() does the same thing as this method. + * + * @param papszMetadataIn the metadata in name=value string list format to + * apply. + * @param pszDomain the domain of interest. Use "" or NULL for the default + * domain. + * @return CE_None on success, CE_Failure on failure and CE_Warning if the + * metadata has been accepted, but is likely not maintained persistently + * by the underlying object between sessions. + */ + +CPLErr GDALMajorObject::SetMetadata( char ** papszMetadataIn, + const char * pszDomain ) + +{ + nFlags |= GMO_MD_DIRTY; + return oMDMD.SetMetadata( papszMetadataIn, pszDomain ); +} + +/************************************************************************/ +/* GDALSetMetadata() */ +/************************************************************************/ + +/** + * \brief Set metadata. + * + * @see GDALMajorObject::SetMetadata() + */ + +CPLErr CPL_STDCALL +GDALSetMetadata( GDALMajorObjectH hObject, char **papszMD, + const char *pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALSetMetadata", CE_Failure ); + + return ((GDALMajorObject *) hObject)->SetMetadata( papszMD, pszDomain ); +} + + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Fetch single metadata item. + * + * The C function GDALGetMetadataItem() does the same thing as this method. + * + * @param pszName the key for the metadata item to fetch. + * @param pszDomain the domain to fetch for, use NULL for the default domain. + * + * @return NULL on failure to find the key, or a pointer to an internal + * copy of the value string on success. + */ + +const char *GDALMajorObject::GetMetadataItem( const char * pszName, + const char * pszDomain ) + +{ + return oMDMD.GetMetadataItem( pszName, pszDomain ); +} + +/************************************************************************/ +/* GDALGetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Fetch single metadata item. + * + * @see GDALMajorObject::GetMetadataItem() + */ + +const char * CPL_STDCALL GDALGetMetadataItem( GDALMajorObjectH hObject, + const char *pszName, + const char *pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALGetMetadataItem", NULL ); + + return ((GDALMajorObject *) hObject)->GetMetadataItem( pszName, pszDomain); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Set single metadata item. + * + * The C function GDALSetMetadataItem() does the same thing as this method. + * + * @param pszName the key for the metadata item to fetch. + * @param pszValue the value to assign to the key. + * @param pszDomain the domain to set within, use NULL for the default domain. + * + * @return CE_None on success, or an error code on failure. + */ + +CPLErr GDALMajorObject::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) + +{ + nFlags |= GMO_MD_DIRTY; + return oMDMD.SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GDALSetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Set single metadata item. + * + * @see GDALMajorObject::SetMetadataItem() + */ + +CPLErr CPL_STDCALL +GDALSetMetadataItem( GDALMajorObjectH hObject, + const char *pszName, const char *pszValue, + const char *pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALSetMetadataItem", CE_Failure ); + + return ((GDALMajorObject *) hObject)->SetMetadataItem( pszName, pszValue, + pszDomain ); +} + +/************************************************************************/ +/* GetMOFlags() */ +/************************************************************************/ + +int GDALMajorObject::GetMOFlags() + +{ + return nFlags; +} + +/************************************************************************/ +/* SetMOFlags() */ +/************************************************************************/ + +void GDALMajorObject::SetMOFlags( int nNewFlags ) + +{ + nFlags = nNewFlags; +} + diff --git a/ogr/gdalmultidomainmetadata.cpp b/ogr/gdalmultidomainmetadata.cpp new file mode 100644 index 0000000..46ec82f --- /dev/null +++ b/ogr/gdalmultidomainmetadata.cpp @@ -0,0 +1,349 @@ +/****************************************************************************** + * $Id: gdalmultidomainmetadata.cpp 27448 2014-06-09 22:11:42Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALMultiDomainMetadata class. This class + * manages metadata items for a variable list of domains. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: gdalmultidomainmetadata.cpp 27448 2014-06-09 22:11:42Z rouault $"); + +/************************************************************************/ +/* GDALMultiDomainMetadata() */ +/************************************************************************/ + +GDALMultiDomainMetadata::GDALMultiDomainMetadata() + +{ + papszDomainList = NULL; + papoMetadataLists = NULL; +} + +/************************************************************************/ +/* ~GDALMultiDomainMetadata() */ +/************************************************************************/ + +GDALMultiDomainMetadata::~GDALMultiDomainMetadata() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +void GDALMultiDomainMetadata::Clear() + +{ + int i, nDomainCount; + + nDomainCount = CSLCount( papszDomainList ); + CSLDestroy( papszDomainList ); + papszDomainList = NULL; + + for( i = 0; i < nDomainCount; i++ ) + { + delete papoMetadataLists[i]; + } + CPLFree( papoMetadataLists ); + papoMetadataLists = NULL; +} + + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALMultiDomainMetadata::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + return NULL; + else + return papoMetadataLists[iDomain]->List(); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALMultiDomainMetadata::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + { + int nDomainCount; + + papszDomainList = CSLAddString( papszDomainList, pszDomain ); + nDomainCount = CSLCount( papszDomainList ); + + papoMetadataLists = (CPLStringList **) + CPLRealloc( papoMetadataLists, sizeof(void*)*(nDomainCount+1) ); + papoMetadataLists[nDomainCount] = NULL; + papoMetadataLists[nDomainCount-1] = new CPLStringList(); + iDomain = nDomainCount-1; + } + + papoMetadataLists[iDomain]->Assign( CSLDuplicate( papszMetadata ) ); + + // we want to mark name/value pair domains as being sorted for fast + // access. + if( !EQUALN(pszDomain,"xml:",4) && !EQUAL(pszDomain, "SUBDATASETS") ) + papoMetadataLists[iDomain]->Sort(); + + return CE_None; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALMultiDomainMetadata::GetMetadataItem( const char *pszName, + const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + return NULL; + else + return papoMetadataLists[iDomain]->FetchNameValue( pszName ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALMultiDomainMetadata::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + +/* -------------------------------------------------------------------- */ +/* Create the domain if it does not already exist. */ +/* -------------------------------------------------------------------- */ + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + { + SetMetadata( NULL, pszDomain ); + iDomain = CSLFindString( papszDomainList, pszDomain ); + } + +/* -------------------------------------------------------------------- */ +/* Set the value in the domain list. */ +/* -------------------------------------------------------------------- */ + papoMetadataLists[iDomain]->SetNameValue( pszName, pszValue ); + + return CE_None; +} + +/************************************************************************/ +/* XMLInit() */ +/* */ +/* This method should be invoked on the parent of the */ +/* elements. */ +/************************************************************************/ + +int GDALMultiDomainMetadata::XMLInit( CPLXMLNode *psTree, int bMerge ) + +{ + CPLXMLNode *psMetadata; + +/* ==================================================================== */ +/* Process all elements, each for one domain. */ +/* ==================================================================== */ + for( psMetadata = psTree->psChild; + psMetadata != NULL; psMetadata = psMetadata->psNext ) + { + CPLXMLNode *psMDI; + const char *pszDomain, *pszFormat; + + if( psMetadata->eType != CXT_Element + || !EQUAL(psMetadata->pszValue,"Metadata") ) + continue; + + pszDomain = CPLGetXMLValue( psMetadata, "domain", "" ); + pszFormat = CPLGetXMLValue( psMetadata, "format", "" ); + + // Make sure we have a CPLStringList for this domain, + // without wiping out an existing one. + if( GetMetadata( pszDomain ) == NULL ) + SetMetadata( NULL, pszDomain ); + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + CPLAssert( iDomain != -1 ); + + CPLStringList *poMDList = papoMetadataLists[iDomain]; + +/* -------------------------------------------------------------------- */ +/* XML format subdocuments. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszFormat,"xml") ) + { + CPLXMLNode *psSubDoc; + + /* find first non-attribute child of current element */ + psSubDoc = psMetadata->psChild; + while( psSubDoc != NULL && psSubDoc->eType == CXT_Attribute ) + psSubDoc = psSubDoc->psNext; + + char *pszDoc = CPLSerializeXMLTree( psSubDoc ); + + poMDList->Clear(); + poMDList->AddStringDirectly( pszDoc ); + } + +/* -------------------------------------------------------------------- */ +/* Name value format. */ +/* value_Text */ +/* -------------------------------------------------------------------- */ + else + { + for( psMDI = psMetadata->psChild; psMDI != NULL; + psMDI = psMDI->psNext ) + { + if( !EQUAL(psMDI->pszValue,"MDI") + || psMDI->eType != CXT_Element + || psMDI->psChild == NULL + || psMDI->psChild->psNext == NULL + || psMDI->psChild->eType != CXT_Attribute + || psMDI->psChild->psChild == NULL ) + continue; + + char* pszName = psMDI->psChild->psChild->pszValue; + char* pszValue = psMDI->psChild->psNext->pszValue; + if( pszName != NULL && pszValue != NULL ) + poMDList->SetNameValue( pszName, pszValue ); + } + } + } + + return CSLCount(papszDomainList) != 0; +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +CPLXMLNode *GDALMultiDomainMetadata::Serialize() + +{ + CPLXMLNode *psFirst = NULL; + + for( int iDomain = 0; + papszDomainList != NULL && papszDomainList[iDomain] != NULL; + iDomain++) + { + char **papszMD = papoMetadataLists[iDomain]->List(); + CPLXMLNode *psMD; + int bFormatXML = FALSE; + + psMD = CPLCreateXMLNode( NULL, CXT_Element, "Metadata" ); + + if( strlen( papszDomainList[iDomain] ) > 0 ) + CPLCreateXMLNode( + CPLCreateXMLNode( psMD, CXT_Attribute, "domain" ), + CXT_Text, papszDomainList[iDomain] ); + + if( EQUALN(papszDomainList[iDomain],"xml:",4) + && CSLCount(papszMD) == 1 ) + { + CPLXMLNode *psValueAsXML = CPLParseXMLString( papszMD[0] ); + if( psValueAsXML != NULL ) + { + bFormatXML = TRUE; + + CPLCreateXMLNode( + CPLCreateXMLNode( psMD, CXT_Attribute, "format" ), + CXT_Text, "xml" ); + + CPLAddXMLChild( psMD, psValueAsXML ); + } + } + + if( !bFormatXML ) + { + CPLXMLNode* psLastChild = NULL; + if( psMD->psChild != NULL ) + { + psLastChild = psMD->psChild; + while( psLastChild->psNext != NULL ) + psLastChild = psLastChild->psNext; + } + for( int i = 0; papszMD != NULL && papszMD[i] != NULL; i++ ) + { + const char *pszRawValue; + char *pszKey = NULL; + CPLXMLNode *psMDI; + + pszRawValue = CPLParseNameValue( papszMD[i], &pszKey ); + + psMDI = CPLCreateXMLNode( NULL, CXT_Element, "MDI" ); + if( psLastChild == NULL ) + psMD->psChild = psMDI; + else + psLastChild->psNext = psMDI; + psLastChild = psMDI; + + CPLSetXMLValue( psMDI, "#key", pszKey ); + CPLCreateXMLNode( psMDI, CXT_Text, pszRawValue ); + + CPLFree( pszKey ); + } + } + + if( psFirst == NULL ) + psFirst = psMD; + else + CPLAddXMLSibling( psFirst, psMD ); + } + + return psFirst; +} + diff --git a/ogr/gdalnodatamaskband.cpp b/ogr/gdalnodatamaskband.cpp new file mode 100644 index 0000000..88c3c3f --- /dev/null +++ b/ogr/gdalnodatamaskband.cpp @@ -0,0 +1,286 @@ +/****************************************************************************** + * $Id: gdalnodatamaskband.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALNoDataMaskBand, a class implementing all + * a default band mask based on nodata values. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalnodatamaskband.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* GDALNoDataMaskBand() */ +/************************************************************************/ + +GDALNoDataMaskBand::GDALNoDataMaskBand( GDALRasterBand *poParent ) + +{ + poDS = NULL; + nBand = 0; + + nRasterXSize = poParent->GetXSize(); + nRasterYSize = poParent->GetYSize(); + + eDataType = GDT_Byte; + poParent->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + this->poParent = poParent; + dfNoDataValue = poParent->GetNoDataValue(); +} + +/************************************************************************/ +/* ~GDALNoDataMaskBand() */ +/************************************************************************/ + +GDALNoDataMaskBand::~GDALNoDataMaskBand() + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALNoDataMaskBand::IReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ + GDALDataType eWrkDT; + +/* -------------------------------------------------------------------- */ +/* Decide on a working type. */ +/* -------------------------------------------------------------------- */ + switch( poParent->GetRasterDataType() ) + { + case GDT_Byte: + eWrkDT = GDT_Byte; + break; + + case GDT_UInt16: + case GDT_UInt32: + eWrkDT = GDT_UInt32; + break; + + case GDT_Int16: + case GDT_Int32: + case GDT_CInt16: + case GDT_CInt32: + eWrkDT = GDT_Int32; + break; + + case GDT_Float32: + case GDT_CFloat32: + eWrkDT = GDT_Float32; + break; + + case GDT_Float64: + case GDT_CFloat64: + eWrkDT = GDT_Float64; + break; + + default: + CPLAssert( FALSE ); + eWrkDT = GDT_Float64; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabySrc; + CPLErr eErr; + + pabySrc = (GByte *) VSIMalloc3( GDALGetDataTypeSize(eWrkDT)/8, nBlockXSize, nBlockYSize ); + if (pabySrc == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALNoDataMaskBand::IReadBlock: Out of memory for buffer." ); + return CE_Failure; + } + + + int nXSizeRequest = nBlockXSize; + if (nXBlockOff * nBlockXSize + nBlockXSize > nRasterXSize) + nXSizeRequest = nRasterXSize - nXBlockOff * nBlockXSize; + int nYSizeRequest = nBlockYSize; + if (nYBlockOff * nBlockYSize + nBlockYSize > nRasterYSize) + nYSizeRequest = nRasterYSize - nYBlockOff * nBlockYSize; + + if (nXSizeRequest != nBlockXSize || nYSizeRequest != nBlockYSize) + { + /* memset the whole buffer to avoid Valgrind warnings in case we can't */ + /* fetch a full block */ + memset(pabySrc, 0, GDALGetDataTypeSize(eWrkDT)/8 * nBlockXSize * nBlockYSize ); + } + + eErr = poParent->RasterIO( GF_Read, + nXBlockOff * nBlockXSize, nYBlockOff * nBlockYSize, + nXSizeRequest, nYSizeRequest, + pabySrc, nXSizeRequest, nYSizeRequest, + eWrkDT, 0, nBlockXSize * (GDALGetDataTypeSize(eWrkDT)/8) ); + if( eErr != CE_None ) + { + CPLFree(pabySrc); + return eErr; + } + + int bIsNoDataNan = CPLIsNan(dfNoDataValue); + +/* -------------------------------------------------------------------- */ +/* Process different cases. */ +/* -------------------------------------------------------------------- */ + int i; + switch( eWrkDT ) + { + case GDT_Byte: + { + GByte byNoData = (GByte) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + if( pabySrc[i] == byNoData ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_UInt32: + { + GUInt32 nNoData = (GUInt32) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + if( ((GUInt32 *)pabySrc)[i] == nNoData ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_Int32: + { + GInt32 nNoData = (GInt32) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + if( ((GInt32 *)pabySrc)[i] == nNoData ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_Float32: + { + float fNoData = (float) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + float fVal =((float *)pabySrc)[i]; + if( bIsNoDataNan && CPLIsNan(fVal)) + ((GByte *) pImage)[i] = 0; + else if( ARE_REAL_EQUAL(fVal, fNoData) ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_Float64: + { + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + double dfVal =((double *)pabySrc)[i]; + if( bIsNoDataNan && CPLIsNan(dfVal)) + ((GByte *) pImage)[i] = 0; + else if( ARE_REAL_EQUAL(dfVal, dfNoDataValue) ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + default: + CPLAssert( FALSE ); + break; + } + + CPLFree( pabySrc ); + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GDALNoDataMaskBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ) +{ + /* Optimization in common use case (#4488) */ + /* This avoids triggering the block cache on this band, which helps */ + /* reducing the global block cache consumption */ + if (eRWFlag == GF_Read && eBufType == GDT_Byte && + poParent->GetRasterDataType() == GDT_Byte && + nXSize == nBufXSize && nYSize == nBufYSize && + nPixelSpace == 1 && nLineSpace == nBufXSize) + { + CPLErr eErr = poParent->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace ); + if (eErr != CE_None) + return eErr; + + GByte* pabyData = (GByte*) pData; + GByte byNoData = (GByte) dfNoDataValue; + + for( int i = nBufXSize * nBufYSize - 1; i >= 0; i-- ) + { + if( pabyData[i] == byNoData ) + pabyData[i] = 0; + else + pabyData[i] = 255; + } + return CE_None; + } + + return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace ); +} diff --git a/ogr/gdalnodatavaluesmaskband.cpp b/ogr/gdalnodatavaluesmaskband.cpp new file mode 100644 index 0000000..4bf5fe1 --- /dev/null +++ b/ogr/gdalnodatavaluesmaskband.cpp @@ -0,0 +1,311 @@ +/****************************************************************************** + * $Id: gdalnodatavaluesmaskband.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALNoDataValuesMaskBand, a class implementing + * a default band mask based on the NODATA_VALUES metadata item. + * A pixel is considered nodata in all bands if and only if all bands + * match the corresponding value in the NODATA_VALUES tuple + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalnodatavaluesmaskband.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* GDALNoDataValuesMaskBand() */ +/************************************************************************/ + +GDALNoDataValuesMaskBand::GDALNoDataValuesMaskBand( GDALDataset* poDS ) + +{ + const char* pszNoDataValues = poDS->GetMetadataItem("NODATA_VALUES"); + char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE); + + int i; + padfNodataValues = (double*)CPLMalloc(sizeof(double) * poDS->GetRasterCount()); + for(i=0;iGetRasterCount();i++) + { + padfNodataValues[i] = atof(papszNoDataValues[i]); + } + + CSLDestroy(papszNoDataValues); + + this->poDS = poDS; + nBand = 0; + + nRasterXSize = poDS->GetRasterXSize(); + nRasterYSize = poDS->GetRasterYSize(); + + eDataType = GDT_Byte; + poDS->GetRasterBand(1)->GetBlockSize( &nBlockXSize, &nBlockYSize ); +} + +/************************************************************************/ +/* ~GDALNoDataValuesMaskBand() */ +/************************************************************************/ + +GDALNoDataValuesMaskBand::~GDALNoDataValuesMaskBand() + +{ + CPLFree(padfNodataValues); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALNoDataValuesMaskBand::IReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ + int iBand; + GDALDataType eWrkDT; + +/* -------------------------------------------------------------------- */ +/* Decide on a working type. */ +/* -------------------------------------------------------------------- */ + switch( poDS->GetRasterBand(1)->GetRasterDataType() ) + { + case GDT_Byte: + eWrkDT = GDT_Byte; + break; + + case GDT_UInt16: + case GDT_UInt32: + eWrkDT = GDT_UInt32; + break; + + case GDT_Int16: + case GDT_Int32: + case GDT_CInt16: + case GDT_CInt32: + eWrkDT = GDT_Int32; + break; + + case GDT_Float32: + case GDT_CFloat32: + eWrkDT = GDT_Float32; + break; + + case GDT_Float64: + case GDT_CFloat64: + eWrkDT = GDT_Float64; + break; + + default: + CPLAssert( FALSE ); + eWrkDT = GDT_Float64; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabySrc; + CPLErr eErr; + + int nBands = poDS->GetRasterCount(); + pabySrc = (GByte *) VSIMalloc3( nBands * GDALGetDataTypeSize(eWrkDT)/8, nBlockXSize, nBlockYSize ); + if (pabySrc == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALNoDataValuesMaskBand::IReadBlock: Out of memory for buffer." ); + return CE_Failure; + } + + int nXSizeRequest = nBlockXSize; + if (nXBlockOff * nBlockXSize + nBlockXSize > nRasterXSize) + nXSizeRequest = nRasterXSize - nXBlockOff * nBlockXSize; + int nYSizeRequest = nBlockYSize; + if (nYBlockOff * nBlockYSize + nBlockYSize > nRasterYSize) + nYSizeRequest = nRasterYSize - nYBlockOff * nBlockYSize; + + if (nXSizeRequest != nBlockXSize || nYSizeRequest != nBlockYSize) + { + /* memset the whole buffer to avoid Valgrind warnings in case we can't */ + /* fetch a full block */ + memset(pabySrc, 0, nBands * GDALGetDataTypeSize(eWrkDT)/8 * nBlockXSize * nBlockYSize ); + } + + int nBlockOffsetPixels = nBlockXSize * nBlockYSize; + int nBandOffsetByte = (GDALGetDataTypeSize(eWrkDT)/8) * nBlockXSize * nBlockYSize; + for(iBand=0;iBandGetRasterBand(iBand + 1)->RasterIO( + GF_Read, + nXBlockOff * nBlockXSize, nYBlockOff * nBlockYSize, + nXSizeRequest, nYSizeRequest, + pabySrc + iBand * nBandOffsetByte, nXSizeRequest, nYSizeRequest, + eWrkDT, 0, nBlockXSize * (GDALGetDataTypeSize(eWrkDT)/8) ); + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Process different cases. */ +/* -------------------------------------------------------------------- */ + int i; + switch( eWrkDT ) + { + case GDT_Byte: + { + GByte* pabyNoData = (GByte*) CPLMalloc(nBands * sizeof(GByte)); + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_conv.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +CPL_CVSID("$Id: gdalopeninfo.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* GDALOpenInfo */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALOpenInfo() */ +/************************************************************************/ + +GDALOpenInfo::GDALOpenInfo( const char * pszFilenameIn, int nOpenFlagsIn, + char **papszSiblingsIn ) + +{ +/* -------------------------------------------------------------------- */ +/* Ensure that C: is treated as C:\ so we can stat it on */ +/* Windows. Similar to what is done in CPLStat(). */ +/* -------------------------------------------------------------------- */ +#ifdef WIN32 + if( strlen(pszFilenameIn) == 2 && pszFilenameIn[1] == ':' ) + { + char szAltPath[10]; + + strcpy( szAltPath, pszFilenameIn ); + strcat( szAltPath, "\\" ); + pszFilename = CPLStrdup( szAltPath ); + } + else +#endif + pszFilename = CPLStrdup( pszFilenameIn ); + +/* -------------------------------------------------------------------- */ +/* Initialize. */ +/* -------------------------------------------------------------------- */ + + nHeaderBytes = 0; + nHeaderBytesTried = 0; + pabyHeader = NULL; + bIsDirectory = FALSE; + bStatOK = FALSE; + nOpenFlags = nOpenFlagsIn; + eAccess = (nOpenFlags & GDAL_OF_UPDATE) ? GA_Update : GA_ReadOnly; + fpL = NULL; + papszOpenOptions = NULL; + +#ifdef HAVE_READLINK + int bHasRetried = FALSE; +#endif + +/* -------------------------------------------------------------------- */ +/* Collect information about the file. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStat; + +#ifdef HAVE_READLINK +retry: +#endif + int bPotentialDirectory = FALSE; + + /* Check if the filename might be a directory of a special virtual file system */ + if( strncmp(pszFilename, "/vsizip/", strlen("/vsizip/")) == 0 || + strncmp(pszFilename, "/vsitar/", strlen("/vsitar/")) == 0 ) + { + const char* pszExt = CPLGetExtension(pszFilename); + if( EQUAL(pszExt, "zip") || EQUAL(pszExt, "tar") || EQUAL(pszExt, "gz") ) + bPotentialDirectory = TRUE; + } + else if( strncmp(pszFilename, "/vsicurl/", strlen("/vsicurl/")) == 0 ) + { + bPotentialDirectory = TRUE; + } + + if( bPotentialDirectory ) + { + /* For those special files, opening them with VSIFOpenL() might result */ + /* in content, even if they should be considered as directories, so */ + /* use stat */ + if( VSIStatExL( pszFilename, &sStat, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 ) + { + bStatOK = TRUE; + if( VSI_ISDIR( sStat.st_mode ) ) + bIsDirectory = TRUE; + } + } + + if( !bIsDirectory ) + fpL = VSIFOpenL( pszFilename, (eAccess == GA_Update) ? "r+b" : "rb" ); + if( fpL != NULL ) + { + bStatOK = TRUE; + pabyHeader = (GByte *) CPLCalloc(1025,1); + nHeaderBytesTried = 1024; + nHeaderBytes = (int) VSIFReadL( pabyHeader, 1, nHeaderBytesTried, fpL ); + VSIRewindL( fpL ); + + /* If we cannot read anything, check if it is not a directory instead */ + if( nHeaderBytes == 0 && + VSIStatExL( pszFilename, &sStat, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 && + VSI_ISDIR( sStat.st_mode ) ) + { + VSIFCloseL(fpL); + fpL = NULL; + CPLFree(pabyHeader); + pabyHeader = NULL; + bIsDirectory = TRUE; + } + } + else if( !bStatOK ) + { + if( VSIStatExL( pszFilename, &sStat, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 ) + { + bStatOK = TRUE; + if( VSI_ISDIR( sStat.st_mode ) ) + bIsDirectory = TRUE; + } +#ifdef HAVE_READLINK + else if (!bHasRetried) + { + /* If someone creates a file with "ln -sf /vsicurl/http://download.osgeo.org/gdal/data/gtiff/utm.tif my_remote_utm.tif" */ + /* we will be able to open it by passing my_remote_utm.tif */ + /* This helps a lot for GDAL based readers that only provide file explorers to open datasets */ + char szPointerFilename[2048]; + int nBytes = readlink(pszFilename, szPointerFilename, sizeof(szPointerFilename)); + if (nBytes != -1) + { + szPointerFilename[MIN(nBytes, (int)sizeof(szPointerFilename)-1)] = 0; + CPLFree(pszFilename); + pszFilename = CPLStrdup(szPointerFilename); + papszSiblingsIn = NULL; + bHasRetried = TRUE; + goto retry; + } + } +#endif + } + +/* -------------------------------------------------------------------- */ +/* Capture sibling list either from passed in values, or by */ +/* scanning for them only if requested through GetSiblingFiles(). */ +/* -------------------------------------------------------------------- */ + if( papszSiblingsIn != NULL ) + { + papszSiblingFiles = CSLDuplicate( papszSiblingsIn ); + bHasGotSiblingFiles = TRUE; + } + else if( bStatOK && !bIsDirectory ) + { + const char* pszOptionVal = + CPLGetConfigOption( "GDAL_DISABLE_READDIR_ON_OPEN", "NO" ); + if (EQUAL(pszOptionVal, "EMPTY_DIR")) + { + papszSiblingFiles = CSLAddString( NULL, CPLGetFilename(pszFilename) ); + bHasGotSiblingFiles = TRUE; + } + else if( CSLTestBoolean(pszOptionVal) ) + { + /* skip reading the directory */ + papszSiblingFiles = NULL; + bHasGotSiblingFiles = TRUE; + } + else + { + /* will be lazy loaded */ + papszSiblingFiles = NULL; + bHasGotSiblingFiles = FALSE; + } + } + else + { + papszSiblingFiles = NULL; + bHasGotSiblingFiles = TRUE; + } +} + +/************************************************************************/ +/* ~GDALOpenInfo() */ +/************************************************************************/ + +GDALOpenInfo::~GDALOpenInfo() + +{ + VSIFree( pabyHeader ); + CPLFree( pszFilename ); + + if( fpL != NULL ) + VSIFCloseL( fpL ); + CSLDestroy( papszSiblingFiles ); +} + +/************************************************************************/ +/* GetSiblingFiles() */ +/************************************************************************/ + +char** GDALOpenInfo::GetSiblingFiles() +{ + if( bHasGotSiblingFiles ) + return papszSiblingFiles; + bHasGotSiblingFiles = TRUE; + + CPLString osDir = CPLGetDirname( pszFilename ); + papszSiblingFiles = VSIReadDir( osDir ); + + /* Small optimization to avoid unnecessary stat'ing from PAux or ENVI */ + /* drivers. The MBTiles driver needs no companion file. */ + if( papszSiblingFiles == NULL && + strncmp(pszFilename, "/vsicurl/", 9) == 0 && + EQUAL(CPLGetExtension( pszFilename ),"mbtiles") ) + { + papszSiblingFiles = CSLAddString( NULL, CPLGetFilename(pszFilename) ); + } + + return papszSiblingFiles; +} + + +/************************************************************************/ +/* TryToIngest() */ +/************************************************************************/ + +int GDALOpenInfo::TryToIngest(int nBytes) +{ + if( fpL == NULL ) + return FALSE; + if( nHeaderBytes < nHeaderBytesTried ) + return TRUE; + pabyHeader = (GByte*) CPLRealloc(pabyHeader, nBytes + 1); + memset(pabyHeader, 0, nBytes + 1); + VSIRewindL(fpL); + nHeaderBytesTried = nBytes; + nHeaderBytes = (int) VSIFReadL(pabyHeader, 1, nBytes, fpL); + VSIRewindL(fpL); + + return TRUE; +} diff --git a/ogr/gdalpamdataset.cpp b/ogr/gdalpamdataset.cpp new file mode 100644 index 0000000..df3c4fb --- /dev/null +++ b/ogr/gdalpamdataset.cpp @@ -0,0 +1,1480 @@ +/****************************************************************************** + * $Id: gdalpamdataset.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALPamDataset, a dataset base class that + * knows how to persist auxilary metadata into a support XML file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: gdalpamdataset.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* GDALPamDataset() */ +/************************************************************************/ + +/** + * \class GDALPamDataset "gdal_pam.h" + * + * A subclass of GDALDataset which introduces the ability to save and + * restore auxilary information (coordinate system, gcps, metadata, + * etc) not supported by a file format via an "auxilary metadata" file + * with the .aux.xml extension. + * + *

    Enabling PAM

    + * + * PAM support can be enabled (resp. disabled) in GDAL by setting the GDAL_PAM_ENABLED + * configuration option (via CPLSetConfigOption(), or the environment) to + * the value of YES (resp. NO). Note: The default value is build dependant and defaults + * to YES in Windows and Unix builds. + * + *

    PAM Proxy Files

    + * + * In order to be able to record auxilary information about files on + * read-only media such as CDROMs or in directories where the user does not + * have write permissions, it is possible to enable the "PAM Proxy Database". + * When enabled the .aux.xml files are kept in a different directory, writable + * by the user. Overviews will also be stored in the PAM proxy directory. + * + * To enable this, set the GDAL_PAM_PROXY_DIR configuration option to be + * the name of the directory where the proxies should be kept. The configuration + * option must be set *before* the first access to PAM, because its value is cached + * for later access. + * + *

    Adding PAM to Drivers

    + * + * Drivers for physical file formats that wish to support persistent auxilary + * metadata in addition to that for the format itself should derive their + * dataset class from GDALPamDataset instead of directly from GDALDataset. + * The raster band classes should also be derived from GDALPamRasterBand. + * + * They should also call something like this near the end of the Open() + * method: + * + * \code + * poDS->SetDescription( poOpenInfo->pszFilename ); + * poDS->TryLoadXML(); + * \endcode + * + * The SetDescription() is necessary so that the dataset will have a valid + * filename set as the description before TryLoadXML() is called. TryLoadXML() + * will look for an .aux.xml file with the same basename as the dataset and + * in the same directory. If found the contents will be loaded and kept + * track of in the GDALPamDataset and GDALPamRasterBand objects. When a + * call like GetProjectionRef() is not implemented by the format specific + * class, it will fall through to the PAM implementation which will return + * information if it was in the .aux.xml file. + * + * Drivers should also try to call the GDALPamDataset/GDALPamRasterBand + * methods as a fallback if their implementation does not find information. + * This allows using the .aux.xml for variations that can't be stored in + * the format. For instance, the GeoTIFF driver GetProjectionRef() looks + * like this: + * + * \code + * if( EQUAL(pszProjection,"") ) + * return GDALPamDataset::GetProjectionRef(); + * else + * return( pszProjection ); + * \endcode + * + * So if the geotiff header is missing, the .aux.xml file will be + * consulted. + * + * Similarly, if SetProjection() were called with a coordinate system + * not supported by GeoTIFF, the SetProjection() method should pass it on + * to the GDALPamDataset::SetProjection() method after issuing a warning + * that the information can't be represented within the file itself. + * + * Drivers for subdataset based formats will also need to declare the + * name of the physical file they are related to, and the name of their + * subdataset before calling TryLoadXML(). + * + * \code + * poDS->SetDescription( poOpenInfo->pszFilename ); + * poDS->SetPhysicalFilename( poDS->pszFilename ); + * poDS->SetSubdatasetName( osSubdatasetName ); + * + * poDS->TryLoadXML(); + * \endcode + */ + +GDALPamDataset::GDALPamDataset() + +{ + nPamFlags = 0; + psPam = NULL; + SetMOFlags( GetMOFlags() | GMO_PAM_CLASS ); +} + +/************************************************************************/ +/* ~GDALPamDataset() */ +/************************************************************************/ + +GDALPamDataset::~GDALPamDataset() + +{ + if( nPamFlags & GPF_DIRTY ) + { + CPLDebug( "GDALPamDataset", "In destructor with dirty metadata." ); + FlushCache(); + } + + PamClear(); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void GDALPamDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + if( nPamFlags & GPF_DIRTY ) + TrySaveXML(); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *GDALPamDataset::SerializeToXML( const char *pszUnused ) + +{ + CPLString oFmt; + + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup root node and attributes. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDSTree; + + psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" ); + +/* -------------------------------------------------------------------- */ +/* SRS */ +/* -------------------------------------------------------------------- */ + if( psPam->pszProjection != NULL && strlen(psPam->pszProjection) > 0 ) + CPLSetXMLValue( psDSTree, "SRS", psPam->pszProjection ); + +/* -------------------------------------------------------------------- */ +/* GeoTransform. */ +/* -------------------------------------------------------------------- */ + if( psPam->bHaveGeoTransform ) + { + CPLSetXMLValue( psDSTree, "GeoTransform", + oFmt.Printf( "%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e", + psPam->adfGeoTransform[0], + psPam->adfGeoTransform[1], + psPam->adfGeoTransform[2], + psPam->adfGeoTransform[3], + psPam->adfGeoTransform[4], + psPam->adfGeoTransform[5] ) ); + } + +/* -------------------------------------------------------------------- */ +/* Metadata. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psMD; + + psMD = oMDMD.Serialize(); + if( psMD != NULL ) + { + if( psMD->psChild == NULL && psMD->psNext == NULL ) + CPLDestroyXMLNode( psMD ); + else + CPLAddXMLChild( psDSTree, psMD ); + } + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( psPam->nGCPCount > 0 ) + { + GDALSerializeGCPListToXML( psDSTree, + psPam->pasGCPList, + psPam->nGCPCount, + psPam->pszGCPProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + CPLXMLNode *psBandTree; + + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + psBandTree = poBand->SerializeToXML( pszUnused ); + + if( psBandTree != NULL ) + CPLAddXMLChild( psDSTree, psBandTree ); + } + +/* -------------------------------------------------------------------- */ +/* We don't want to return anything if we had no metadata to */ +/* attach. */ +/* -------------------------------------------------------------------- */ + if( psDSTree->psChild == NULL ) + { + CPLDestroyXMLNode( psDSTree ); + psDSTree = NULL; + } + + return psDSTree; +} + +/************************************************************************/ +/* PamInitialize() */ +/************************************************************************/ + +void GDALPamDataset::PamInitialize() + +{ +#ifdef PAM_ENABLED + static const char *pszPamDefault = "YES"; +#else + static const char *pszPamDefault = "NO"; +#endif + + if( psPam || (nPamFlags & GPF_DISABLED) ) + return; + + if( !CSLTestBoolean( CPLGetConfigOption( "GDAL_PAM_ENABLED", + pszPamDefault ) ) ) + { + nPamFlags |= GPF_DISABLED; + return; + } + + /* ERO 2011/04/13 : GPF_AUXMODE seems to be unimplemented */ + if( EQUAL( CPLGetConfigOption( "GDAL_PAM_MODE", "PAM" ), "AUX") ) + nPamFlags |= GPF_AUXMODE; + + psPam = new GDALDatasetPamInfo; + psPam->pszPamFilename = NULL; + psPam->pszProjection = NULL; + psPam->bHaveGeoTransform = FALSE; + psPam->nGCPCount = 0; + psPam->pasGCPList = NULL; + psPam->pszGCPProjection = NULL; + + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + poBand->PamInitialize(); + } +} + +/************************************************************************/ +/* PamClear() */ +/************************************************************************/ + +void GDALPamDataset::PamClear() + +{ + if( psPam ) + { + CPLFree( psPam->pszPamFilename ); + CPLFree( psPam->pszProjection ); + CPLFree( psPam->pszGCPProjection ); + if( psPam->nGCPCount > 0 ) + { + GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList ); + CPLFree( psPam->pasGCPList ); + } + + delete psPam; + psPam = NULL; + } +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr GDALPamDataset::XMLInit( CPLXMLNode *psTree, const char *pszUnused ) + +{ +/* -------------------------------------------------------------------- */ +/* Check for an SRS node. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 ) + { + OGRSpatialReference oSRS; + + CPLFree( psPam->pszProjection ); + psPam->pszProjection = NULL; + + if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") ) + == OGRERR_NONE ) + oSRS.exportToWkt( &(psPam->pszProjection) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for a GeoTransform node. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 ) + { + const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", ""); + char **papszTokens; + + papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE ); + if( CSLCount(papszTokens) != 6 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GeoTransform node does not have expected six values."); + } + else + { + for( int iTA = 0; iTA < 6; iTA++ ) + psPam->adfGeoTransform[iTA] = atof(papszTokens[iTA]); + psPam->bHaveGeoTransform = TRUE; + } + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Check for GCPs. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" ); + + if( psGCPList != NULL ) + { + CPLFree( psPam->pszGCPProjection ); + psPam->pszGCPProjection = NULL; + + // Make sure any previous GCPs, perhaps from an .aux file, are cleared + // if we have new ones. + if( psPam->nGCPCount > 0 ) + { + GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList ); + CPLFree( psPam->pasGCPList ); + psPam->nGCPCount = 0; + psPam->pasGCPList = 0; + } + + GDALDeserializeGCPListFromXML( psGCPList, + &(psPam->pasGCPList), + &(psPam->nGCPCount), + &(psPam->pszGCPProjection) ); + } + +/* -------------------------------------------------------------------- */ +/* Apply any dataset level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Try loading ESRI xml encoded projection */ +/* -------------------------------------------------------------------- */ + if (psPam->pszProjection == NULL) + { + char** papszXML = oMDMD.GetMetadata( "xml:ESRI" ); + if (CSLCount(papszXML) == 1) + { + CPLXMLNode *psValueAsXML = CPLParseXMLString( papszXML[0] ); + if (psValueAsXML) + { + const char* pszESRI_WKT = CPLGetXMLValue(psValueAsXML, + "=GeodataXform.SpatialReference.WKT", NULL); + if (pszESRI_WKT) + { + OGRSpatialReference* poSRS = new OGRSpatialReference(NULL); + char* pszTmp = (char*)pszESRI_WKT; + if (poSRS->importFromWkt(&pszTmp) == OGRERR_NONE && + poSRS->morphFromESRI() == OGRERR_NONE) + { + char* pszWKT = NULL; + if (poSRS->exportToWkt(&pszWKT) == OGRERR_NONE) + { + psPam->pszProjection = CPLStrdup(pszWKT); + } + CPLFree(pszWKT); + } + delete poSRS; + } + CPLDestroyXMLNode(psValueAsXML); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psBandTree; + + for( psBandTree = psTree->psChild; + psBandTree != NULL; psBandTree = psBandTree->psNext ) + { + if( psBandTree->eType != CXT_Element + || !EQUAL(psBandTree->pszValue,"PAMRasterBand") ) + continue; + + int nBand = atoi(CPLGetXMLValue( psBandTree, "band", "0")); + + if( nBand < 1 || nBand > GetRasterCount() ) + continue; + + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(nBand); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + poBand->XMLInit( psBandTree, pszUnused ); + } + +/* -------------------------------------------------------------------- */ +/* Clear dirty flag. */ +/* -------------------------------------------------------------------- */ + nPamFlags &= ~GPF_DIRTY; + + return CE_None; +} + +/************************************************************************/ +/* SetPhysicalFilename() */ +/************************************************************************/ + +void GDALPamDataset::SetPhysicalFilename( const char *pszFilename ) + +{ + PamInitialize(); + + if( psPam ) + psPam->osPhysicalFilename = pszFilename; +} + +/************************************************************************/ +/* GetPhysicalFilename() */ +/************************************************************************/ + +const char *GDALPamDataset::GetPhysicalFilename() + +{ + PamInitialize(); + + if( psPam ) + return psPam->osPhysicalFilename; + else + return ""; +} + +/************************************************************************/ +/* SetSubdatasetName() */ +/************************************************************************/ + +void GDALPamDataset::SetSubdatasetName( const char *pszSubdataset ) + +{ + PamInitialize(); + + if( psPam ) + psPam->osSubdatasetName = pszSubdataset; +} + +/************************************************************************/ +/* GetSubdatasetName() */ +/************************************************************************/ + +const char *GDALPamDataset::GetSubdatasetName() + +{ + PamInitialize(); + + if( psPam ) + return psPam->osSubdatasetName; + else + return ""; +} + +/************************************************************************/ +/* BuildPamFilename() */ +/************************************************************************/ + +const char *GDALPamDataset::BuildPamFilename() + +{ + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* What is the name of the physical file we are referencing? */ +/* We allow an override via the psPam->pszPhysicalFile item. */ +/* -------------------------------------------------------------------- */ + if( psPam->pszPamFilename != NULL ) + return psPam->pszPamFilename; + + const char *pszPhysicalFile = psPam->osPhysicalFilename; + + if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL ) + pszPhysicalFile = GetDescription(); + + if( strlen(pszPhysicalFile) == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try a proxy lookup, otherwise just add .aux.xml. */ +/* -------------------------------------------------------------------- */ + const char *pszProxyPam = PamGetProxy( pszPhysicalFile ); + if( pszProxyPam != NULL ) + psPam->pszPamFilename = CPLStrdup(pszProxyPam); + else + { + psPam->pszPamFilename = (char*) CPLMalloc(strlen(pszPhysicalFile)+10); + strcpy( psPam->pszPamFilename, pszPhysicalFile ); + strcat( psPam->pszPamFilename, ".aux.xml" ); + } + + return psPam->pszPamFilename; +} + +/************************************************************************/ +/* IsPamFilenameAPotentialSiblingFile() */ +/************************************************************************/ + +int GDALPamDataset::IsPamFilenameAPotentialSiblingFile() +{ + if (psPam == NULL) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Determine if the PAM filename is a .aux.xml file next to the */ +/* physical file, or if it comes from the ProxyDB */ +/* -------------------------------------------------------------------- */ + const char *pszPhysicalFile = psPam->osPhysicalFilename; + + if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL ) + pszPhysicalFile = GetDescription(); + + int nLenPhysicalFile = strlen(pszPhysicalFile); + int bIsSiblingPamFile = strncmp(psPam->pszPamFilename, pszPhysicalFile, + nLenPhysicalFile) == 0 && + strcmp(psPam->pszPamFilename + nLenPhysicalFile, + ".aux.xml") == 0; + + return bIsSiblingPamFile; +} + +/************************************************************************/ +/* TryLoadXML() */ +/************************************************************************/ + +CPLErr GDALPamDataset::TryLoadXML(char **papszSiblingFiles) + +{ + CPLXMLNode *psTree = NULL; + + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Clear dirty flag. Generally when we get to this point is */ +/* from a call at the end of the Open() method, and some calls */ +/* may have already marked the PAM info as dirty (for instance */ +/* setting metadata), but really everything to this point is */ +/* reproducable, and so the PAM info shouldn't really be */ +/* thought of as dirty. */ +/* -------------------------------------------------------------------- */ + nPamFlags &= ~GPF_DIRTY; + +/* -------------------------------------------------------------------- */ +/* Try reading the file. */ +/* -------------------------------------------------------------------- */ + if( !BuildPamFilename() ) + return CE_None; + + VSIStatBufL sStatBuf; + +/* -------------------------------------------------------------------- */ +/* In case the PAM filename is a .aux.xml file next to the */ +/* physical file and we have a siblings list, then we can skip */ +/* stat'ing the filesystem. */ +/* -------------------------------------------------------------------- */ + if (papszSiblingFiles != NULL && IsPamFilenameAPotentialSiblingFile()) + { + int iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(psPam->pszPamFilename) ); + if( iSibling >= 0 ) + { + CPLErrorReset(); + CPLPushErrorHandler( CPLQuietErrorHandler ); + psTree = CPLParseXMLFile( psPam->pszPamFilename ); + CPLPopErrorHandler(); + } + } + else + if( VSIStatExL( psPam->pszPamFilename, &sStatBuf, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 + && VSI_ISREG( sStatBuf.st_mode ) ) + { + CPLErrorReset(); + CPLPushErrorHandler( CPLQuietErrorHandler ); + psTree = CPLParseXMLFile( psPam->pszPamFilename ); + CPLPopErrorHandler(); + } + +/* -------------------------------------------------------------------- */ +/* If we are looking for a subdataset, search for it's subtree */ +/* now. */ +/* -------------------------------------------------------------------- */ + if( psTree && psPam->osSubdatasetName.size() ) + { + CPLXMLNode *psSubTree; + + for( psSubTree = psTree->psChild; + psSubTree != NULL; + psSubTree = psSubTree->psNext ) + { + if( psSubTree->eType != CXT_Element + || !EQUAL(psSubTree->pszValue,"Subdataset") ) + continue; + + if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ), + psPam->osSubdatasetName) ) + continue; + + psSubTree = CPLGetXMLNode( psSubTree, "PAMDataset" ); + break; + } + + if( psSubTree != NULL ) + psSubTree = CPLCloneXMLTree( psSubTree ); + + CPLDestroyXMLNode( psTree ); + psTree = psSubTree; + } + +/* -------------------------------------------------------------------- */ +/* If we fail, try .aux. */ +/* -------------------------------------------------------------------- */ + if( psTree == NULL ) + return TryLoadAux(papszSiblingFiles); + +/* -------------------------------------------------------------------- */ +/* Initialize ourselves from this XML tree. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + CPLString osVRTPath(CPLGetPath(psPam->pszPamFilename)); + eErr = XMLInit( psTree, osVRTPath ); + + CPLDestroyXMLNode( psTree ); + + if( eErr != CE_None ) + PamClear(); + + return eErr; +} + +/************************************************************************/ +/* TrySaveXML() */ +/************************************************************************/ + +CPLErr GDALPamDataset::TrySaveXML() + +{ + CPLXMLNode *psTree; + CPLErr eErr = CE_None; + + nPamFlags &= ~GPF_DIRTY; + + if( psPam == NULL || (nPamFlags & GPF_NOSAVE) ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Make sure we know the filename we want to store in. */ +/* -------------------------------------------------------------------- */ + if( !BuildPamFilename() ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Build the XML representation of the auxilary metadata. */ +/* -------------------------------------------------------------------- */ + psTree = SerializeToXML( NULL ); + + if( psTree == NULL ) + { + /* If we have unset all metadata, we have to delete the PAM file */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + VSIUnlink(psPam->pszPamFilename); + CPLPopErrorHandler(); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* If we are working with a subdataset, we need to integrate */ +/* the subdataset tree within the whole existing pam tree, */ +/* after removing any old version of the same subdataset. */ +/* -------------------------------------------------------------------- */ + if( psPam->osSubdatasetName.size() != 0 ) + { + CPLXMLNode *psOldTree, *psSubTree; + + CPLErrorReset(); + CPLPushErrorHandler( CPLQuietErrorHandler ); + psOldTree = CPLParseXMLFile( psPam->pszPamFilename ); + CPLPopErrorHandler(); + + if( psOldTree == NULL ) + psOldTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" ); + + for( psSubTree = psOldTree->psChild; + psSubTree != NULL; + psSubTree = psSubTree->psNext ) + { + if( psSubTree->eType != CXT_Element + || !EQUAL(psSubTree->pszValue,"Subdataset") ) + continue; + + if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ), + psPam->osSubdatasetName) ) + continue; + + break; + } + + if( psSubTree == NULL ) + { + psSubTree = CPLCreateXMLNode( psOldTree, CXT_Element, + "Subdataset" ); + CPLCreateXMLNode( + CPLCreateXMLNode( psSubTree, CXT_Attribute, "name" ), + CXT_Text, psPam->osSubdatasetName ); + } + + CPLXMLNode *psOldPamDataset = CPLGetXMLNode( psSubTree, "PAMDataset"); + if( psOldPamDataset != NULL ) + { + CPLRemoveXMLChild( psSubTree, psOldPamDataset ); + CPLDestroyXMLNode( psOldPamDataset ); + } + + CPLAddXMLChild( psSubTree, psTree ); + psTree = psOldTree; + } + +/* -------------------------------------------------------------------- */ +/* Try saving the auxilary metadata. */ +/* -------------------------------------------------------------------- */ + int bSaved; + + CPLPushErrorHandler( CPLQuietErrorHandler ); + bSaved = CPLSerializeXMLTreeToFile( psTree, psPam->pszPamFilename ); + CPLPopErrorHandler(); + +/* -------------------------------------------------------------------- */ +/* If it fails, check if we have a proxy directory for auxilary */ +/* metadata to be stored in, and try to save there. */ +/* -------------------------------------------------------------------- */ + if( bSaved ) + eErr = CE_None; + else + { + const char *pszNewPam; + const char *pszBasename = GetDescription(); + + if( psPam && psPam->osPhysicalFilename.length() > 0 ) + pszBasename = psPam->osPhysicalFilename; + + if( PamGetProxy(pszBasename) == NULL + && ((pszNewPam = PamAllocateProxy(pszBasename)) != NULL)) + { + CPLErrorReset(); + CPLFree( psPam->pszPamFilename ); + psPam->pszPamFilename = CPLStrdup(pszNewPam); + eErr = TrySaveXML(); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to save auxilary information in %s.", + psPam->pszPamFilename ); + eErr = CE_Warning; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLDestroyXMLNode( psTree ); + + return eErr; +} + +/************************************************************************/ +/* CloneInfo() */ +/************************************************************************/ + +CPLErr GDALPamDataset::CloneInfo( GDALDataset *poSrcDS, int nCloneFlags ) + +{ + int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING; + int nSavedMOFlags = GetMOFlags(); + + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Supress NotImplemented error messages - mainly needed if PAM */ +/* disabled. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED ); + +/* -------------------------------------------------------------------- */ +/* GeoTransform */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_GEOTRANSFORM ) + { + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { + double adfOldGT[6]; + + if( !bOnlyIfMissing || GetGeoTransform( adfOldGT ) != CE_None ) + SetGeoTransform( adfGeoTransform ); + } + } + +/* -------------------------------------------------------------------- */ +/* Projection */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_PROJECTION ) + { + const char *pszWKT = poSrcDS->GetProjectionRef(); + + if( pszWKT != NULL && strlen(pszWKT) > 0 ) + { + if( !bOnlyIfMissing + || GetProjectionRef() == NULL + || strlen(GetProjectionRef()) == 0 ) + SetProjection( pszWKT ); + } + } + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_GCPS ) + { + if( poSrcDS->GetGCPCount() > 0 ) + { + if( !bOnlyIfMissing || GetGCPCount() == 0 ) + { + SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + poSrcDS->GetGCPProjection() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Metadata */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_METADATA ) + { + if( poSrcDS->GetMetadata() != NULL ) + { + if( !bOnlyIfMissing + || CSLCount(GetMetadata()) != CSLCount(poSrcDS->GetMetadata()) ) + { + SetMetadata( poSrcDS->GetMetadata() ); + } + } + if( poSrcDS->GetMetadata("RPC") != NULL ) + { + if( !bOnlyIfMissing + || CSLCount(GetMetadata("RPC")) + != CSLCount(poSrcDS->GetMetadata("RPC")) ) + { + SetMetadata( poSrcDS->GetMetadata("RPC"), "RPC" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_PROCESS_BANDS ) + { + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + if( poSrcDS->GetRasterCount() >= iBand+1 ) + poBand->CloneInfo( poSrcDS->GetRasterBand(iBand+1), + nCloneFlags ); + else + CPLDebug( "GDALPamDataset", "Skipping CloneInfo for band not in source, this is a bit unusual!" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy masks. These are really copied at a lower level using */ +/* GDALDefaultOverviews, for formats with no native mask */ +/* support but this is a convenient central point to put this */ +/* for most drivers. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_MASK ) + { + GDALDriver::DefaultCopyMasks( poSrcDS, this, FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Restore MO flags. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags ); + + return CE_None; +} + +/************************************************************************/ +/* GetFileList() */ +/* */ +/* Add .aux.xml or .aux file into file list as appropriate. */ +/************************************************************************/ + +char **GDALPamDataset::GetFileList() + +{ + VSIStatBufL sStatBuf; + char **papszFileList = GDALDataset::GetFileList(); + + if( psPam && psPam->osPhysicalFilename.size() > 0 + && CSLFindString( papszFileList, psPam->osPhysicalFilename ) == -1 ) + { + papszFileList = CSLInsertString( papszFileList, 0, + psPam->osPhysicalFilename ); + } + + if( psPam && psPam->pszPamFilename ) + { + int bAddPamFile = (nPamFlags & GPF_DIRTY); + if (!bAddPamFile) + { + if (oOvManager.GetSiblingFiles() != NULL && IsPamFilenameAPotentialSiblingFile()) + bAddPamFile = CSLFindString(oOvManager.GetSiblingFiles(), + CPLGetFilename(psPam->pszPamFilename)) >= 0; + else + bAddPamFile = VSIStatExL( psPam->pszPamFilename, &sStatBuf, + VSI_STAT_EXISTS_FLAG ) == 0; + } + if (bAddPamFile) + { + papszFileList = CSLAddString( papszFileList, psPam->pszPamFilename ); + } + } + + if( psPam && psPam->osAuxFilename.size() > 0 && + CSLFindString( papszFileList, psPam->osAuxFilename ) == -1 ) + { + papszFileList = CSLAddString( papszFileList, psPam->osAuxFilename ); + } + return papszFileList; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr GDALPamDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* Initialize PAM. */ +/* -------------------------------------------------------------------- */ + PamInitialize(); + if( psPam == NULL ) + return GDALDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* If we appear to have subdatasets and to have a physical */ +/* filename, use that physical filename to derive a name for a */ +/* new overview file. */ +/* -------------------------------------------------------------------- */ + if( oOvManager.IsInitialized() && psPam->osPhysicalFilename.length() != 0 ) + return oOvManager.BuildOverviewsSubDataset( + psPam->osPhysicalFilename, pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + else + return GDALDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GDALPamDataset::GetProjectionRef() + +{ + if( psPam && psPam->pszProjection ) + return psPam->pszProjection; + else + return GDALDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetProjection( const char *pszProjectionIn ) + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALDataset::SetProjection( pszProjectionIn ); + else + { + CPLFree( psPam->pszProjection ); + psPam->pszProjection = CPLStrdup( pszProjectionIn ); + MarkPamDirty(); + + return CE_None; + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALPamDataset::GetGeoTransform( double * padfTransform ) + +{ + if( psPam && psPam->bHaveGeoTransform ) + { + memcpy( padfTransform, psPam->adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + return GDALDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetGeoTransform( double * padfTransform ) + +{ + PamInitialize(); + + if( psPam ) + { + MarkPamDirty(); + psPam->bHaveGeoTransform = TRUE; + memcpy( psPam->adfGeoTransform, padfTransform, sizeof(double) * 6 ); + return( CE_None ); + } + else + { + return GDALDataset::SetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int GDALPamDataset::GetGCPCount() + +{ + if( psPam && psPam->nGCPCount > 0 ) + return psPam->nGCPCount; + else + return GDALDataset::GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GDALPamDataset::GetGCPProjection() + +{ + if( psPam && psPam->pszGCPProjection != NULL ) + return psPam->pszGCPProjection; + else + return GDALDataset::GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GDALPamDataset::GetGCPs() + +{ + if( psPam && psPam->nGCPCount > 0 ) + return psPam->pasGCPList; + else + return GDALDataset::GetGCPs(); +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) + +{ + PamInitialize(); + + if( psPam ) + { + CPLFree( psPam->pszGCPProjection ); + if( psPam->nGCPCount > 0 ) + { + GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList ); + CPLFree( psPam->pasGCPList ); + } + + psPam->pszGCPProjection = CPLStrdup(pszGCPProjection); + psPam->nGCPCount = nGCPCount; + psPam->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList ); + + MarkPamDirty(); + + return CE_None; + } + else + { + return GDALDataset::SetGCPs( nGCPCount, pasGCPList, pszGCPProjection ); + } +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + MarkPamDirty(); + + return GDALDataset::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + MarkPamDirty(); + + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALPamDataset::GetMetadataItem( const char *pszName, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* A request against the ProxyOverviewRequest is a special */ +/* mechanism to request an overview filename be allocated in */ +/* the proxy pool location. The allocated name is saved as */ +/* metadata as well as being returned. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && EQUAL(pszDomain,"ProxyOverviewRequest") ) + { + CPLString osPrelimOvr = GetDescription(); + osPrelimOvr += ":::OVR"; + + const char *pszProxyOvrFilename = PamAllocateProxy( osPrelimOvr ); + if( pszProxyOvrFilename == NULL ) + return NULL; + + SetMetadataItem( "OVERVIEW_FILE", pszProxyOvrFilename, "OVERVIEWS" ); + + return pszProxyOvrFilename; + } + +/* -------------------------------------------------------------------- */ +/* If the OVERVIEW_FILE metadata is requested, we intercept the */ +/* request in order to replace ":::BASE:::" with the path to */ +/* the physical file - if available. This is primarily for the */ +/* purpose of managing subdataset overview filenames as being */ +/* relative to the physical file the subdataset comes */ +/* from. (#3287). */ +/* -------------------------------------------------------------------- */ + else if( pszDomain != NULL + && EQUAL(pszDomain,"OVERVIEWS") + && EQUAL(pszName,"OVERVIEW_FILE") ) + { + const char *pszOverviewFile = + GDALDataset::GetMetadataItem( pszName, pszDomain ); + + if( pszOverviewFile == NULL + || !EQUALN(pszOverviewFile,":::BASE:::",10) ) + return pszOverviewFile; + + CPLString osPath; + + if( strlen(GetPhysicalFilename()) > 0 ) + osPath = CPLGetPath(GetPhysicalFilename()); + else + osPath = CPLGetPath(GetDescription()); + + return CPLFormFilename( osPath, pszOverviewFile + 10, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Everything else is a pass through. */ +/* -------------------------------------------------------------------- */ + else + return GDALDataset::GetMetadataItem( pszName, pszDomain ); + +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALPamDataset::GetMetadata( const char *pszDomain ) + +{ +// if( pszDomain == NULL || !EQUAL(pszDomain,"ProxyOverviewRequest") ) + return GDALDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* TryLoadAux() */ +/************************************************************************/ + +CPLErr GDALPamDataset::TryLoadAux(char **papszSiblingFiles) + +{ +/* -------------------------------------------------------------------- */ +/* Initialize PAM. */ +/* -------------------------------------------------------------------- */ + PamInitialize(); + if( psPam == NULL ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* What is the name of the physical file we are referencing? */ +/* We allow an override via the psPam->pszPhysicalFile item. */ +/* -------------------------------------------------------------------- */ + const char *pszPhysicalFile = psPam->osPhysicalFilename; + + if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL ) + pszPhysicalFile = GetDescription(); + + if( strlen(pszPhysicalFile) == 0 ) + return CE_None; + + if( papszSiblingFiles ) + { + CPLString osAuxFilename = CPLResetExtension( pszPhysicalFile, "aux"); + int iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + { + osAuxFilename = pszPhysicalFile; + osAuxFilename += ".aux"; + iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* Try to open .aux file. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poAuxDS = GDALFindAssociatedAuxFile( pszPhysicalFile, + GA_ReadOnly, this ); + + if( poAuxDS == NULL ) + return CE_None; + + psPam->osAuxFilename = poAuxDS->GetDescription(); + +/* -------------------------------------------------------------------- */ +/* Do we have an SRS on the aux file? */ +/* -------------------------------------------------------------------- */ + if( strlen(poAuxDS->GetProjectionRef()) > 0 ) + GDALPamDataset::SetProjection( poAuxDS->GetProjectionRef() ); + +/* -------------------------------------------------------------------- */ +/* Geotransform. */ +/* -------------------------------------------------------------------- */ + if( poAuxDS->GetGeoTransform( psPam->adfGeoTransform ) == CE_None ) + psPam->bHaveGeoTransform = TRUE; + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( poAuxDS->GetGCPCount() > 0 ) + { + psPam->nGCPCount = poAuxDS->GetGCPCount(); + psPam->pasGCPList = GDALDuplicateGCPs( psPam->nGCPCount, + poAuxDS->GetGCPs() ); + } + +/* -------------------------------------------------------------------- */ +/* Apply metadata. We likely ought to be merging this in rather */ +/* than overwriting everything that was there. */ +/* -------------------------------------------------------------------- */ + char **papszMD = poAuxDS->GetMetadata(); + if( CSLCount(papszMD) > 0 ) + { + char **papszMerged = + CSLMerge( CSLDuplicate(GetMetadata()), papszMD ); + GDALPamDataset::SetMetadata( papszMerged ); + CSLDestroy( papszMerged ); + } + + papszMD = poAuxDS->GetMetadata("XFORMS"); + if( CSLCount(papszMD) > 0 ) + { + char **papszMerged = + CSLMerge( CSLDuplicate(GetMetadata("XFORMS")), papszMD ); + GDALPamDataset::SetMetadata( papszMerged, "XFORMS" ); + CSLDestroy( papszMerged ); + } + +/* ==================================================================== */ +/* Process bands. */ +/* ==================================================================== */ + int iBand; + + for( iBand = 0; iBand < poAuxDS->GetRasterCount(); iBand++ ) + { + if( iBand >= GetRasterCount() ) + break; + + GDALRasterBand *poAuxBand = poAuxDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poBand = GetRasterBand( iBand+1 ); + + papszMD = poAuxBand->GetMetadata(); + if( CSLCount(papszMD) > 0 ) + { + char **papszMerged = + CSLMerge( CSLDuplicate(poBand->GetMetadata()), papszMD ); + poBand->SetMetadata( papszMerged ); + CSLDestroy( papszMerged ); + } + + if( strlen(poAuxBand->GetDescription()) > 0 ) + poBand->SetDescription( poAuxBand->GetDescription() ); + + if( poAuxBand->GetCategoryNames() != NULL ) + poBand->SetCategoryNames( poAuxBand->GetCategoryNames() ); + + if( poAuxBand->GetColorTable() != NULL + && poBand->GetColorTable() == NULL ) + poBand->SetColorTable( poAuxBand->GetColorTable() ); + + // histograms? + double dfMin, dfMax; + int nBuckets, *panHistogram=NULL; + + if( poAuxBand->GetDefaultHistogram( &dfMin, &dfMax, + &nBuckets, &panHistogram, + FALSE, NULL, NULL ) == CE_None ) + { + poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets, + panHistogram ); + CPLFree( panHistogram ); + } + + // RAT + if( poAuxBand->GetDefaultRAT() != NULL ) + poBand->SetDefaultRAT( poAuxBand->GetDefaultRAT() ); + + // NoData + int bSuccess = FALSE; + double dfNoDataValue = poAuxBand->GetNoDataValue( &bSuccess ); + if( bSuccess ) + poBand->SetNoDataValue( dfNoDataValue ); + } + + GDALClose( poAuxDS ); + +/* -------------------------------------------------------------------- */ +/* Mark PAM info as clean. */ +/* -------------------------------------------------------------------- */ + nPamFlags &= ~GPF_DIRTY; + + return CE_Failure; +} diff --git a/ogr/gdalpamproxydb.cpp b/ogr/gdalpamproxydb.cpp new file mode 100644 index 0000000..b771f74 --- /dev/null +++ b/ogr/gdalpamproxydb.cpp @@ -0,0 +1,400 @@ +/****************************************************************************** + * $Id: gdalpamproxydb.cpp 22812 2011-07-25 04:50:23Z warmerdam $ + * + * Project: GDAL Core + * Purpose: Implementation of the GDAL PAM Proxy database interface. + * The proxy db is used to associate .aux.xml files in a temp + * directory - used for files for which aux.xml files can't be + * created (ie. read-only file systems). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdalpamproxydb.cpp 22812 2011-07-25 04:50:23Z warmerdam $"); + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPamProxyDB */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPamProxyDB +{ + public: + GDALPamProxyDB() { nUpdateCounter = -1; } + + CPLString osProxyDBDir; + + int nUpdateCounter; + + std::vector aosOriginalFiles; + std::vector aosProxyFiles; + + void CheckLoadDB(); + void LoadDB(); + void SaveDB(); +}; + +static int bProxyDBInitialized = FALSE; +static GDALPamProxyDB *poProxyDB = NULL; +static void *hProxyDBLock = NULL; + +/************************************************************************/ +/* CheckLoadDB() */ +/* */ +/* Eventually we want to check if the file has changed, and if */ +/* so, force it to be reloaded. TODO: */ +/************************************************************************/ + +void GDALPamProxyDB::CheckLoadDB() + +{ + if( nUpdateCounter == -1 ) + LoadDB(); +} + +/************************************************************************/ +/* LoadDB() */ +/* */ +/* It is assumed the caller already holds the lock. */ +/************************************************************************/ + +void GDALPamProxyDB::LoadDB() + +{ +/* -------------------------------------------------------------------- */ +/* Open the database relating original names to proxy .aux.xml */ +/* file names. */ +/* -------------------------------------------------------------------- */ + CPLString osDBName = + CPLFormFilename( osProxyDBDir, "gdal_pam_proxy", "dat" ); + VSILFILE *fpDB = VSIFOpenL( osDBName, "r" ); + + nUpdateCounter = 0; + if( fpDB == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Read header, verify and extract update counter. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[100]; + + if( VSIFReadL( abyHeader, 1, 100, fpDB ) != 100 + || strncmp( (const char *) abyHeader, "GDAL_PROXY", 10 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Problem reading %s header - short or corrupt?", + osDBName.c_str() ); + return; + } + + nUpdateCounter = atoi((const char *) abyHeader + 10); + +/* -------------------------------------------------------------------- */ +/* Read the file in one gulp. */ +/* -------------------------------------------------------------------- */ + int nBufLength; + char *pszDBData; + + VSIFSeekL( fpDB, 0, SEEK_END ); + nBufLength = (int) (VSIFTellL(fpDB) - 100); + + pszDBData = (char *) CPLCalloc(1,nBufLength+1); + VSIFSeekL( fpDB, 100, SEEK_SET ); + VSIFReadL( pszDBData, 1, nBufLength, fpDB ); + + VSIFCloseL( fpDB ); + +/* -------------------------------------------------------------------- */ +/* Parse the list of in/out names. */ +/* -------------------------------------------------------------------- */ + int iNext = 0; + + while( iNext < nBufLength ) + { + CPLString osOriginal, osProxy; + + osOriginal.assign( pszDBData + iNext ); + + for( ; iNext < nBufLength && pszDBData[iNext] != '\0'; iNext++ ) {} + + if( iNext == nBufLength ) + break; + + iNext++; + + osProxy = osProxyDBDir; + osProxy += "/"; + osProxy += pszDBData + iNext; + + for( ; iNext < nBufLength && pszDBData[iNext] != '\0'; iNext++ ) {} + iNext++; + + aosOriginalFiles.push_back( osOriginal ); + aosProxyFiles.push_back( osProxy ); + } + + CPLFree( pszDBData ); +} + +/************************************************************************/ +/* SaveDB() */ +/************************************************************************/ + +void GDALPamProxyDB::SaveDB() + +{ +/* -------------------------------------------------------------------- */ +/* Open the database relating original names to proxy .aux.xml */ +/* file names. */ +/* -------------------------------------------------------------------- */ + CPLString osDBName = + CPLFormFilename( osProxyDBDir, "gdal_pam_proxy", "dat" ); + + void *hLock = CPLLockFile( osDBName, 1.0 ); + + // proceed even if lock fails - we need CPLBreakLockFile()! + if( hLock == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GDALPamProxyDB::SaveDB() - Failed to lock %s file, proceeding anyways.", + osDBName.c_str() ); + } + + VSILFILE *fpDB = VSIFOpenL( osDBName, "w" ); + if( fpDB == NULL ) + { + if( hLock ) + CPLUnlockFile( hLock ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to save %s Pam Proxy DB.\n%s", + osDBName.c_str(), + VSIStrerror( errno ) ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Write header. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[100]; + + memset( abyHeader, ' ', sizeof(abyHeader) ); + strncpy( (char *) abyHeader, "GDAL_PROXY", 10 ); + sprintf( (char *) abyHeader + 10, "%9d", nUpdateCounter ); + + VSIFWriteL( abyHeader, 1, 100, fpDB ); + +/* -------------------------------------------------------------------- */ +/* Write names. */ +/* -------------------------------------------------------------------- */ + unsigned int i; + + for( i = 0; i < aosOriginalFiles.size(); i++ ) + { + size_t nBytesWritten; + const char *pszProxyFile; + + VSIFWriteL( aosOriginalFiles[i].c_str(), 1, + strlen(aosOriginalFiles[i].c_str())+1, fpDB ); + + pszProxyFile = CPLGetFilename(aosProxyFiles[i]); + nBytesWritten = VSIFWriteL( pszProxyFile, 1, + strlen(pszProxyFile)+1, fpDB ); + + if( nBytesWritten != strlen(pszProxyFile)+1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to write complete %s Pam Proxy DB.\n%s", + osDBName.c_str(), + VSIStrerror( errno ) ); + VSIFCloseL( fpDB ); + VSIUnlink( osDBName ); + return; + } + } + + VSIFCloseL( fpDB ); + + if( hLock ) + CPLUnlockFile( hLock ); +} + + +/************************************************************************/ +/* InitProxyDB() */ +/* */ +/* Initialize ProxyDB (if it isn't already initialized). */ +/************************************************************************/ + +static void InitProxyDB() + +{ + if( !bProxyDBInitialized ) + { + CPLMutexHolderD( &hProxyDBLock ); + + if( !bProxyDBInitialized ) + { + const char *pszProxyDir = + CPLGetConfigOption( "GDAL_PAM_PROXY_DIR", NULL ); + + if( pszProxyDir ) + { + poProxyDB = new GDALPamProxyDB(); + poProxyDB->osProxyDBDir = pszProxyDir; + } + } + + bProxyDBInitialized = TRUE; + } +} + +/************************************************************************/ +/* PamCleanProxyDB() */ +/************************************************************************/ + +void PamCleanProxyDB() + +{ + { + CPLMutexHolderD( &hProxyDBLock ); + + bProxyDBInitialized = FALSE; + + delete poProxyDB; + poProxyDB = NULL; + } + + CPLDestroyMutex( hProxyDBLock ); + hProxyDBLock = NULL; +} + +/************************************************************************/ +/* PamGetProxy() */ +/************************************************************************/ + +const char *PamGetProxy( const char *pszOriginal ) + +{ + InitProxyDB(); + + if( poProxyDB == NULL ) + return NULL; + + CPLMutexHolderD( &hProxyDBLock ); + unsigned int i; + + poProxyDB->CheckLoadDB(); + + for( i = 0; i < poProxyDB->aosOriginalFiles.size(); i++ ) + { + if( strcmp( poProxyDB->aosOriginalFiles[i], pszOriginal ) == 0 ) + return poProxyDB->aosProxyFiles[i]; + } + + return NULL; +} + +/************************************************************************/ +/* PamAllocateProxy() */ +/************************************************************************/ + +const char *PamAllocateProxy( const char *pszOriginal ) + +{ + InitProxyDB(); + + if( poProxyDB == NULL ) + return NULL; + + CPLMutexHolderD( &hProxyDBLock ); + + poProxyDB->CheckLoadDB(); + +/* -------------------------------------------------------------------- */ +/* Form the proxy filename based on the original path if */ +/* possible, but dummy out any questionable characters, path */ +/* delimiters and such. This is intended to make the proxy */ +/* name be identifiable by folks digging around in the proxy */ +/* database directory. */ +/* */ +/* We also need to be careful about length. */ +/* -------------------------------------------------------------------- */ + CPLString osRevProxyFile; + int i; + + i = strlen(pszOriginal) - 1; + while( i >= 0 && osRevProxyFile.size() < 220 ) + { + if( i > 6 && EQUALN(pszOriginal+i-5,":::OVR",6) ) + i -= 6; + + // make some effort to break long names at path delimiters. + if( (pszOriginal[i] == '/' || pszOriginal[i] == '\\') + && osRevProxyFile.size() > 200 ) + break; + + if( (pszOriginal[i] >= 'A' && pszOriginal[i] <= 'Z') + || (pszOriginal[i] >= 'a' && pszOriginal[i] <= 'z') + || (pszOriginal[i] >= '0' && pszOriginal[i] <= '9') + || pszOriginal[i] == '.' ) + osRevProxyFile += pszOriginal[i]; + else + osRevProxyFile += '_'; + + i--; + } + + CPLString osOriginal = pszOriginal; + CPLString osProxy; + CPLString osCounter; + + osProxy = poProxyDB->osProxyDBDir + "/"; + + osCounter.Printf( "%06d_", poProxyDB->nUpdateCounter++ ); + osProxy += osCounter; + + for( i = osRevProxyFile.size()-1; i >= 0; i-- ) + osProxy += osRevProxyFile[i]; + + if( osOriginal.find(":::OVR") != CPLString::npos ) + osProxy += ".ovr"; + else + osProxy += ".aux.xml"; + +/* -------------------------------------------------------------------- */ +/* Add the proxy and the original to the proxy list and resave */ +/* the database. */ +/* -------------------------------------------------------------------- */ + poProxyDB->aosOriginalFiles.push_back( osOriginal ); + poProxyDB->aosProxyFiles.push_back( osProxy ); + + poProxyDB->SaveDB(); + + return PamGetProxy( pszOriginal ); +} diff --git a/ogr/gdalpamrasterband.cpp b/ogr/gdalpamrasterband.cpp new file mode 100644 index 0000000..705e532 --- /dev/null +++ b/ogr/gdalpamrasterband.cpp @@ -0,0 +1,1336 @@ +/****************************************************************************** + * $Id: gdalpamrasterband.cpp 27269 2014-05-01 16:38:02Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALPamRasterBand, a raster band base class + * that knows how to persistently store auxilary metadata in an + * external xml file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "gdal_rat.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalpamrasterband.cpp 27269 2014-05-01 16:38:02Z rouault $"); + +/************************************************************************/ +/* GDALPamRasterBand() */ +/************************************************************************/ + +GDALPamRasterBand::GDALPamRasterBand() + +{ + psPam = NULL; + SetMOFlags( GetMOFlags() | GMO_PAM_CLASS ); +} + +/************************************************************************/ +/* ~GDALPamRasterBand() */ +/************************************************************************/ + +GDALPamRasterBand::~GDALPamRasterBand() + +{ + PamClear(); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *GDALPamRasterBand::SerializeToXML( const char *pszUnused ) + +{ + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup root node and attributes. */ +/* -------------------------------------------------------------------- */ + CPLString oFmt; + + CPLXMLNode *psTree; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMRasterBand" ); + + if( GetBand() > 0 ) + CPLSetXMLValue( psTree, "#band", oFmt.Printf( "%d", GetBand() ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize information of interest. */ +/* -------------------------------------------------------------------- */ + if( strlen(GetDescription()) > 0 ) + CPLSetXMLValue( psTree, "Description", GetDescription() ); + + if( psPam->bNoDataValueSet ) + { + if (CPLIsNan(psPam->dfNoDataValue)) + CPLSetXMLValue( psTree, "NoDataValue", "nan" ); + else + CPLSetXMLValue( psTree, "NoDataValue", + oFmt.Printf( "%.14E", psPam->dfNoDataValue ) ); + + /* hex encode real floating point values */ + if( psPam->dfNoDataValue != floor(psPam->dfNoDataValue) + || psPam->dfNoDataValue != atof(oFmt) ) + { + double dfNoDataLittleEndian; + + dfNoDataLittleEndian = psPam->dfNoDataValue; + CPL_LSBPTR64( &dfNoDataLittleEndian ); + + char *pszHexEncoding = + CPLBinaryToHex( 8, (GByte *) &dfNoDataLittleEndian ); + CPLSetXMLValue( psTree, "NoDataValue.#le_hex_equiv",pszHexEncoding); + CPLFree( pszHexEncoding ); + } + } + + if( psPam->pszUnitType != NULL ) + CPLSetXMLValue( psTree, "UnitType", psPam->pszUnitType ); + + if( psPam->dfOffset != 0.0 ) + CPLSetXMLValue( psTree, "Offset", + oFmt.Printf( "%.16g", psPam->dfOffset ) ); + + if( psPam->dfScale != 1.0 ) + CPLSetXMLValue( psTree, "Scale", + oFmt.Printf( "%.16g", psPam->dfScale ) ); + + if( psPam->eColorInterp != GCI_Undefined ) + CPLSetXMLValue( psTree, "ColorInterp", + GDALGetColorInterpretationName( psPam->eColorInterp )); + +/* -------------------------------------------------------------------- */ +/* Category names. */ +/* -------------------------------------------------------------------- */ + if( psPam->papszCategoryNames != NULL ) + { + CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, + "CategoryNames" ); + CPLXMLNode* psLastChild = NULL; + + for( int iEntry=0; psPam->papszCategoryNames[iEntry] != NULL; iEntry++) + { + CPLXMLNode *psNode = CPLCreateXMLElementAndValue( NULL, "Category", + psPam->papszCategoryNames[iEntry] ); + if( psLastChild == NULL ) + psCT_XML->psChild = psNode; + else + psLastChild->psNext = psNode; + psLastChild = psNode; + } + } + +/* -------------------------------------------------------------------- */ +/* Color Table. */ +/* -------------------------------------------------------------------- */ + if( psPam->poColorTable != NULL ) + { + CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, + "ColorTable" ); + CPLXMLNode* psLastChild = NULL; + + for( int iEntry=0; iEntry < psPam->poColorTable->GetColorEntryCount(); + iEntry++ ) + { + GDALColorEntry sEntry; + CPLXMLNode *psEntry_XML = CPLCreateXMLNode( NULL, CXT_Element, + "Entry" ); + if( psLastChild == NULL ) + psCT_XML->psChild = psEntry_XML; + else + psLastChild->psNext = psEntry_XML; + psLastChild = psEntry_XML; + + psPam->poColorTable->GetColorEntryAsRGB( iEntry, &sEntry ); + + CPLSetXMLValue( psEntry_XML, "#c1", oFmt.Printf("%d",sEntry.c1) ); + CPLSetXMLValue( psEntry_XML, "#c2", oFmt.Printf("%d",sEntry.c2) ); + CPLSetXMLValue( psEntry_XML, "#c3", oFmt.Printf("%d",sEntry.c3) ); + CPLSetXMLValue( psEntry_XML, "#c4", oFmt.Printf("%d",sEntry.c4) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Min/max. */ +/* -------------------------------------------------------------------- */ + if( psPam->bHaveMinMax ) + { + CPLSetXMLValue( psTree, "Minimum", + oFmt.Printf( "%.16g", psPam->dfMin ) ); + CPLSetXMLValue( psTree, "Maximum", + oFmt.Printf( "%.16g", psPam->dfMax ) ); + } + +/* -------------------------------------------------------------------- */ +/* Statistics */ +/* -------------------------------------------------------------------- */ + if( psPam->bHaveStats ) + { + CPLSetXMLValue( psTree, "Mean", + oFmt.Printf( "%.16g", psPam->dfMean ) ); + CPLSetXMLValue( psTree, "StandardDeviation", + oFmt.Printf( "%.16g", psPam->dfStdDev ) ); + } + +/* -------------------------------------------------------------------- */ +/* Histograms. */ +/* -------------------------------------------------------------------- */ + if( psPam->psSavedHistograms != NULL ) + CPLAddXMLChild( psTree, CPLCloneXMLTree( psPam->psSavedHistograms ) ); + +/* -------------------------------------------------------------------- */ +/* Raster Attribute Table */ +/* -------------------------------------------------------------------- */ + if( psPam->poDefaultRAT != NULL ) + { + CPLXMLNode* psSerializedRAT = psPam->poDefaultRAT->Serialize(); + if( psSerializedRAT != NULL ) + CPLAddXMLChild( psTree, psSerializedRAT ); + } + +/* -------------------------------------------------------------------- */ +/* Metadata. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psMD; + + psMD = oMDMD.Serialize(); + if( psMD != NULL ) + { + if( psMD->psChild == NULL ) + CPLDestroyXMLNode( psMD ); + else + CPLAddXMLChild( psTree, psMD ); + } + +/* -------------------------------------------------------------------- */ +/* We don't want to return anything if we had no metadata to */ +/* attach. */ +/* -------------------------------------------------------------------- */ + if( psTree->psChild == NULL || psTree->psChild->psNext == NULL ) + { + CPLDestroyXMLNode( psTree ); + psTree = NULL; + } + + return psTree; +} + +/************************************************************************/ +/* PamInitialize() */ +/************************************************************************/ + +void GDALPamRasterBand::PamInitialize() + +{ + if( psPam ) + return; + + GDALPamDataset *poParentDS = (GDALPamDataset *) GetDataset(); + + if( poParentDS == NULL || !(poParentDS->GetMOFlags() & GMO_PAM_CLASS) ) + return; + + poParentDS->PamInitialize(); + if( poParentDS->psPam == NULL ) + return; + + // Often (always?) initializing our parent will have initialized us. + if( psPam != NULL ) + return; + + psPam = (GDALRasterBandPamInfo *) + CPLCalloc(sizeof(GDALRasterBandPamInfo),1); + + psPam->dfScale = 1.0; + psPam->poParentDS = poParentDS; + psPam->dfNoDataValue = -1e10; + psPam->poDefaultRAT = NULL; +} + +/************************************************************************/ +/* PamClear() */ +/************************************************************************/ + +void GDALPamRasterBand::PamClear() + +{ + if( psPam ) + { + if( psPam->poColorTable ) + delete psPam->poColorTable; + psPam->poColorTable = NULL; + + CPLFree( psPam->pszUnitType ); + CSLDestroy( psPam->papszCategoryNames ); + + if( psPam->poDefaultRAT != NULL ) + { + delete psPam->poDefaultRAT; + psPam->poDefaultRAT = NULL; + } + + if (psPam->psSavedHistograms != NULL) + { + CPLDestroyXMLNode (psPam->psSavedHistograms ); + psPam->psSavedHistograms = NULL; + } + + CPLFree( psPam ); + psPam = NULL; + } +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::XMLInit( CPLXMLNode *psTree, const char *pszUnused ) + +{ + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Apply any dataset level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Collect various other items of metadata. */ +/* -------------------------------------------------------------------- */ + GDALMajorObject::SetDescription( CPLGetXMLValue( psTree, "Description", "" ) ); + + if( CPLGetXMLValue( psTree, "NoDataValue", NULL ) != NULL ) + { + const char *pszLEHex = + CPLGetXMLValue( psTree, "NoDataValue.le_hex_equiv", NULL ); + if( pszLEHex != NULL ) + { + int nBytes; + GByte *pabyBin = CPLHexToBinary( pszLEHex, &nBytes ); + if( nBytes == 8 ) + { + CPL_LSBPTR64( pabyBin ); + + GDALPamRasterBand::SetNoDataValue( *((double *) pabyBin) ); + } + else + { + GDALPamRasterBand::SetNoDataValue( + atof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) ); + } + CPLFree( pabyBin ); + } + else + { + GDALPamRasterBand::SetNoDataValue( + atof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) ); + } + } + + GDALPamRasterBand::SetOffset( + atof(CPLGetXMLValue( psTree, "Offset", "0.0" )) ); + GDALPamRasterBand::SetScale( + atof(CPLGetXMLValue( psTree, "Scale", "1.0" )) ); + + GDALPamRasterBand::SetUnitType( CPLGetXMLValue( psTree, "UnitType", NULL)); + + if( CPLGetXMLValue( psTree, "ColorInterp", NULL ) != NULL ) + { + const char *pszInterp = CPLGetXMLValue( psTree, "ColorInterp", NULL ); + GDALPamRasterBand::SetColorInterpretation( + GDALGetColorInterpretationByName(pszInterp)); + } + +/* -------------------------------------------------------------------- */ +/* Category names. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "CategoryNames" ) != NULL ) + { + CPLXMLNode *psEntry; + CPLStringList oCategoryNames; + + for( psEntry = CPLGetXMLNode( psTree, "CategoryNames" )->psChild; + psEntry != NULL; psEntry = psEntry->psNext ) + { + /* Don't skeep tag with empty content */ + if( psEntry->eType != CXT_Element + || !EQUAL(psEntry->pszValue,"Category") + || (psEntry->psChild != NULL && psEntry->psChild->eType != CXT_Text) ) + continue; + + oCategoryNames.AddString( + (psEntry->psChild) ? psEntry->psChild->pszValue : "" ); + } + + GDALPamRasterBand::SetCategoryNames( oCategoryNames.List() ); + } + +/* -------------------------------------------------------------------- */ +/* Collect a color table. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "ColorTable" ) != NULL ) + { + CPLXMLNode *psEntry; + GDALColorTable oTable; + int iEntry = 0; + + for( psEntry = CPLGetXMLNode( psTree, "ColorTable" )->psChild; + psEntry != NULL; psEntry = psEntry->psNext ) + { + GDALColorEntry sCEntry; + + sCEntry.c1 = (short) atoi(CPLGetXMLValue( psEntry, "c1", "0" )); + sCEntry.c2 = (short) atoi(CPLGetXMLValue( psEntry, "c2", "0" )); + sCEntry.c3 = (short) atoi(CPLGetXMLValue( psEntry, "c3", "0" )); + sCEntry.c4 = (short) atoi(CPLGetXMLValue( psEntry, "c4", "255" )); + + oTable.SetColorEntry( iEntry++, &sCEntry ); + } + + GDALPamRasterBand::SetColorTable( &oTable ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a complete set of stats? */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "Minimum" ) != NULL + && CPLGetXMLNode( psTree, "Maximum" ) != NULL ) + { + psPam->bHaveMinMax = TRUE; + psPam->dfMin = atof(CPLGetXMLValue(psTree, "Minimum","0")); + psPam->dfMax = atof(CPLGetXMLValue(psTree, "Maximum","0")); + } + + if( CPLGetXMLNode( psTree, "Mean" ) != NULL + && CPLGetXMLNode( psTree, "StandardDeviation" ) != NULL ) + { + psPam->bHaveStats = TRUE; + psPam->dfMean = atof(CPLGetXMLValue(psTree, "Mean","0")); + psPam->dfStdDev = atof(CPLGetXMLValue(psTree,"StandardDeviation","0")); + } + +/* -------------------------------------------------------------------- */ +/* Histograms */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHist = CPLGetXMLNode( psTree, "Histograms" ); + if( psHist != NULL ) + { + CPLXMLNode *psNext = psHist->psNext; + psHist->psNext = NULL; + + if (psPam->psSavedHistograms != NULL) + { + CPLDestroyXMLNode (psPam->psSavedHistograms ); + psPam->psSavedHistograms = NULL; + } + psPam->psSavedHistograms = CPLCloneXMLTree( psHist ); + psHist->psNext = psNext; + } + +/* -------------------------------------------------------------------- */ +/* Raster Attribute Table */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRAT = CPLGetXMLNode( psTree, "GDALRasterAttributeTable" ); + if( psRAT != NULL ) + { + if( psPam->poDefaultRAT != NULL ) + { + delete psPam->poDefaultRAT; + psPam->poDefaultRAT = NULL; + } + psPam->poDefaultRAT = new GDALDefaultRasterAttributeTable(); + psPam->poDefaultRAT->XMLInit( psRAT, "" ); + } + + return CE_None; +} + +/************************************************************************/ +/* CloneInfo() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::CloneInfo( GDALRasterBand *poSrcBand, + int nCloneFlags ) + +{ + int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING; + int bSuccess; + int nSavedMOFlags = GetMOFlags(); + + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Supress NotImplemented error messages - mainly needed if PAM */ +/* disabled. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED ); + +/* -------------------------------------------------------------------- */ +/* Metadata */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_BAND_METADATA ) + { + if( poSrcBand->GetMetadata() != NULL ) + { + if( !bOnlyIfMissing + || CSLCount(GetMetadata()) != CSLCount(poSrcBand->GetMetadata()) ) + { + SetMetadata( poSrcBand->GetMetadata() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Band description. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_BAND_DESCRIPTION ) + { + if( strlen(poSrcBand->GetDescription()) > 0 ) + { + if( !bOnlyIfMissing || strlen(GetDescription()) == 0 ) + GDALPamRasterBand::SetDescription( poSrcBand->GetDescription()); + } + } + +/* -------------------------------------------------------------------- */ +/* NODATA */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_NODATA ) + { + double dfNoData = poSrcBand->GetNoDataValue( &bSuccess ); + + if( bSuccess ) + { + if( !bOnlyIfMissing + || GetNoDataValue( &bSuccess ) != dfNoData + || !bSuccess ) + GDALPamRasterBand::SetNoDataValue( dfNoData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Category names */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_CATEGORYNAMES ) + { + if( poSrcBand->GetCategoryNames() != NULL ) + { + if( !bOnlyIfMissing || GetCategoryNames() == NULL ) + GDALPamRasterBand::SetCategoryNames( poSrcBand->GetCategoryNames() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Offset/scale */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_SCALEOFFSET ) + { + double dfOffset = poSrcBand->GetOffset( &bSuccess ); + + if( bSuccess ) + { + if( !bOnlyIfMissing || GetOffset() != dfOffset ) + GDALPamRasterBand::SetOffset( dfOffset ); + } + + double dfScale = poSrcBand->GetScale( &bSuccess ); + + if( bSuccess ) + { + if( !bOnlyIfMissing || GetScale() != dfScale ) + GDALPamRasterBand::SetScale( dfScale ); + } + } + +/* -------------------------------------------------------------------- */ +/* Unittype. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_UNITTYPE ) + { + if( strlen(poSrcBand->GetUnitType()) > 0 ) + { + if( !bOnlyIfMissing + || !EQUAL(GetUnitType(),poSrcBand->GetUnitType()) ) + { + GDALPamRasterBand::SetUnitType( poSrcBand->GetUnitType() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* ColorInterp */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_COLORINTERP ) + { + if( poSrcBand->GetColorInterpretation() != GCI_Undefined ) + { + if( !bOnlyIfMissing + || poSrcBand->GetColorInterpretation() + != GetColorInterpretation() ) + GDALPamRasterBand::SetColorInterpretation( + poSrcBand->GetColorInterpretation() ); + } + } + +/* -------------------------------------------------------------------- */ +/* color table. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_COLORTABLE ) + { + if( poSrcBand->GetColorTable() != NULL ) + { + if( !bOnlyIfMissing || GetColorTable() == NULL ) + { + GDALPamRasterBand::SetColorTable( + poSrcBand->GetColorTable() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Raster Attribute Table. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_RAT ) + { + const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT(); + + if( poRAT != NULL ) + { + if( !bOnlyIfMissing || GetDefaultRAT() == NULL ) + { + GDALPamRasterBand::SetDefaultRAT( poRAT ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Restore MO flags. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags ); + + return CE_None; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + psPam->poParentDS->MarkPamDirty(); + + return GDALRasterBand::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + psPam->poParentDS->MarkPamDirty(); + + return GDALRasterBand::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetNoDataValue( double dfNewValue ) + +{ + PamInitialize(); + + if( psPam ) + { + psPam->bNoDataValueSet = TRUE; + psPam->dfNoDataValue = dfNewValue; + psPam->poParentDS->MarkPamDirty(); + return CE_None; + } + else + return GDALRasterBand::SetNoDataValue( dfNewValue ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GDALPamRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( psPam != NULL ) + { + if( pbSuccess ) + *pbSuccess = psPam->bNoDataValueSet; + + return psPam->dfNoDataValue; + } + else + return GDALRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double GDALPamRasterBand::GetOffset( int *pbSuccess ) + +{ + if( psPam ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return psPam->dfOffset; + } + else + return GDALRasterBand::GetOffset( pbSuccess ); +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetOffset( double dfNewOffset ) + +{ + PamInitialize(); + + if( psPam != NULL ) + { + if( psPam->dfOffset != dfNewOffset ) + { + psPam->dfOffset = dfNewOffset; + psPam->poParentDS->MarkPamDirty(); + } + + return CE_None; + } + else + return GDALRasterBand::SetOffset( dfNewOffset ); +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double GDALPamRasterBand::GetScale( int *pbSuccess ) + +{ + if( psPam ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return psPam->dfScale; + } + else + return GDALRasterBand::GetScale( pbSuccess ); +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetScale( double dfNewScale ) + +{ + PamInitialize(); + + if( psPam != NULL ) + { + if( dfNewScale != psPam->dfScale ) + { + psPam->dfScale = dfNewScale; + psPam->poParentDS->MarkPamDirty(); + } + return CE_None; + } + else + return GDALRasterBand::SetScale( dfNewScale ); +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *GDALPamRasterBand::GetUnitType() + +{ + if( psPam != NULL ) + { + if( psPam->pszUnitType == NULL ) + return ""; + else + return psPam->pszUnitType; + } + else + return GDALRasterBand::GetUnitType(); +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetUnitType( const char *pszNewValue ) + +{ + PamInitialize(); + + if( psPam ) + { + if( pszNewValue == NULL || pszNewValue[0] == '\0' ) + { + if (psPam->pszUnitType != NULL) + psPam->poParentDS->MarkPamDirty(); + CPLFree( psPam->pszUnitType ); + psPam->pszUnitType = NULL; + } + else + { + if (psPam->pszUnitType == NULL || + strcmp(psPam->pszUnitType, pszNewValue) != 0) + psPam->poParentDS->MarkPamDirty(); + CPLFree( psPam->pszUnitType ); + psPam->pszUnitType = CPLStrdup(pszNewValue); + } + + return CE_None; + } + else + return GDALRasterBand::SetUnitType( pszNewValue ); +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **GDALPamRasterBand::GetCategoryNames() + +{ + if( psPam ) + return psPam->papszCategoryNames; + else + return GDALRasterBand::GetCategoryNames(); +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetCategoryNames( char ** papszNewNames ) + +{ + PamInitialize(); + + if( psPam ) + { + CSLDestroy( psPam->papszCategoryNames ); + psPam->papszCategoryNames = CSLDuplicate( papszNewNames ); + psPam->poParentDS->MarkPamDirty(); + return CE_None; + } + else + return GDALRasterBand::SetCategoryNames( papszNewNames ); + +} + + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GDALPamRasterBand::GetColorTable() + +{ + if( psPam ) + return psPam->poColorTable; + else + return GDALRasterBand::GetColorTable(); +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetColorTable( GDALColorTable *poTableIn ) + +{ + PamInitialize(); + + if( psPam ) + { + if( psPam->poColorTable != NULL ) + { + delete psPam->poColorTable; + psPam->poColorTable = NULL; + } + + if( poTableIn ) + { + psPam->poColorTable = poTableIn->Clone(); + psPam->eColorInterp = GCI_PaletteIndex; + } + + psPam->poParentDS->MarkPamDirty(); + + return CE_None; + } + else + return GDALRasterBand::SetColorTable( poTableIn ); + +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetColorInterpretation( GDALColorInterp eInterpIn ) + +{ + PamInitialize(); + + if( psPam ) + { + psPam->poParentDS->MarkPamDirty(); + + psPam->eColorInterp = eInterpIn; + + return CE_None; + } + else + return GDALRasterBand::SetColorInterpretation( eInterpIn ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GDALPamRasterBand::GetColorInterpretation() + +{ + if( psPam ) + return psPam->eColorInterp; + else + return GDALRasterBand::GetColorInterpretation(); +} + +/************************************************************************/ +/* SetDescription() */ +/* */ +/* We let the GDALMajorObject hold the description, but we keep */ +/* track of whether it has been changed so we know to save it. */ +/************************************************************************/ + +void GDALPamRasterBand::SetDescription( const char *pszDescription ) + +{ + PamInitialize(); + + if( psPam && strcmp(pszDescription,GetDescription()) != 0 ) + psPam->poParentDS->MarkPamDirty(); + + GDALRasterBand::SetDescription( pszDescription ); +} + +/************************************************************************/ +/* PamParseHistogram() */ +/************************************************************************/ + +int +PamParseHistogram( CPLXMLNode *psHistItem, + double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int *pbIncludeOutOfRange, int *pbApproxOK ) + +{ + if( psHistItem == NULL ) + return FALSE; + + *pdfMin = atof(CPLGetXMLValue( psHistItem, "HistMin", "0")); + *pdfMax = atof(CPLGetXMLValue( psHistItem, "HistMax", "1")); + *pnBuckets = atoi(CPLGetXMLValue( psHistItem, "BucketCount","2")); + if (*pnBuckets <= 0 || *pnBuckets > INT_MAX / 2) + return FALSE; + + if( ppanHistogram == NULL ) + return TRUE; + + // Fetch the histogram and use it. + int iBucket; + const char *pszHistCounts = CPLGetXMLValue( psHistItem, + "HistCounts", "" ); + + /* Sanity check to test consistency of BucketCount and HistCounts */ + if( strlen(pszHistCounts) < 2 * (size_t)(*pnBuckets) -1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "HistCounts content isn't consistant with BucketCount value"); + return FALSE; + } + + *ppanHistogram = (int *) VSICalloc(sizeof(int),*pnBuckets); + if (*ppanHistogram == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate memory for %d buckets", *pnBuckets); + return FALSE; + } + + for( iBucket = 0; iBucket < *pnBuckets; iBucket++ ) + { + (*ppanHistogram)[iBucket] = atoi(pszHistCounts); + + // skip to next number. + while( *pszHistCounts != '\0' && *pszHistCounts != '|' ) + pszHistCounts++; + if( *pszHistCounts == '|' ) + pszHistCounts++; + } + + return TRUE; +} + +/************************************************************************/ +/* PamFindMatchingHistogram() */ +/************************************************************************/ +CPLXMLNode * +PamFindMatchingHistogram( CPLXMLNode *psSavedHistograms, + double dfMin, double dfMax, int nBuckets, + int bIncludeOutOfRange, int bApproxOK ) + +{ + if( psSavedHistograms == NULL ) + return NULL; + + CPLXMLNode *psXMLHist; + for( psXMLHist = psSavedHistograms->psChild; + psXMLHist != NULL; psXMLHist = psXMLHist->psNext ) + { + if( psXMLHist->eType != CXT_Element + || !EQUAL(psXMLHist->pszValue,"HistItem") ) + continue; + + double dfHistMin = atof(CPLGetXMLValue( psXMLHist, "HistMin", "0")); + double dfHistMax = atof(CPLGetXMLValue( psXMLHist, "HistMax", "0")); + + if( !(ARE_REAL_EQUAL(dfHistMin, dfMin)) + || !(ARE_REAL_EQUAL(dfHistMax, dfMax)) + || atoi(CPLGetXMLValue( psXMLHist, + "BucketCount","0")) != nBuckets + || !atoi(CPLGetXMLValue( psXMLHist, + "IncludeOutOfRange","0")) != !bIncludeOutOfRange + || (!bApproxOK && atoi(CPLGetXMLValue( psXMLHist, + "Approximate","0"))) ) + + continue; + + return psXMLHist; + } + + return NULL; +} + +/************************************************************************/ +/* PamHistogramToXMLTree() */ +/************************************************************************/ + +CPLXMLNode * +PamHistogramToXMLTree( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApprox ) + +{ + char *pszHistCounts; + int iBucket, iHistOffset; + CPLXMLNode *psXMLHist; + CPLString oFmt; + + if( nBuckets > (INT_MAX - 10) / 12 ) + return NULL; + + pszHistCounts = (char *) VSIMalloc(12 * nBuckets + 10); + if( pszHistCounts == NULL ) + return NULL; + + psXMLHist = CPLCreateXMLNode( NULL, CXT_Element, "HistItem" ); + + CPLSetXMLValue( psXMLHist, "HistMin", + oFmt.Printf( "%.16g", dfMin )); + CPLSetXMLValue( psXMLHist, "HistMax", + oFmt.Printf( "%.16g", dfMax )); + CPLSetXMLValue( psXMLHist, "BucketCount", + oFmt.Printf( "%d", nBuckets )); + CPLSetXMLValue( psXMLHist, "IncludeOutOfRange", + oFmt.Printf( "%d", bIncludeOutOfRange )); + CPLSetXMLValue( psXMLHist, "Approximate", + oFmt.Printf( "%d", bApprox )); + + iHistOffset = 0; + pszHistCounts[0] = '\0'; + for( iBucket = 0; iBucket < nBuckets; iBucket++ ) + { + sprintf( pszHistCounts + iHistOffset, "%d", panHistogram[iBucket] ); + if( iBucket < nBuckets-1 ) + strcat( pszHistCounts + iHistOffset, "|" ); + iHistOffset += strlen(pszHistCounts+iHistOffset); + } + + CPLSetXMLValue( psXMLHist, "HistCounts", pszHistCounts ); + CPLFree( pszHistCounts ); + + return psXMLHist; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Check if we have a matching histogram. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamFindMatchingHistogram( psPam->psSavedHistograms, + dfMin, dfMax, nBuckets, + bIncludeOutOfRange, bApproxOK ); + if( psHistItem != NULL ) + { + int *panTempHist = NULL; + + if( PamParseHistogram( psHistItem, &dfMin, &dfMax, &nBuckets, + &panTempHist, + &bIncludeOutOfRange, &bApproxOK ) ) + { + memcpy( panHistogram, panTempHist, sizeof(int) * nBuckets ); + CPLFree( panTempHist ); + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* We don't have an existing histogram matching the request, so */ +/* generate one manually. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + eErr = GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Save an XML description of this histogram. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None ) + { + CPLXMLNode *psXMLHist; + + psXMLHist = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK ); + if( psXMLHist != NULL ) + { + psPam->poParentDS->MarkPamDirty(); + + if( psPam->psSavedHistograms == NULL ) + psPam->psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + CPLAddXMLChild( psPam->psSavedHistograms, psXMLHist ); + } + } + + return eErr; +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram) + +{ + CPLXMLNode *psNode; + + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::SetDefaultHistogram( dfMin, dfMax, + nBuckets, panHistogram ); + +/* -------------------------------------------------------------------- */ +/* Do we have a matching histogram we should replace? */ +/* -------------------------------------------------------------------- */ + psNode = PamFindMatchingHistogram( psPam->psSavedHistograms, + dfMin, dfMax, nBuckets, + TRUE, TRUE ); + if( psNode != NULL ) + { + /* blow this one away */ + CPLRemoveXMLChild( psPam->psSavedHistograms, psNode ); + CPLDestroyXMLNode( psNode ); + } + +/* -------------------------------------------------------------------- */ +/* Translate into a histogram XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, TRUE, FALSE ); + if( psHistItem == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Insert our new default histogram at the front of the */ +/* histogram list so that it will be the default histogram. */ +/* -------------------------------------------------------------------- */ + psPam->poParentDS->MarkPamDirty(); + + if( psPam->psSavedHistograms == NULL ) + psPam->psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + psHistItem->psNext = psPam->psSavedHistograms->psChild; + psPam->psSavedHistograms->psChild = psHistItem; + + return CE_None; +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr +GDALPamRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( psPam && psPam->psSavedHistograms != NULL ) + { + CPLXMLNode *psXMLHist; + + for( psXMLHist = psPam->psSavedHistograms->psChild; + psXMLHist != NULL; psXMLHist = psXMLHist->psNext ) + { + int bApprox, bIncludeOutOfRange; + + if( psXMLHist->eType != CXT_Element + || !EQUAL(psXMLHist->pszValue,"HistItem") ) + continue; + + if( PamParseHistogram( psXMLHist, pdfMin, pdfMax, pnBuckets, + ppanHistogram, &bIncludeOutOfRange, + &bApprox ) ) + return CE_None; + else + return CE_Failure; + } + } + + return GDALRasterBand::GetDefaultHistogram( pdfMin, pdfMax, pnBuckets, + ppanHistogram, bForce, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *GDALPamRasterBand::GetDefaultRAT() + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::GetDefaultRAT(); + + return psPam->poDefaultRAT; +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetDefaultRAT( const GDALRasterAttributeTable *poRAT) + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::SetDefaultRAT( poRAT ); + + psPam->poParentDS->MarkPamDirty(); + + if( psPam->poDefaultRAT != NULL ) + { + delete psPam->poDefaultRAT; + psPam->poDefaultRAT = NULL; + } + + if( poRAT == NULL ) + psPam->poDefaultRAT = NULL; + else + psPam->poDefaultRAT = poRAT->Clone(); + + return CE_None; +} + diff --git a/ogr/gdalproxydataset.cpp b/ogr/gdalproxydataset.cpp new file mode 100644 index 0000000..700aab2 --- /dev/null +++ b/ogr/gdalproxydataset.cpp @@ -0,0 +1,303 @@ +/****************************************************************************** + * $Id: gdalproxydataset.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: A dataset and raster band classes that act as proxy for underlying + * GDALDataset* and GDALRasterBand* + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_proxy.h" + +CPL_CVSID("$Id: gdalproxydataset.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/* ******************************************************************** */ +/* GDALProxyDataset */ +/* ******************************************************************** */ + +#define D_PROXY_METHOD_WITH_RET(retType, retErrValue, methodName, argList, argParams) \ +retType GDALProxyDataset::methodName argList \ +{ \ + retType ret; \ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); \ + if (poUnderlyingDataset) \ + { \ + ret = poUnderlyingDataset->methodName argParams; \ + UnrefUnderlyingDataset(poUnderlyingDataset); \ + } \ + else \ + { \ + ret = retErrValue; \ + } \ + return ret; \ +} + + +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, IRasterIO, + ( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace), + ( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace )) + + +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, IBuildOverviews, + ( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ), + ( pszResampling, nOverviews, panOverviewList, + nListBands, panBandList, pfnProgress, pProgressData )) + +void GDALProxyDataset::FlushCache() +{ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset) + { + poUnderlyingDataset->FlushCache(); + UnrefUnderlyingDataset(poUnderlyingDataset); + } +} + +D_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadataDomainList, (), ()) +D_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadata, (const char * pszDomain), (pszDomain)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadata, + (char ** papszMetadata, const char * pszDomain), + (papszMetadata, pszDomain)) +D_PROXY_METHOD_WITH_RET(const char*, NULL, GetMetadataItem, + (const char * pszName, const char * pszDomain), + (pszName, pszDomain)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadataItem, + (const char * pszName, const char * pszValue, const char * pszDomain), + (pszName, pszValue, pszDomain)) + +D_PROXY_METHOD_WITH_RET(const char *, NULL, GetProjectionRef, (), ()) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetProjection, (const char* pszProjection), (pszProjection)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetGeoTransform, (double* padfGeoTransform), (padfGeoTransform)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetGeoTransform, (double* padfGeoTransform), (padfGeoTransform)) + +D_PROXY_METHOD_WITH_RET(void *, NULL, GetInternalHandle, ( const char * arg1), (arg1)) +D_PROXY_METHOD_WITH_RET(GDALDriver *, NULL, GetDriver, (), ()) +D_PROXY_METHOD_WITH_RET(char **, NULL, GetFileList, (), ()) +D_PROXY_METHOD_WITH_RET(int, 0, GetGCPCount, (), ()) +D_PROXY_METHOD_WITH_RET(const char *, NULL, GetGCPProjection, (), ()) +D_PROXY_METHOD_WITH_RET(const GDAL_GCP *, NULL, GetGCPs, (), ()) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetGCPs, + (int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection), + (nGCPCount, pasGCPList, pszGCPProjection)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, AdviseRead, + ( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ), + (nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, eDT, nBandCount, panBandList, papszOptions)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, CreateMaskBand, ( int nFlags ), (nFlags)) + +/************************************************************************/ +/* UnrefUnderlyingDataset() */ +/************************************************************************/ + +void GDALProxyDataset::UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset) +{ +} + +/* ******************************************************************** */ +/* GDALProxyRasterBand */ +/* ******************************************************************** */ + + +#define RB_PROXY_METHOD_WITH_RET(retType, retErrValue, methodName, argList, argParams) \ +retType GDALProxyRasterBand::methodName argList \ +{ \ + retType ret; \ + GDALRasterBand* poSrcBand = RefUnderlyingRasterBand(); \ + if (poSrcBand) \ + { \ + ret = poSrcBand->methodName argParams; \ + UnrefUnderlyingRasterBand(poSrcBand); \ + } \ + else \ + { \ + ret = retErrValue; \ + } \ + return ret; \ +} + + +#define RB_PROXY_METHOD_WITH_RET_WITH_INIT_BLOCK(retType, retErrValue, methodName, argList, argParams) \ +retType GDALProxyRasterBand::methodName argList \ +{ \ + retType ret; \ + GDALRasterBand* poSrcBand = RefUnderlyingRasterBand(); \ + if (poSrcBand) \ + { \ + if( !poSrcBand->InitBlockInfo() ) \ + ret = CE_Failure; \ + else \ + ret = poSrcBand->methodName argParams; \ + UnrefUnderlyingRasterBand(poSrcBand); \ + } \ + else \ + { \ + ret = retErrValue; \ + } \ + return ret; \ +} + +RB_PROXY_METHOD_WITH_RET_WITH_INIT_BLOCK(CPLErr, CE_Failure, IReadBlock, + ( int nXBlockOff, int nYBlockOff, void* pImage), + (nXBlockOff, nYBlockOff, pImage) ) +RB_PROXY_METHOD_WITH_RET_WITH_INIT_BLOCK(CPLErr, CE_Failure, IWriteBlock, + ( int nXBlockOff, int nYBlockOff, void* pImage), + (nXBlockOff, nYBlockOff, pImage) ) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, IRasterIO, + ( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + int nLineSpace ), + (eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace ) ) + +RB_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadataDomainList, (), ()) +RB_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadata, (const char * pszDomain), (pszDomain)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadata, + (char ** papszMetadata, const char * pszDomain), + (papszMetadata, pszDomain)) +RB_PROXY_METHOD_WITH_RET(const char*, NULL, GetMetadataItem, + (const char * pszName, const char * pszDomain), + (pszName, pszDomain)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadataItem, + (const char * pszName, const char * pszValue, const char * pszDomain), + (pszName, pszValue, pszDomain)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, FlushCache, (), ()) +RB_PROXY_METHOD_WITH_RET(char**, NULL, GetCategoryNames, (), ()) +RB_PROXY_METHOD_WITH_RET(double, 0, GetNoDataValue, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetMinimum, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetMaximum, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetOffset, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetScale, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(const char*, NULL, GetUnitType, (), ()) +RB_PROXY_METHOD_WITH_RET(GDALColorInterp, GCI_Undefined, GetColorInterpretation, (), ()) +RB_PROXY_METHOD_WITH_RET(GDALColorTable*, NULL, GetColorTable, (), ()) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, Fill, + (double dfRealValue, double dfImaginaryValue), + (dfRealValue, dfImaginaryValue)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetCategoryNames, ( char ** arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetNoDataValue, ( double arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetColorTable, ( GDALColorTable *arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetColorInterpretation, + ( GDALColorInterp arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetOffset, ( double arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetScale, ( double arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetUnitType, ( const char * arg ), (arg)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetStatistics, + ( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ), + (bApproxOK, bForce, pdfMin, pdfMax, pdfMean, padfStdDev)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, ComputeStatistics, + ( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfn, void *pProgressData ), + ( bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfn, pProgressData)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetStatistics, + ( double dfMin, double dfMax, + double dfMean, double dfStdDev ), + (dfMin, dfMax, dfMean, dfStdDev)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, ComputeRasterMinMax, + ( int arg1, double* arg2 ), (arg1, arg2)) + +RB_PROXY_METHOD_WITH_RET(int, 0, HasArbitraryOverviews, (), ()) +RB_PROXY_METHOD_WITH_RET(int, 0, GetOverviewCount, (), ()) +RB_PROXY_METHOD_WITH_RET(GDALRasterBand*, NULL, GetOverview, (int arg1), (arg1)) +RB_PROXY_METHOD_WITH_RET(GDALRasterBand*, NULL, GetRasterSampleOverview, + (int arg1), (arg1)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, BuildOverviews, + (const char * arg1, int arg2, int *arg3, + GDALProgressFunc arg4, void * arg5), + (arg1, arg2, arg3, arg4, arg5)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, AdviseRead, + ( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ), + (nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, eDT, papszOptions)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetHistogram, + ( double dfMin, double dfMax, + int nBuckets, int * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfn, void *pProgressData ), + (dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, + bApproxOK, pfn, pProgressData)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetDefaultHistogram, + (double *pdfMin, double *pdfMax, + int *pnBuckets, int ** ppanHistogram, + int bForce, + GDALProgressFunc pfn, void *pProgressData ), + (pdfMin, pdfMax, pnBuckets, ppanHistogram, bForce, + pfn, pProgressData)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetDefaultHistogram, + ( double dfMin, double dfMax, + int nBuckets, int * panHistogram ), + (dfMin, dfMax, nBuckets, panHistogram)) + +RB_PROXY_METHOD_WITH_RET(GDALRasterAttributeTable *, NULL, + GetDefaultRAT, (), ()) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetDefaultRAT, + ( const GDALRasterAttributeTable * arg1), (arg1)) + +RB_PROXY_METHOD_WITH_RET(GDALRasterBand*, NULL, GetMaskBand, (), ()) +RB_PROXY_METHOD_WITH_RET(int, 0, GetMaskFlags, (), ()) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, CreateMaskBand, ( int nFlags ), (nFlags)) + +RB_PROXY_METHOD_WITH_RET(CPLVirtualMem*, NULL, GetVirtualMemAuto, + ( GDALRWFlag eRWFlag, int *pnPixelSpace, GIntBig *pnLineSpace, char **papszOptions ), + (eRWFlag, pnPixelSpace, pnLineSpace, papszOptions) ) + +/************************************************************************/ +/* UnrefUnderlyingRasterBand() */ +/************************************************************************/ + +void GDALProxyRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand) +{ +} diff --git a/ogr/gdalproxypool.cpp b/ogr/gdalproxypool.cpp new file mode 100644 index 0000000..c6cb12a --- /dev/null +++ b/ogr/gdalproxypool.cpp @@ -0,0 +1,1257 @@ +/****************************************************************************** + * $Id: gdalproxypool.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: A dataset and raster band classes that differ the opening of the + * underlying dataset in a limited pool of opened datasets. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_proxy.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdalproxypool.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/* We *must* share the same mutex as the gdaldataset.cpp file, as we are */ +/* doing GDALOpen() calls that can indirectly call GDALOpenShared() on */ +/* an auxiliary dataset ... */ +/* Then we could get dead-locks in multi-threaded use case */ + +/* ******************************************************************** */ +/* GDALDatasetPool */ +/* ******************************************************************** */ + +/* This class is a singleton that maintains a pool of opened datasets */ +/* The cache uses a LRU strategy */ + +class GDALDatasetPool; +static GDALDatasetPool* singleton = NULL; + +void GDALNullifyProxyPoolSingleton() { singleton = NULL; } + +struct _GDALProxyPoolCacheEntry +{ + GIntBig responsiblePID; + char *pszFileName; + GDALDataset *poDS; + + /* Ref count of the cached dataset */ + int refCount; + + GDALProxyPoolCacheEntry* prev; + GDALProxyPoolCacheEntry* next; +}; + +class GDALDatasetPool +{ + private: + /* Ref count of the pool singleton */ + /* Taken by "toplevel" GDALProxyPoolDataset in its constructor and released */ + /* in its destructor. See also refCountOfDisableRefCount for the difference */ + /* between toplevel and inner GDALProxyPoolDataset */ + int refCount; + + int maxSize; + int currentSize; + GDALProxyPoolCacheEntry* firstEntry; + GDALProxyPoolCacheEntry* lastEntry; + + /* This variable prevents a dataset that is going to be opened in GDALDatasetPool::_RefDataset */ + /* from increasing refCount if, during its opening, it creates a GDALProxyPoolDataset */ + /* We increment it before opening or closing a cached dataset and decrement it afterwards */ + /* The typical use case is a VRT made of simple sources that are VRT */ + /* We don't want the "inner" VRT to take a reference on the pool, otherwise there is */ + /* a high chance that this reference will not be dropped and the pool remain ghost */ + int refCountOfDisableRefCount; + + /* Caution : to be sure that we don't run out of entries, size must be at */ + /* least greater or equal than the maximum number of threads */ + GDALDatasetPool(int maxSize); + ~GDALDatasetPool(); + GDALProxyPoolCacheEntry* _RefDataset(const char* pszFileName, GDALAccess eAccess); + + void ShowContent(); + void CheckLinks(); + + public: + static void Ref(); + static void Unref(); + static GDALProxyPoolCacheEntry* RefDataset(const char* pszFileName, GDALAccess eAccess); + static void UnrefDataset(GDALProxyPoolCacheEntry* cacheEntry); + + static void PreventDestroy(); + static void ForceDestroy(); +}; + + +/************************************************************************/ +/* GDALDatasetPool() */ +/************************************************************************/ + +GDALDatasetPool::GDALDatasetPool(int maxSize) +{ + this->maxSize = maxSize; + currentSize = 0; + firstEntry = NULL; + lastEntry = NULL; + refCount = 0; + refCountOfDisableRefCount = 0; +} + +/************************************************************************/ +/* ~GDALDatasetPool() */ +/************************************************************************/ + +GDALDatasetPool::~GDALDatasetPool() +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + while(cur) + { + GDALProxyPoolCacheEntry* next = cur->next; + CPLFree(cur->pszFileName); + CPLAssert(cur->refCount == 0); + if (cur->poDS) + { + GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID); + GDALClose(cur->poDS); + } + CPLFree(cur); + cur = next; + } + GDALSetResponsiblePIDForCurrentThread(responsiblePID); +} + +/************************************************************************/ +/* ShowContent() */ +/************************************************************************/ + +void GDALDatasetPool::ShowContent() +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + int i = 0; + while(cur) + { + printf("[%d] pszFileName=%s, refCount=%d, responsiblePID=%d\n", + i, cur->pszFileName, cur->refCount, (int)cur->responsiblePID); + i++; + cur = cur->next; + } +} + +/************************************************************************/ +/* CheckLinks() */ +/************************************************************************/ + +void GDALDatasetPool::CheckLinks() +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + int i = 0; + while(cur) + { + CPLAssert(cur == firstEntry || cur->prev->next == cur); + CPLAssert(cur == lastEntry || cur->next->prev == cur); + i++; + CPLAssert(cur->next != NULL || cur == lastEntry); + cur = cur->next; + } + CPLAssert(i == currentSize); +} + +/************************************************************************/ +/* _RefDataset() */ +/************************************************************************/ + +GDALProxyPoolCacheEntry* GDALDatasetPool::_RefDataset(const char* pszFileName, GDALAccess eAccess) +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + GDALProxyPoolCacheEntry* lastEntryWithZeroRefCount = NULL; + + while(cur) + { + GDALProxyPoolCacheEntry* next = cur->next; + + if (strcmp(cur->pszFileName, pszFileName) == 0 && + cur->responsiblePID == responsiblePID) + { + if (cur != firstEntry) + { + /* Move to begin */ + if (cur->next) + cur->next->prev = cur->prev; + else + lastEntry = cur->prev; + cur->prev->next = cur->next; + cur->prev = NULL; + firstEntry->prev = cur; + cur->next = firstEntry; + firstEntry = cur; + +#ifdef DEBUG_PROXY_POOL + CheckLinks(); +#endif + } + + cur->refCount ++; + return cur; + } + + if (cur->refCount == 0) + lastEntryWithZeroRefCount = cur; + + cur = next; + } + + if (currentSize == maxSize) + { + if (lastEntryWithZeroRefCount == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many threads are running for the current value of the dataset pool size (%d).\n" + "or too many proxy datasets are opened in a cascaded way.\n" + "Try increasing GDAL_MAX_DATASET_POOL_SIZE.", maxSize); + return NULL; + } + + CPLFree(lastEntryWithZeroRefCount->pszFileName); + lastEntryWithZeroRefCount->pszFileName = NULL; + if (lastEntryWithZeroRefCount->poDS) + { + /* Close by pretending we are the thread that GDALOpen'ed this */ + /* dataset */ + GDALSetResponsiblePIDForCurrentThread(lastEntryWithZeroRefCount->responsiblePID); + + refCountOfDisableRefCount ++; + GDALClose(lastEntryWithZeroRefCount->poDS); + refCountOfDisableRefCount --; + + lastEntryWithZeroRefCount->poDS = NULL; + GDALSetResponsiblePIDForCurrentThread(responsiblePID); + } + + /* Recycle this entry for the to-be-openeded dataset and */ + /* moves it to the top of the list */ + if (lastEntryWithZeroRefCount->prev) + lastEntryWithZeroRefCount->prev->next = lastEntryWithZeroRefCount->next; + else + CPLAssert(0); + if (lastEntryWithZeroRefCount->next) + lastEntryWithZeroRefCount->next->prev = lastEntryWithZeroRefCount->prev; + else + { + CPLAssert(lastEntryWithZeroRefCount == lastEntry); + lastEntry->prev->next = NULL; + lastEntry = lastEntry->prev; + } + lastEntryWithZeroRefCount->prev = NULL; + lastEntryWithZeroRefCount->next = firstEntry; + firstEntry->prev = lastEntryWithZeroRefCount; + cur = firstEntry = lastEntryWithZeroRefCount; +#ifdef DEBUG_PROXY_POOL + CheckLinks(); +#endif + } + else + { + /* Prepend */ + cur = (GDALProxyPoolCacheEntry*) CPLMalloc(sizeof(GDALProxyPoolCacheEntry)); + if (lastEntry == NULL) + lastEntry = cur; + cur->prev = NULL; + cur->next = firstEntry; + if (firstEntry) + firstEntry->prev = cur; + firstEntry = cur; + currentSize ++; +#ifdef DEBUG_PROXY_POOL + CheckLinks(); +#endif + } + + cur->pszFileName = CPLStrdup(pszFileName); + cur->responsiblePID = responsiblePID; + cur->refCount = 1; + + refCountOfDisableRefCount ++; + cur->poDS = (GDALDataset*) GDALOpen(pszFileName, eAccess); + refCountOfDisableRefCount --; + + return cur; +} + +/************************************************************************/ +/* Ref() */ +/************************************************************************/ + +void GDALDatasetPool::Ref() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (singleton == NULL) + { + int maxSize = atoi(CPLGetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "100")); + if (maxSize < 2 || maxSize > 1000) + maxSize = 100; + singleton = new GDALDatasetPool(maxSize); + } + if (singleton->refCountOfDisableRefCount == 0) + singleton->refCount++; +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPool::PreventDestroy() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (! singleton) + return; + singleton->refCountOfDisableRefCount ++; +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPoolPreventDestroy() +{ + GDALDatasetPool::PreventDestroy(); +} + + +/************************************************************************/ +/* Unref() */ +/************************************************************************/ + +void GDALDatasetPool::Unref() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (! singleton) + { + CPLAssert(0); + return; + } + if (singleton->refCountOfDisableRefCount == 0) + { + singleton->refCount--; + if (singleton->refCount == 0) + { + delete singleton; + singleton = NULL; + } + } +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPool::ForceDestroy() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (! singleton) + return; + singleton->refCountOfDisableRefCount --; + CPLAssert(singleton->refCountOfDisableRefCount == 0); + singleton->refCount = 0; + delete singleton; + singleton = NULL; +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPoolForceDestroy() +{ + GDALDatasetPool::ForceDestroy(); +} + +/************************************************************************/ +/* RefDataset() */ +/************************************************************************/ + +GDALProxyPoolCacheEntry* GDALDatasetPool::RefDataset(const char* pszFileName, GDALAccess eAccess) +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + return singleton->_RefDataset(pszFileName, eAccess); +} + +/************************************************************************/ +/* UnrefDataset() */ +/************************************************************************/ + +void GDALDatasetPool::UnrefDataset(GDALProxyPoolCacheEntry* cacheEntry) +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + cacheEntry->refCount --; +} + +CPL_C_START + +typedef struct +{ + char* pszDomain; + char** papszMetadata; +} GetMetadataElt; + +static +unsigned long hash_func_get_metadata(const void* _elt) +{ + GetMetadataElt* elt = (GetMetadataElt*) _elt; + return CPLHashSetHashStr(elt->pszDomain); +} + +static +int equal_func_get_metadata(const void* _elt1, const void* _elt2) +{ + GetMetadataElt* elt1 = (GetMetadataElt*) _elt1; + GetMetadataElt* elt2 = (GetMetadataElt*) _elt2; + return CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain); +} + +static +void free_func_get_metadata(void* _elt) +{ + GetMetadataElt* elt = (GetMetadataElt*) _elt; + CPLFree(elt->pszDomain); + CSLDestroy(elt->papszMetadata); +} + + +typedef struct +{ + char* pszName; + char* pszDomain; + char* pszMetadataItem; +} GetMetadataItemElt; + +static +unsigned long hash_func_get_metadata_item(const void* _elt) +{ + GetMetadataItemElt* elt = (GetMetadataItemElt*) _elt; + return CPLHashSetHashStr(elt->pszName) ^ CPLHashSetHashStr(elt->pszDomain); +} + +static +int equal_func_get_metadata_item(const void* _elt1, const void* _elt2) +{ + GetMetadataItemElt* elt1 = (GetMetadataItemElt*) _elt1; + GetMetadataItemElt* elt2 = (GetMetadataItemElt*) _elt2; + return CPLHashSetEqualStr(elt1->pszName, elt2->pszName) && + CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain); +} + +static +void free_func_get_metadata_item(void* _elt) +{ + GetMetadataItemElt* elt = (GetMetadataItemElt*) _elt; + CPLFree(elt->pszName); + CPLFree(elt->pszDomain); + CPLFree(elt->pszMetadataItem); +} + +CPL_C_END + +/* ******************************************************************** */ +/* GDALProxyPoolDataset */ +/* ******************************************************************** */ + +/* Note : the bShared parameter must be used with caution. You can */ +/* set it to TRUE for being used as a VRT source : in that case, */ +/* VRTSimpleSource will take care of destroying it when there are no */ +/* reference to it (in VRTSimpleSource::~VRTSimpleSource()) */ +/* However this will not be registered as a genuine shared dataset, like it */ +/* would have been with MarkAsShared(). But MarkAsShared() is not usable for */ +/* GDALProxyPoolDataset objects, as they share the same description as their */ +/* underlying dataset. So *NEVER* call MarkAsShared() on a GDALProxyPoolDataset */ +/* object */ + +GDALProxyPoolDataset::GDALProxyPoolDataset(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess, int bShared, + const char * pszProjectionRef, + double * padfGeoTransform) +{ + GDALDatasetPool::Ref(); + + SetDescription(pszSourceDatasetDescription); + + this->nRasterXSize = nRasterXSize; + this->nRasterYSize = nRasterYSize; + this->eAccess = eAccess; + + this->bShared = bShared; + + this->responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + + if (pszProjectionRef) + { + this->pszProjectionRef = NULL; + bHasSrcProjection = FALSE; + } + else + { + this->pszProjectionRef = CPLStrdup(pszProjectionRef); + bHasSrcProjection = TRUE; + } + if (padfGeoTransform) + { + memcpy(adfGeoTransform, padfGeoTransform,6 * sizeof(double)); + bHasSrcGeoTransform = TRUE; + } + else + { + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + bHasSrcGeoTransform = FALSE; + } + + pszGCPProjection = NULL; + nGCPCount = 0; + pasGCPList = NULL; + metadataSet = NULL; + metadataItemSet = NULL; + cacheEntry = NULL; +} + +/************************************************************************/ +/* ~GDALProxyPoolDataset() */ +/************************************************************************/ + +GDALProxyPoolDataset::~GDALProxyPoolDataset() +{ + /* See comment in constructor */ + /* It is not really a genuine shared dataset, so we don't */ + /* want ~GDALDataset() to try to release it from its */ + /* shared dataset hashset. This will save a */ + /* "Should not happen. Cannot find %s, this=%p in phSharedDatasetSet" debug message */ + bShared = FALSE; + + CPLFree(pszProjectionRef); + CPLFree(pszGCPProjection); + if (nGCPCount) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + if (metadataSet) + CPLHashSetDestroy(metadataSet); + if (metadataItemSet) + CPLHashSetDestroy(metadataItemSet); + + GDALDatasetPool::Unref(); +} + +/************************************************************************/ +/* AddSrcBandDescription() */ +/************************************************************************/ + +void GDALProxyPoolDataset::AddSrcBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize) +{ + SetBand(nBands + 1, new GDALProxyPoolRasterBand(this, nBands + 1, eDataType, nBlockXSize, nBlockYSize)); +} + +/************************************************************************/ +/* RefUnderlyingDataset() */ +/************************************************************************/ + +GDALDataset* GDALProxyPoolDataset::RefUnderlyingDataset() +{ + /* We pretend that the current thread is responsiblePID, that is */ + /* to say the thread that created that GDALProxyPoolDataset object. */ + /* This is for the case when a GDALProxyPoolDataset is created by a */ + /* thread and used by other threads. These other threads, when doing actual */ + /* IO, will come there and potentially open the underlying dataset. */ + /* By doing this, they can indirectly call GDALOpenShared() on .aux file */ + /* for example. So this call to GDALOpenShared() must occur as if it */ + /* was done by the creating thread, otherwise it will not be correctly closed afterwards... */ + /* To make a long story short : this is necessary when warping with ChunkAndWarpMulti */ + /* a VRT of GeoTIFFs that have associated .aux files */ + GIntBig curResponsiblePID = GDALGetResponsiblePIDForCurrentThread(); + GDALSetResponsiblePIDForCurrentThread(responsiblePID); + cacheEntry = GDALDatasetPool::RefDataset(GetDescription(), eAccess); + GDALSetResponsiblePIDForCurrentThread(curResponsiblePID); + if (cacheEntry != NULL) + { + if (cacheEntry->poDS != NULL) + return cacheEntry->poDS; + else + GDALDatasetPool::UnrefDataset(cacheEntry); + } + return NULL; +} + +/************************************************************************/ +/* UnrefUnderlyingDataset() */ +/************************************************************************/ + +void GDALProxyPoolDataset::UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset) +{ + if (cacheEntry != NULL) + { + CPLAssert(cacheEntry->poDS == poUnderlyingDataset); + if (cacheEntry->poDS != NULL) + GDALDatasetPool::UnrefDataset(cacheEntry); + } +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GDALProxyPoolDataset::SetProjection(const char* pszProjectionRef) +{ + bHasSrcProjection = FALSE; + return GDALProxyDataset::SetProjection(pszProjectionRef); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GDALProxyPoolDataset::GetProjectionRef() +{ + if (bHasSrcProjection) + return pszProjectionRef; + else + return GDALProxyDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALProxyPoolDataset::SetGeoTransform( double * padfGeoTransform ) +{ + bHasSrcGeoTransform = FALSE; + return GDALProxyDataset::SetGeoTransform(padfGeoTransform); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALProxyPoolDataset::GetGeoTransform( double * padfGeoTransform ) +{ + if (bHasSrcGeoTransform) + { + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; + } + else + { + return GDALProxyDataset::GetGeoTransform(padfGeoTransform); + } +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALProxyPoolDataset::GetMetadata( const char * pszDomain ) +{ + if (metadataSet == NULL) + metadataSet = CPLHashSetNew(hash_func_get_metadata, + equal_func_get_metadata, + free_func_get_metadata); + + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + char** papszUnderlyingMetadata = poUnderlyingDataset->GetMetadata(pszDomain); + + GetMetadataElt* pElt = (GetMetadataElt*) CPLMalloc(sizeof(GetMetadataElt)); + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata); + CPLHashSetInsert(metadataSet, pElt); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pElt->papszMetadata; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALProxyPoolDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (metadataItemSet == NULL) + metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item, + equal_func_get_metadata_item, + free_func_get_metadata_item); + + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + const char* pszUnderlyingMetadataItem = + poUnderlyingDataset->GetMetadataItem(pszName, pszDomain); + + GetMetadataItemElt* pElt = (GetMetadataItemElt*) CPLMalloc(sizeof(GetMetadataItemElt)); + pElt->pszName = (pszName) ? CPLStrdup(pszName) : NULL; + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->pszMetadataItem = (pszUnderlyingMetadataItem) ? CPLStrdup(pszUnderlyingMetadataItem) : NULL; + CPLHashSetInsert(metadataItemSet, pElt); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pElt->pszMetadataItem; +} + +/************************************************************************/ +/* GetInternalHandle() */ +/************************************************************************/ + +void *GDALProxyPoolDataset::GetInternalHandle( const char * pszRequest) +{ + CPLError(CE_Warning, CPLE_AppDefined, + "GetInternalHandle() cannot be safely called on a proxy pool dataset\n" + "as the returned value may be invalidated at any time.\n"); + return GDALProxyDataset::GetInternalHandle(pszRequest); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GDALProxyPoolDataset::GetGCPProjection() +{ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + CPLFree(pszGCPProjection); + pszGCPProjection = NULL; + + const char* pszUnderlyingGCPProjection = poUnderlyingDataset->GetGCPProjection(); + if (pszUnderlyingGCPProjection) + pszGCPProjection = CPLStrdup(pszUnderlyingGCPProjection); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GDALProxyPoolDataset::GetGCPs() +{ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + if (nGCPCount) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + pasGCPList = NULL; + } + + const GDAL_GCP* pasUnderlyingGCPList = poUnderlyingDataset->GetGCPs(); + nGCPCount = poUnderlyingDataset->GetGCPCount(); + if (nGCPCount) + pasGCPList = GDALDuplicateGCPs(nGCPCount, pasUnderlyingGCPList ); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pasGCPList; +} + +/************************************************************************/ +/* GDALProxyPoolDatasetCreate() */ +/************************************************************************/ + +GDALProxyPoolDatasetH GDALProxyPoolDatasetCreate(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess, int bShared, + const char * pszProjectionRef, + double * padfGeoTransform) +{ + return (GDALProxyPoolDatasetH) + new GDALProxyPoolDataset(pszSourceDatasetDescription, + nRasterXSize, nRasterYSize, + eAccess, bShared, + pszProjectionRef, padfGeoTransform); +} + +/************************************************************************/ +/* GDALProxyPoolDatasetDelete() */ +/************************************************************************/ + +void CPL_DLL GDALProxyPoolDatasetDelete(GDALProxyPoolDatasetH hProxyPoolDataset) +{ + delete (GDALProxyPoolDataset*)hProxyPoolDataset; +} + +/************************************************************************/ +/* GDALProxyPoolDatasetAddSrcBandDescription() */ +/************************************************************************/ + +void GDALProxyPoolDatasetAddSrcBandDescription( GDALProxyPoolDatasetH hProxyPoolDataset, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) +{ + ((GDALProxyPoolDataset*)hProxyPoolDataset)-> + AddSrcBandDescription(eDataType, nBlockXSize, nBlockYSize); +} + +/* ******************************************************************** */ +/* GDALProxyPoolRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eDataType; + this->nRasterXSize = poDS->GetRasterXSize(); + this->nRasterYSize = poDS->GetRasterYSize(); + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + + Init(); +} + +/* ******************************************************************** */ +/* GDALProxyPoolRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingRasterBand) +{ + this->poDS = poDS; + this->nBand = poUnderlyingRasterBand->GetBand(); + this->eDataType = poUnderlyingRasterBand->GetRasterDataType(); + this->nRasterXSize = poUnderlyingRasterBand->GetXSize(); + this->nRasterYSize = poUnderlyingRasterBand->GetYSize(); + poUnderlyingRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + + Init(); +} + +/* ******************************************************************** */ + /* Init() */ +/* ******************************************************************** */ + +void GDALProxyPoolRasterBand::Init() +{ + metadataSet = NULL; + metadataItemSet = NULL; + pszUnitType = NULL; + papszCategoryNames = NULL; + poColorTable = NULL; + + nSizeProxyOverviewRasterBand = 0; + papoProxyOverviewRasterBand = NULL; + poProxyMaskBand = NULL; +} + +/* ******************************************************************** */ +/* ~GDALProxyPoolRasterBand() */ +/* ******************************************************************** */ +GDALProxyPoolRasterBand::~GDALProxyPoolRasterBand() +{ + if (metadataSet) + CPLHashSetDestroy(metadataSet); + if (metadataItemSet) + CPLHashSetDestroy(metadataItemSet); + CPLFree(pszUnitType); + CSLDestroy(papszCategoryNames); + if (poColorTable) + delete poColorTable; + + int i; + for(i=0;iRefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + GDALRasterBand* poBand = poUnderlyingDataset->GetRasterBand(nBand); + if (poBand == NULL) + { + ((GDALProxyPoolDataset*)poDS)->UnrefUnderlyingDataset(poUnderlyingDataset); + } + + return poBand; +} + +/************************************************************************/ +/* UnrefUnderlyingRasterBand() */ +/************************************************************************/ + +void GDALProxyPoolRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand) +{ + if (poUnderlyingRasterBand) + ((GDALProxyPoolDataset*)poDS)->UnrefUnderlyingDataset(poUnderlyingRasterBand->GetDataset()); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALProxyPoolRasterBand::GetMetadata( const char * pszDomain ) +{ + if (metadataSet == NULL) + metadataSet = CPLHashSetNew(hash_func_get_metadata, + equal_func_get_metadata, + free_func_get_metadata); + + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + char** papszUnderlyingMetadata = poUnderlyingRasterBand->GetMetadata(pszDomain); + + GetMetadataElt* pElt = (GetMetadataElt*) CPLMalloc(sizeof(GetMetadataElt)); + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata); + CPLHashSetInsert(metadataSet, pElt); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return pElt->papszMetadata; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALProxyPoolRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (metadataItemSet == NULL) + metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item, + equal_func_get_metadata_item, + free_func_get_metadata_item); + + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + const char* pszUnderlyingMetadataItem = + poUnderlyingRasterBand->GetMetadataItem(pszName, pszDomain); + + GetMetadataItemElt* pElt = (GetMetadataItemElt*) CPLMalloc(sizeof(GetMetadataItemElt)); + pElt->pszName = (pszName) ? CPLStrdup(pszName) : NULL; + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->pszMetadataItem = (pszUnderlyingMetadataItem) ? CPLStrdup(pszUnderlyingMetadataItem) : NULL; + CPLHashSetInsert(metadataItemSet, pElt); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return pElt->pszMetadataItem; +} + +/* ******************************************************************** */ +/* GetCategoryNames() */ +/* ******************************************************************** */ + +char **GDALProxyPoolRasterBand::GetCategoryNames() +{ + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + CSLDestroy(papszCategoryNames); + papszCategoryNames = NULL; + + char** papszUnderlyingCategoryNames = poUnderlyingRasterBand->GetCategoryNames(); + if (papszUnderlyingCategoryNames) + papszCategoryNames = CSLDuplicate(papszUnderlyingCategoryNames); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return papszCategoryNames; +} + +/* ******************************************************************** */ +/* GetUnitType() */ +/* ******************************************************************** */ + +const char *GDALProxyPoolRasterBand::GetUnitType() +{ + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + CPLFree(pszUnitType); + pszUnitType = NULL; + + const char* pszUnderlyingUnitType = poUnderlyingRasterBand->GetUnitType(); + if (pszUnderlyingUnitType) + pszUnitType = CPLStrdup(pszUnderlyingUnitType); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return pszUnitType; +} + +/* ******************************************************************** */ +/* GetColorTable() */ +/* ******************************************************************** */ + +GDALColorTable *GDALProxyPoolRasterBand::GetColorTable() +{ + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + if (poColorTable) + delete poColorTable; + poColorTable = NULL; + + GDALColorTable* poUnderlyingColorTable = poUnderlyingRasterBand->GetColorTable(); + if (poUnderlyingColorTable) + poColorTable = poUnderlyingColorTable->Clone(); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return poColorTable; +} + +/* ******************************************************************** */ +/* GetOverview() */ +/* ******************************************************************** */ + +GDALRasterBand *GDALProxyPoolRasterBand::GetOverview(int nOverviewBand) +{ + if (nOverviewBand >= 0 && nOverviewBand < nSizeProxyOverviewRasterBand) + { + if (papoProxyOverviewRasterBand[nOverviewBand]) + return papoProxyOverviewRasterBand[nOverviewBand]; + } + + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + GDALRasterBand* poOverviewRasterBand = poUnderlyingRasterBand->GetOverview(nOverviewBand); + if (poOverviewRasterBand == NULL) + { + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + return NULL; + } + + if (nOverviewBand >= nSizeProxyOverviewRasterBand) + { + int i; + papoProxyOverviewRasterBand = (GDALProxyPoolOverviewRasterBand**) + CPLRealloc(papoProxyOverviewRasterBand, + sizeof(GDALProxyPoolOverviewRasterBand*) * (nOverviewBand + 1)); + for(i=nSizeProxyOverviewRasterBand; iGetMaskBand(); + + poProxyMaskBand = + new GDALProxyPoolMaskBand((GDALProxyPoolDataset*)poDS, + poMaskBand, + this); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return poProxyMaskBand; +} + +/* ******************************************************************** */ +/* GDALProxyPoolOverviewRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolOverviewRasterBand::GDALProxyPoolOverviewRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingOverviewBand, + GDALProxyPoolRasterBand* poMainBand, + int nOverviewBand) : + GDALProxyPoolRasterBand(poDS, poUnderlyingOverviewBand) +{ + this->poMainBand = poMainBand; + this->nOverviewBand = nOverviewBand; + + poUnderlyingMainRasterBand = NULL; + nRefCountUnderlyingMainRasterBand = 0; +} + +/* ******************************************************************** */ +/* ~GDALProxyPoolOverviewRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolOverviewRasterBand::~GDALProxyPoolOverviewRasterBand() +{ + CPLAssert(nRefCountUnderlyingMainRasterBand == 0); +} + +/* ******************************************************************** */ +/* RefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +GDALRasterBand* GDALProxyPoolOverviewRasterBand::RefUnderlyingRasterBand() +{ + poUnderlyingMainRasterBand = poMainBand->RefUnderlyingRasterBand(); + if (poUnderlyingMainRasterBand == NULL) + return NULL; + + nRefCountUnderlyingMainRasterBand ++; + return poUnderlyingMainRasterBand->GetOverview(nOverviewBand); +} + +/* ******************************************************************** */ +/* UnrefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +void GDALProxyPoolOverviewRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand) +{ + poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand); + nRefCountUnderlyingMainRasterBand --; +} + + +/* ******************************************************************** */ +/* GDALProxyPoolMaskBand() */ +/* ******************************************************************** */ + +GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingMaskBand, + GDALProxyPoolRasterBand* poMainBand) : + GDALProxyPoolRasterBand(poDS, poUnderlyingMaskBand) +{ + this->poMainBand = poMainBand; + + poUnderlyingMainRasterBand = NULL; + nRefCountUnderlyingMainRasterBand = 0; +} + +/* ******************************************************************** */ +/* GDALProxyPoolMaskBand() */ +/* ******************************************************************** */ + +GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALProxyPoolRasterBand* poMainBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) : + GDALProxyPoolRasterBand(poDS, 1, eDataType, nBlockXSize, nBlockYSize) +{ + this->poMainBand = poMainBand; + + poUnderlyingMainRasterBand = NULL; + nRefCountUnderlyingMainRasterBand = 0; +} + +/* ******************************************************************** */ +/* ~GDALProxyPoolMaskBand() */ +/* ******************************************************************** */ + +GDALProxyPoolMaskBand::~GDALProxyPoolMaskBand() +{ + CPLAssert(nRefCountUnderlyingMainRasterBand == 0); +} + +/* ******************************************************************** */ +/* RefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +GDALRasterBand* GDALProxyPoolMaskBand::RefUnderlyingRasterBand() +{ + poUnderlyingMainRasterBand = poMainBand->RefUnderlyingRasterBand(); + if (poUnderlyingMainRasterBand == NULL) + return NULL; + + nRefCountUnderlyingMainRasterBand ++; + return poUnderlyingMainRasterBand->GetMaskBand(); +} + +/* ******************************************************************** */ +/* UnrefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +void GDALProxyPoolMaskBand::UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand) +{ + poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand); + nRefCountUnderlyingMainRasterBand --; +} diff --git a/ogr/gdalrasterband.cpp b/ogr/gdalrasterband.cpp new file mode 100644 index 0000000..f5640a7 --- /dev/null +++ b/ogr/gdalrasterband.cpp @@ -0,0 +1,5076 @@ +/****************************************************************************** + * $Id: gdalrasterband.cpp 27382 2014-05-24 11:09:32Z rouault $ + * + * Project: GDAL Core + * Purpose: Base class for format specific band class implementation. This + * base class provides default implementation for many methods. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_rat.h" +#include "cpl_string.h" + +#define SUBBLOCK_SIZE 64 +#define TO_SUBBLOCK(x) ((x) >> 6) +#define WITHIN_SUBBLOCK(x) ((x) & 0x3f) + +CPL_CVSID("$Id: gdalrasterband.cpp 27382 2014-05-24 11:09:32Z rouault $"); + +/************************************************************************/ +/* GDALRasterBand() */ +/************************************************************************/ + +/*! Constructor. Applications should never create GDALRasterBands directly. */ + +GDALRasterBand::GDALRasterBand() + +{ + poDS = NULL; + nBand = 0; + nRasterXSize = nRasterYSize = 0; + + eAccess = GA_ReadOnly; + nBlockXSize = nBlockYSize = -1; + eDataType = GDT_Byte; + + nSubBlocksPerRow = nBlocksPerRow = 0; + nSubBlocksPerColumn = nBlocksPerColumn = 0; + + bSubBlockingActive = FALSE; + papoBlocks = NULL; + + poMask = NULL; + bOwnMask = false; + nMaskFlags = 0; + + nBlockReads = 0; + bForceCachedIO = CSLTestBoolean( + CPLGetConfigOption( "GDAL_FORCE_CACHING", "NO") ); + + eFlushBlockErr = CE_None; +} + +/************************************************************************/ +/* ~GDALRasterBand() */ +/************************************************************************/ + +/*! Destructor. Applications should never destroy GDALRasterBands directly, + instead destroy the GDALDataset. */ + +GDALRasterBand::~GDALRasterBand() + +{ + FlushCache(); + + CPLFree( papoBlocks ); + + if( nBlockReads > nBlocksPerRow * nBlocksPerColumn + && nBand == 1 && poDS != NULL ) + { + CPLDebug( "GDAL", "%d block reads on %d block band 1 of %s.", + nBlockReads, nBlocksPerRow * nBlocksPerColumn, + poDS->GetDescription() ); + } + + if( bOwnMask ) + { + delete poMask; + poMask = NULL; + nMaskFlags = 0; + bOwnMask = false; + } +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data for this band. + * + * This method allows reading a region of a GDALRasterBand into a buffer, + * or writing data from a buffer into a region of a GDALRasterBand. It + * automatically takes care of data type translation if the data type + * (eBufType) of the buffer is different than that of the GDALRasterBand. + * The method also takes care of image decimation / replication if the + * buffer size (nBufXSize x nBufYSize) is different than the size of the + * region being accessed (nXSize x nYSize). + * + * The nPixelSpace and nLineSpace parameters allow reading into or + * writing from unusually organized buffers. This is primarily used + * for buffers containing more than one bands raster data in interleaved + * format. + * + * Some formats may efficiently implement decimation into a buffer by + * reading from lower resolution overview images. + * + * For highest performance full resolution data access, read and write + * on "block boundaries" as returned by GetBlockSize(), or use the + * ReadBlock() and WriteBlock() methods. + * + * This method is the same as the C GDALRasterIO() function. + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pData The buffer into which the data should be read, or from which + * it should be written. This buffer must contain at least nBufXSize * + * nBufYSize words of type eBufType. It is organized in left to right, + * top to bottom pixel order. Spacing is controlled by the nPixelSpace, + * and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region is + * to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired region is + * to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @return CE_Failure if the access fails, otherwise CE_None. + */ + +CPLErr GDALRasterBand::RasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + int nLineSpace ) + +{ + + if( NULL == pData ) + { + ReportError( CE_Failure, CPLE_AppDefined, + "The buffer into which the data should be read is null" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Some size values are "noop". Lets just return to avoid */ +/* stressing lower level functions. */ +/* -------------------------------------------------------------------- */ + if( nXSize < 1 || nYSize < 1 || nBufXSize < 1 || nBufYSize < 1 ) + { + CPLDebug( "GDAL", + "RasterIO() skipped for odd window or buffer size.\n" + " Window = (%d,%d)x%dx%d\n" + " Buffer = %dx%d\n", + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); + + return CE_None; + } + + if( eRWFlag == GF_Write && eFlushBlockErr != CE_None ) + { + ReportError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + CPLErr eErr = eFlushBlockErr; + eFlushBlockErr = CE_None; + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* If pixel and line spaceing are defaulted assign reasonable */ +/* value assuming a packed buffer. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8; + + if( nLineSpace == 0 ) + { + if (nPixelSpace > INT_MAX / nBufXSize) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Int overflow : %d x %d", nPixelSpace, nBufXSize ); + return CE_Failure; + } + nLineSpace = nPixelSpace * nBufXSize; + } + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + if( nXOff < 0 || nXOff > INT_MAX - nXSize || nXOff + nXSize > nRasterXSize + || nYOff < 0 || nYOff > INT_MAX - nYSize || nYOff + nYSize > nRasterYSize ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Access window out of range in RasterIO(). Requested\n" + "(%d,%d) of size %dx%d on raster of %dx%d.", + nXOff, nYOff, nXSize, nYSize, nRasterXSize, nRasterYSize ); + return CE_Failure; + } + + if( eRWFlag != GF_Read && eRWFlag != GF_Write ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "eRWFlag = %d, only GF_Read (0) and GF_Write (1) are legal.", + eRWFlag ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Call the format specific function. */ +/* -------------------------------------------------------------------- */ + if( bForceCachedIO ) + return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace ); + else + return IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace ) ; +} + +/************************************************************************/ +/* GDALRasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data for this band. + * + * @see GDALRasterBand::RasterIO() + */ + +CPLErr CPL_STDCALL +GDALRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ) + +{ + VALIDATE_POINTER1( hBand, "GDALRasterIO", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return( poBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace ) ); +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +/** + * \brief Read a block of image data efficiently. + * + * This method accesses a "natural" block from the raster band without + * resampling, or data type conversion. For a more generalized, but + * potentially less efficient access use RasterIO(). + * + * This method is the same as the C GDALReadBlock() function. + * + * See the GetLockedBlockRef() method for a way of accessing internally cached + * block oriented data without an extra copy into an application buffer. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param pImage the buffer into which the data will be read. The buffer + * must be large enough to hold GetBlockXSize()*GetBlockYSize() words + * of type GetRasterDataType(). + * + * @return CE_None on success or CE_Failure on an error. + * + * The following code would efficiently compute a histogram of eight bit + * raster data. Note that the final block may be partial ... data beyond + * the edge of the underlying raster band in these edge blocks is of an + * undermined value. + * +
    + CPLErr GetHistogram( GDALRasterBand *poBand, int *panHistogram )
    +
    + {
    +     int        nXBlocks, nYBlocks, nXBlockSize, nYBlockSize;
    +     int        iXBlock, iYBlock;
    +     GByte      *pabyData;
    +
    +     memset( panHistogram, 0, sizeof(int) * 256 );
    +
    +     CPLAssert( poBand->GetRasterDataType() == GDT_Byte );
    +
    +     poBand->GetBlockSize( &nXBlockSize, &nYBlockSize );
    +     nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize;
    +     nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize;
    +
    +     pabyData = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize);
    +
    +     for( iYBlock = 0; iYBlock < nYBlocks; iYBlock++ )
    +     {
    +         for( iXBlock = 0; iXBlock < nXBlocks; iXBlock++ )
    +         {
    +             int        nXValid, nYValid;
    +             
    +             poBand->ReadBlock( iXBlock, iYBlock, pabyData );
    +
    +             // Compute the portion of the block that is valid
    +             // for partial edge blocks.
    +             if( (iXBlock+1) * nXBlockSize > poBand->GetXSize() )
    +                 nXValid = poBand->GetXSize() - iXBlock * nXBlockSize;
    +             else
    +                 nXValid = nXBlockSize;
    +
    +             if( (iYBlock+1) * nYBlockSize > poBand->GetYSize() )
    +                 nYValid = poBand->GetYSize() - iYBlock * nYBlockSize;
    +             else
    +                 nYValid = nYBlockSize;
    +
    +             // Collect the histogram counts.
    +             for( int iY = 0; iY < nYValid; iY++ )
    +             {
    +                 for( int iX = 0; iX < nXValid; iX++ )
    +                 {
    +                     panHistogram[pabyData[iX + iY * nXBlockSize]] += 1;
    +                 }
    +             }
    +         }
    +     }
    + }
    + 
    +
    + */ + + +CPLErr GDALRasterBand::ReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ +/* -------------------------------------------------------------------- */ +/* Validate arguments. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pImage != NULL ); + + if( !InitBlockInfo() ) + return CE_Failure; + + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nXBlockOff value (%d) in " + "GDALRasterBand::ReadBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nYBlockOff value (%d) in " + "GDALRasterBand::ReadBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Invoke underlying implementation method. */ +/* -------------------------------------------------------------------- */ + return( IReadBlock( nXBlockOff, nYBlockOff, pImage ) ); +} + +/************************************************************************/ +/* GDALReadBlock() */ +/************************************************************************/ + +/** + * \brief Read a block of image data efficiently. + * + * @see GDALRasterBand::ReadBlock() + */ + +CPLErr CPL_STDCALL GDALReadBlock( GDALRasterBandH hBand, int nXOff, int nYOff, + void * pData ) + +{ + VALIDATE_POINTER1( hBand, "GDALReadBlock", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return( poBand->ReadBlock( nXOff, nYOff, pData ) ); +} + +/************************************************************************/ +/* IWriteBlock() */ +/* */ +/* Default internal implementation ... to be overriden by */ +/* subclasses that support writing. */ +/************************************************************************/ + +CPLErr GDALRasterBand::IWriteBlock( int, int, void * ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "WriteBlock() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +/** + * \brief Write a block of image data efficiently. + * + * This method accesses a "natural" block from the raster band without + * resampling, or data type conversion. For a more generalized, but + * potentially less efficient access use RasterIO(). + * + * This method is the same as the C GDALWriteBlock() function. + * + * See ReadBlock() for an example of block oriented data access. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param pImage the buffer from which the data will be written. The buffer + * must be large enough to hold GetBlockXSize()*GetBlockYSize() words + * of type GetRasterDataType(). + * + * @return CE_None on success or CE_Failure on an error. + */ + +CPLErr GDALRasterBand::WriteBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ +/* -------------------------------------------------------------------- */ +/* Validate arguments. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pImage != NULL ); + + if( !InitBlockInfo() ) + return CE_Failure; + + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nXBlockOff value (%d) in " + "GDALRasterBand::WriteBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nYBlockOff value (%d) in " + "GDALRasterBand::WriteBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + + if( eAccess == GA_ReadOnly ) + { + ReportError( CE_Failure, CPLE_NoWriteAccess, + "Attempt to write to read only dataset in" + "GDALRasterBand::WriteBlock().\n" ); + + return( CE_Failure ); + } + + if( eFlushBlockErr != CE_None ) + { + ReportError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + CPLErr eErr = eFlushBlockErr; + eFlushBlockErr = CE_None; + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Invoke underlying implementation method. */ +/* -------------------------------------------------------------------- */ + return( IWriteBlock( nXBlockOff, nYBlockOff, pImage ) ); +} + +/************************************************************************/ +/* GDALWriteBlock() */ +/************************************************************************/ + +/** + * \brief Write a block of image data efficiently. + * + * @see GDALRasterBand::WriteBlock() + */ + +CPLErr CPL_STDCALL GDALWriteBlock( GDALRasterBandH hBand, int nXOff, int nYOff, + void * pData ) + +{ + VALIDATE_POINTER1( hBand, "GDALWriteBlock", CE_Failure ); + + GDALRasterBand *poBand = static_cast( hBand ); + return( poBand->WriteBlock( nXOff, nYOff, pData ) ); +} + + +/************************************************************************/ +/* GetRasterDataType() */ +/************************************************************************/ + +/** + * \brief Fetch the pixel data type for this band. + * + * This method is the same as the C function GDALGetRasterDataType(). + * + * @return the data type of pixels for this band. + */ + + +GDALDataType GDALRasterBand::GetRasterDataType() + +{ + return eDataType; +} + +/************************************************************************/ +/* GDALGetRasterDataType() */ +/************************************************************************/ + +/** + * \brief Fetch the pixel data type for this band. + * + * @see GDALRasterBand::GetRasterDataType() + */ + +GDALDataType CPL_STDCALL GDALGetRasterDataType( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterDataType", GDT_Unknown ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetRasterDataType(); +} + +/************************************************************************/ +/* GetBlockSize() */ +/************************************************************************/ + +/** + * \brief Fetch the "natural" block size of this band. + * + * GDAL contains a concept of the natural block size of rasters so that + * applications can organized data access efficiently for some file formats. + * The natural block size is the block size that is most efficient for + * accessing the format. For many formats this is simple a whole scanline + * in which case *pnXSize is set to GetXSize(), and *pnYSize is set to 1. + * + * However, for tiled images this will typically be the tile size. + * + * Note that the X and Y block sizes don't have to divide the image size + * evenly, meaning that right and bottom edge blocks may be incomplete. + * See ReadBlock() for an example of code dealing with these issues. + * + * This method is the same as the C function GDALGetBlockSize(). + * + * @param pnXSize integer to put the X block size into or NULL. + * + * @param pnYSize integer to put the Y block size into or NULL. + */ + +void GDALRasterBand::GetBlockSize( int * pnXSize, int *pnYSize ) + +{ + if( nBlockXSize <= 0 || nBlockYSize <= 0 ) + { + ReportError( CE_Failure, CPLE_AppDefined, "Invalid block dimension : %d * %d", + nBlockXSize, nBlockYSize ); + if( pnXSize != NULL ) + *pnXSize = 0; + if( pnYSize != NULL ) + *pnYSize = 0; + } + else + { + if( pnXSize != NULL ) + *pnXSize = nBlockXSize; + if( pnYSize != NULL ) + *pnYSize = nBlockYSize; + } +} + +/************************************************************************/ +/* GDALGetBlockSize() */ +/************************************************************************/ + +/** + * \brief Fetch the "natural" block size of this band. + * + * @see GDALRasterBand::GetBlockSize() + */ + +void CPL_STDCALL +GDALGetBlockSize( GDALRasterBandH hBand, int * pnXSize, int * pnYSize ) + +{ + VALIDATE_POINTER0( hBand, "GDALGetBlockSize" ); + + GDALRasterBand *poBand = static_cast(hBand); + poBand->GetBlockSize( pnXSize, pnYSize ); +} + +/************************************************************************/ +/* InitBlockInfo() */ +/************************************************************************/ + +int GDALRasterBand::InitBlockInfo() + +{ + if( papoBlocks != NULL ) + return TRUE; + + /* Do some validation of raster and block dimensions in case the driver */ + /* would have neglected to do it itself */ + if( nBlockXSize <= 0 || nBlockYSize <= 0 ) + { + ReportError( CE_Failure, CPLE_AppDefined, "Invalid block dimension : %d * %d", + nBlockXSize, nBlockYSize ); + return FALSE; + } + + if( nRasterXSize <= 0 || nRasterYSize <= 0 ) + { + ReportError( CE_Failure, CPLE_AppDefined, "Invalid raster dimension : %d * %d", + nRasterXSize, nRasterYSize ); + return FALSE; + } + + if (nBlockXSize >= 10000 || nBlockYSize >= 10000) + { + /* Check that the block size is not overflowing int capacity as it is */ + /* (reasonnably) assumed in many places (GDALRasterBlock::Internalize(), */ + /* GDALRasterBand::Fill(), many drivers...) */ + /* As 10000 * 10000 * 16 < INT_MAX, we don't need to do the multiplication in other cases */ + + int nSizeInBytes = nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + + GIntBig nBigSizeInBytes = (GIntBig)nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + if ((GIntBig)nSizeInBytes != nBigSizeInBytes) + { + ReportError( CE_Failure, CPLE_NotSupported, "Too big block : %d * %d", + nBlockXSize, nBlockYSize ); + return FALSE; + } + } + + nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize); + + if( nBlocksPerRow < SUBBLOCK_SIZE/2 ) + { + bSubBlockingActive = FALSE; + + if (nBlocksPerRow < INT_MAX / nBlocksPerColumn) + { + papoBlocks = (GDALRasterBlock **) + VSICalloc( sizeof(void*), nBlocksPerRow * nBlocksPerColumn ); + } + else + { + ReportError( CE_Failure, CPLE_NotSupported, "Too many blocks : %d x %d", + nBlocksPerRow, nBlocksPerColumn ); + return FALSE; + } + } + else + { + bSubBlockingActive = TRUE; + + nSubBlocksPerRow = DIV_ROUND_UP(nBlocksPerRow, SUBBLOCK_SIZE); + nSubBlocksPerColumn = DIV_ROUND_UP(nBlocksPerColumn, SUBBLOCK_SIZE); + + if (nSubBlocksPerRow < INT_MAX / nSubBlocksPerColumn) + { + papoBlocks = (GDALRasterBlock **) + VSICalloc( sizeof(void*), nSubBlocksPerRow * nSubBlocksPerColumn ); + } + else + { + ReportError( CE_Failure, CPLE_NotSupported, "Too many subblocks : %d x %d", + nSubBlocksPerRow, nSubBlocksPerColumn ); + return FALSE; + } + } + + if( papoBlocks == NULL ) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in InitBlockInfo()." ); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* AdoptBlock() */ +/* */ +/* Add a block to the raster band's block matrix. If this */ +/* exceeds our maximum blocks for this layer, flush the oldest */ +/* block out. */ +/* */ +/* This method is protected. */ +/************************************************************************/ + +CPLErr GDALRasterBand::AdoptBlock( int nXBlockOff, int nYBlockOff, + GDALRasterBlock * poBlock ) + +{ + int nBlockIndex; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Simple case without subblocking. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + if( papoBlocks[nBlockIndex] == poBlock ) + return( CE_None ); + + if( papoBlocks[nBlockIndex] != NULL ) + FlushBlock( nXBlockOff, nYBlockOff ); + + papoBlocks[nBlockIndex] = poBlock; + poBlock->Touch(); + + return( CE_None ); + } + +/* -------------------------------------------------------------------- */ +/* Identify the subblock in which our target occurs, and create */ +/* it if necessary. */ +/* -------------------------------------------------------------------- */ + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + { + const int nSubGridSize = + sizeof(GDALRasterBlock*) * SUBBLOCK_SIZE * SUBBLOCK_SIZE; + + papoBlocks[nSubBlock] = (GDALRasterBlock *) VSICalloc(1, nSubGridSize); + if( papoBlocks[nSubBlock] == NULL ) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in AdoptBlock()." ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + if( papoSubBlockGrid[nBlockInSubBlock] == poBlock ) + return CE_None; + + if( papoSubBlockGrid[nBlockInSubBlock] != NULL ) + FlushBlock( nXBlockOff, nYBlockOff ); + + papoSubBlockGrid[nBlockInSubBlock] = poBlock; + poBlock->Touch(); + + return CE_None; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +/** + * \brief Flush raster data cache. + * + * This call will recover memory used to cache data blocks for this raster + * band, and ensure that new requests are referred to the underlying driver. + * + * This method is the same as the C function GDALFlushRasterCache(). + * + * @return CE_None on success. + */ + +CPLErr GDALRasterBand::FlushCache() + +{ + CPLErr eGlobalErr = eFlushBlockErr; + + if (eFlushBlockErr != CE_None) + { + ReportError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + eFlushBlockErr = CE_None; + } + + if (papoBlocks == NULL) + return eGlobalErr; + +/* -------------------------------------------------------------------- */ +/* Flush all blocks in memory ... this case is without subblocking.*/ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + for( int iY = 0; iY < nBlocksPerColumn; iY++ ) + { + for( int iX = 0; iX < nBlocksPerRow; iX++ ) + { + if( papoBlocks[iX + iY*nBlocksPerRow] != NULL ) + { + CPLErr eErr; + + eErr = FlushBlock( iX, iY, eGlobalErr == CE_None ); + + if( eErr != CE_None ) + eGlobalErr = eErr; + } + } + } + return eGlobalErr; + } + +/* -------------------------------------------------------------------- */ +/* With subblocking. We can short circuit missing subblocks. */ +/* -------------------------------------------------------------------- */ + int iSBX, iSBY; + + for( iSBY = 0; iSBY < nSubBlocksPerColumn; iSBY++ ) + { + for( iSBX = 0; iSBX < nSubBlocksPerRow; iSBX++ ) + { + int nSubBlock = iSBX + iSBY * nSubBlocksPerRow; + + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + if( papoSubBlockGrid == NULL ) + continue; + + for( int iY = 0; iY < SUBBLOCK_SIZE; iY++ ) + { + for( int iX = 0; iX < SUBBLOCK_SIZE; iX++ ) + { + if( papoSubBlockGrid[iX + iY * SUBBLOCK_SIZE] != NULL ) + { + CPLErr eErr; + + eErr = FlushBlock( iX + iSBX * SUBBLOCK_SIZE, + iY + iSBY * SUBBLOCK_SIZE, + eGlobalErr == CE_None ); + if( eErr != CE_None ) + eGlobalErr = eErr; + } + } + } + + // We might as well get rid of this grid chunk since we know + // it is now empty. + papoBlocks[nSubBlock] = NULL; + CPLFree( papoSubBlockGrid ); + } + } + + return( eGlobalErr ); +} + +/************************************************************************/ +/* GDALFlushRasterCache() */ +/************************************************************************/ + +/** + * \brief Flush raster data cache. + * + * @see GDALRasterBand::FlushCache() + */ + +CPLErr CPL_STDCALL GDALFlushRasterCache( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALFlushRasterCache", CE_Failure ); + + return ((GDALRasterBand *) hBand)->FlushCache(); +} + +/************************************************************************/ +/* FlushBlock() */ +/* */ +/* Flush a block out of the block cache. If it has been */ +/* modified write it to disk. If no specific tile is */ +/* indicated, write the oldest tile. */ +/* */ +/* Protected method. */ +/************************************************************************/ + +CPLErr GDALRasterBand::FlushBlock( int nXBlockOff, int nYBlockOff, int bWriteDirtyBlock ) + +{ + int nBlockIndex; + GDALRasterBlock *poBlock = NULL; + + if( !papoBlocks ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Validate the request */ +/* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::FlushBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::FlushBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Simple case for single level caches. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + GDALRasterBlock::SafeLockBlock( papoBlocks + nBlockIndex ); + + poBlock = papoBlocks[nBlockIndex]; + papoBlocks[nBlockIndex] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Identify our subblock. */ +/* -------------------------------------------------------------------- */ + else + { + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + GDALRasterBlock::SafeLockBlock( papoSubBlockGrid + nBlockInSubBlock ); + + poBlock = papoSubBlockGrid[nBlockInSubBlock]; + papoSubBlockGrid[nBlockInSubBlock] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Is the target block dirty? If so we need to write it. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + if( poBlock == NULL ) + return CE_None; + + poBlock->Detach(); + + if( bWriteDirtyBlock && poBlock->GetDirty() ) + eErr = poBlock->Write(); + +/* -------------------------------------------------------------------- */ +/* Deallocate the block; */ +/* -------------------------------------------------------------------- */ + poBlock->DropLock(); + delete poBlock; + + return eErr; +} + +/************************************************************************/ +/* TryGetLockedBlockRef() */ +/************************************************************************/ + +/** + * \brief Try fetching block ref. + * + * This method will returned the requested block (locked) if it is already + * in the block cache for the layer. If not, NULL is returned. + * + * If a non-NULL value is returned, then a lock for the block will have been + * acquired on behalf of the caller. It is absolutely imperative that the + * caller release this lock (with GDALRasterBlock::DropLock()) or else + * severe problems may result. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the top most block, 1 the next block and so forth. + * + * @return NULL if block not available, or locked block pointer. + */ + +GDALRasterBlock *GDALRasterBand::TryGetLockedBlockRef( int nXBlockOff, + int nYBlockOff ) + +{ + int nBlockIndex = 0; + + if( !InitBlockInfo() ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Validate the request */ +/* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::TryGetLockedBlockRef()\n", + nXBlockOff ); + + return( NULL ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::TryGetLockedBlockRef()\n", + nYBlockOff ); + + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Simple case for single level caches. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + GDALRasterBlock::SafeLockBlock( papoBlocks + nBlockIndex ); + + return papoBlocks[nBlockIndex]; + } + +/* -------------------------------------------------------------------- */ +/* Identify our subblock. */ +/* -------------------------------------------------------------------- */ + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + GDALRasterBlock::SafeLockBlock( papoSubBlockGrid + nBlockInSubBlock ); + + return papoSubBlockGrid[nBlockInSubBlock]; +} + +/************************************************************************/ +/* GetLockedBlockRef() */ +/************************************************************************/ + +/** + * \brief Fetch a pointer to an internally cached raster block. + * + * This method will returned the requested block (locked) if it is already + * in the block cache for the layer. If not, the block will be read from + * the driver, and placed in the layer block cached, then returned. If an + * error occurs reading the block from the driver, a NULL value will be + * returned. + * + * If a non-NULL value is returned, then a lock for the block will have been + * acquired on behalf of the caller. It is absolutely imperative that the + * caller release this lock (with GDALRasterBlock::DropLock()) or else + * severe problems may result. + * + * Note that calling GetLockedBlockRef() on a previously uncached band will + * enable caching. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the top most block, 1 the next block and so forth. + * + * @param bJustInitialize If TRUE the block will be allocated and initialized, + * but not actually read from the source. This is useful when it will just + * be completely set and written back. + * + * @return pointer to the block object, or NULL on failure. + */ + +GDALRasterBlock * GDALRasterBand::GetLockedBlockRef( int nXBlockOff, + int nYBlockOff, + int bJustInitialize ) + +{ + GDALRasterBlock *poBlock = NULL; + +/* -------------------------------------------------------------------- */ +/* Try and fetch from cache. */ +/* -------------------------------------------------------------------- */ + poBlock = TryGetLockedBlockRef( nXBlockOff, nYBlockOff ); + +/* -------------------------------------------------------------------- */ +/* If we didn't find it in our memory cache, instantiate a */ +/* block (potentially load from disk) and "adopt" it into the */ +/* cache. */ +/* -------------------------------------------------------------------- */ + if( poBlock == NULL ) + { + if( !InitBlockInfo() ) + return( NULL ); + + /* -------------------------------------------------------------------- */ + /* Validate the request */ + /* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::GetLockedBlockRef()\n", + nXBlockOff ); + + return( NULL ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::GetLockedBlockRef()\n", + nYBlockOff ); + + return( NULL ); + } + + poBlock = new GDALRasterBlock( this, nXBlockOff, nYBlockOff ); + + poBlock->AddLock(); + + /* allocate data space */ + if( poBlock->Internalize() != CE_None ) + { + poBlock->DropLock(); + delete poBlock; + return( NULL ); + } + + if ( AdoptBlock( nXBlockOff, nYBlockOff, poBlock ) != CE_None ) + { + poBlock->DropLock(); + delete poBlock; + return( NULL ); + } + + if( !bJustInitialize + && IReadBlock(nXBlockOff,nYBlockOff,poBlock->GetDataRef()) != CE_None) + { + poBlock->DropLock(); + FlushBlock( nXBlockOff, nYBlockOff ); + ReportError( CE_Failure, CPLE_AppDefined, + "IReadBlock failed at X offset %d, Y offset %d", + nXBlockOff, nYBlockOff ); + return( NULL ); + } + + if( !bJustInitialize ) + { + nBlockReads++; + if( nBlockReads == nBlocksPerRow * nBlocksPerColumn + 1 + && nBand == 1 && poDS != NULL ) + { + CPLDebug( "GDAL", "Potential thrashing on band %d of %s.", + nBand, poDS->GetDescription() ); + } + } + } + + return poBlock; +} + +/************************************************************************/ +/* Fill() */ +/************************************************************************/ + +/** + * \brief Fill this band with a constant value. + * + * GDAL makes no guarantees + * about what values pixels in newly created files are set to, so this + * method can be used to clear a band to a specified "default" value. + * The fill value is passed in as a double but this will be converted + * to the underlying type before writing to the file. An optional + * second argument allows the imaginary component of a complex + * constant value to be specified. + * + * This method is the same as the C function GDALFillRaster(). + * + * @param dfRealValue Real component of fill value + * @param dfImaginaryValue Imaginary component of fill value, defaults to zero + * + * @return CE_Failure if the write fails, otherwise CE_None + */ +CPLErr GDALRasterBand::Fill(double dfRealValue, double dfImaginaryValue) { + + // General approach is to construct a source block of the file's + // native type containing the appropriate value and then copy this + // to each block in the image via the RasterBlock cache. Using + // the cache means we avoid file I/O if it's not necessary, at the + // expense of some extra memcpy's (since we write to the + // RasterBlock cache, which is then at some point written to the + // underlying file, rather than simply directly to the underlying + // file.) + + // Check we can write to the file + if( eAccess == GA_ReadOnly ) { + ReportError(CE_Failure, CPLE_NoWriteAccess, + "Attempt to write to read only dataset in" + "GDALRasterBand::Fill().\n" ); + return CE_Failure; + } + + // Make sure block parameters are set + if( !InitBlockInfo() ) + return CE_Failure; + + // Allocate the source block + int blockSize = nBlockXSize * nBlockYSize; + int elementSize = GDALGetDataTypeSize(eDataType) / 8; + int blockByteSize = blockSize * elementSize; + unsigned char* srcBlock = (unsigned char*) VSIMalloc(blockByteSize); + if (srcBlock == NULL) { + ReportError(CE_Failure, CPLE_OutOfMemory, + "GDALRasterBand::Fill(): Out of memory " + "allocating %d bytes.\n", blockByteSize); + return CE_Failure; + } + + // Initialize the first element of the block, doing type conversion + double complexSrc[2] = { dfRealValue, dfImaginaryValue }; + GDALCopyWords(complexSrc, GDT_CFloat64, 0, srcBlock, eDataType, 0, 1); + + // Copy first element to the rest of the block + for (unsigned char* blockPtr = srcBlock + elementSize; + blockPtr < srcBlock + blockByteSize; blockPtr += elementSize) { + memcpy(blockPtr, srcBlock, elementSize); + } + + // Write block to block cache + for (int j = 0; j < nBlocksPerColumn; ++j) { + for (int i = 0; i < nBlocksPerRow; ++i) { + GDALRasterBlock* destBlock = GetLockedBlockRef(i, j, TRUE); + if (destBlock == NULL) { + ReportError(CE_Failure, CPLE_OutOfMemory, + "GDALRasterBand::Fill(): Error " + "while retrieving cache block.\n"); + VSIFree(srcBlock); + return CE_Failure; + } + if (destBlock->GetDataRef() == NULL) + { + destBlock->DropLock(); + VSIFree(srcBlock); + return CE_Failure; + } + memcpy(destBlock->GetDataRef(), srcBlock, blockByteSize); + destBlock->MarkDirty(); + destBlock->DropLock(); + } + } + + // Free up the source block + VSIFree(srcBlock); + + return CE_None; +} + + +/************************************************************************/ +/* GDALFillRaster() */ +/************************************************************************/ + +/** + * \brief Fill this band with a constant value. + * + * @see GDALRasterBand::Fill() + */ +CPLErr CPL_STDCALL GDALFillRaster(GDALRasterBandH hBand, double dfRealValue, + double dfImaginaryValue) +{ + VALIDATE_POINTER1( hBand, "GDALFillRaster", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->Fill(dfRealValue, dfImaginaryValue); +} + +/************************************************************************/ +/* GetAccess() */ +/************************************************************************/ + +/** + * \brief Find out if we have update permission for this band. + * + * This method is the same as the C function GDALGetRasterAccess(). + * + * @return Either GA_Update or GA_ReadOnly. + */ + +GDALAccess GDALRasterBand::GetAccess() + +{ + return eAccess; +} + +/************************************************************************/ +/* GDALGetRasterAccess() */ +/************************************************************************/ + +/** + * \brief Find out if we have update permission for this band. + * + * @see GDALRasterBand::GetAccess() + */ + +GDALAccess CPL_STDCALL GDALGetRasterAccess( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterAccess", GA_ReadOnly ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetAccess(); +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +/** + * \brief Fetch the list of category names for this raster. + * + * The return list is a "StringList" in the sense of the CPL functions. + * That is a NULL terminated array of strings. Raster values without + * associated names will have an empty string in the returned list. The + * first entry in the list is for raster values of zero, and so on. + * + * The returned stringlist should not be altered or freed by the application. + * It may change on the next GDAL call, so please copy it if it is needed + * for any period of time. + * + * This method is the same as the C function GDALGetRasterCategoryNames(). + * + * @return list of names, or NULL if none. + */ + +char **GDALRasterBand::GetCategoryNames() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetRasterCategoryNames() */ +/************************************************************************/ + +/** + * \brief Fetch the list of category names for this raster. + * + * @see GDALRasterBand::GetCategoryNames() + */ + +char ** CPL_STDCALL GDALGetRasterCategoryNames( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterCategoryNames", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetCategoryNames(); +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +/** + * \brief Set the category names for this band. + * + * See the GetCategoryNames() method for more on the interpretation of + * category names. + * + * This method is the same as the C function GDALSetRasterCategoryNames(). + * + * @param papszNames the NULL terminated StringList of category names. May + * be NULL to just clear the existing list. + * + * @return CE_None on success of CE_Failure on failure. If unsupported + * by the driver CE_Failure is returned, but no error message is reported. + */ + +CPLErr GDALRasterBand::SetCategoryNames( char ** papszNames ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetCategoryNames() not supported for this dataset." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetCategoryNames() */ +/************************************************************************/ + +/** + * \brief Set the category names for this band. + * + * @see GDALRasterBand::SetCategoryNames() + */ + +CPLErr CPL_STDCALL +GDALSetRasterCategoryNames( GDALRasterBandH hBand, char ** papszNames ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterCategoryNames", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetCategoryNames( papszNames ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +/** + * \brief Fetch the no data value for this band. + * + * If there is no out of data value, an out of range value will generally + * be returned. The no data value for a band is generally a special marker + * value used to mark pixels that are not valid data. Such pixels should + * generally not be displayed, nor contribute to analysis operations. + * + * This method is the same as the C function GDALGetRasterNoDataValue(). + * + * @param pbSuccess pointer to a boolean to use to indicate if a value + * is actually associated with this layer. May be NULL (default). + * + * @return the nodata value for this band. + */ + +double GDALRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + return -1e10; +} + +/************************************************************************/ +/* GDALGetRasterNoDataValue() */ +/************************************************************************/ + +/** + * \brief Fetch the no data value for this band. + * + * @see GDALRasterBand::GetNoDataValue() + */ + +double CPL_STDCALL +GDALGetRasterNoDataValue( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterNoDataValue", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +/** + * \brief Set the no data value for this band. + * + * To clear the nodata value, just set it with an "out of range" value. + * Complex band no data values must have an imagery component of zero. + * + * This method is the same as the C function GDALSetRasterNoDataValue(). + * + * @param dfNoData the value to set. + * + * @return CE_None on success, or CE_Failure on failure. If unsupported + * by the driver, CE_Failure is returned by no error message will have + * been emitted. + */ + +CPLErr GDALRasterBand::SetNoDataValue( double dfNoData ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetNoDataValue() not supported for this dataset." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterNoDataValue() */ +/************************************************************************/ + +/** + * \brief Set the no data value for this band. + * + * @see GDALRasterBand::SetNoDataValue() + */ + +CPLErr CPL_STDCALL +GDALSetRasterNoDataValue( GDALRasterBandH hBand, double dfValue ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterNoDataValue", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetNoDataValue( dfValue ); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +/** + * \brief Fetch the maximum value for this band. + * + * For file formats that don't know this intrinsically, the maximum supported + * value for the data type will generally be returned. + * + * This method is the same as the C function GDALGetRasterMaximum(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is a tight maximum or not. May be NULL (default). + * + * @return the maximum raster value (excluding no data pixels) + */ + +double GDALRasterBand::GetMaximum( int *pbSuccess ) + +{ + const char *pszValue = NULL; + + if( (pszValue = GetMetadataItem("STATISTICS_MAXIMUM")) != NULL ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return CPLAtofM(pszValue); + } + + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + switch( eDataType ) + { + case GDT_Byte: + { + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + if (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")) + return 127; + else + return 255; + } + + case GDT_UInt16: + return 65535; + + case GDT_Int16: + case GDT_CInt16: + return 32767; + + case GDT_Int32: + case GDT_CInt32: + return 2147483647.0; + + case GDT_UInt32: + return 4294967295.0; + + case GDT_Float32: + case GDT_CFloat32: + return 4294967295.0; /* not actually accurate */ + + case GDT_Float64: + case GDT_CFloat64: + return 4294967295.0; /* not actually accurate */ + + default: + return 4294967295.0; /* not actually accurate */ + } +} + +/************************************************************************/ +/* GDALGetRasterMaximum() */ +/************************************************************************/ + +/** + * \brief Fetch the maximum value for this band. + * + * @see GDALRasterBand::GetMaximum() + */ + +double CPL_STDCALL +GDALGetRasterMaximum( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterMaximum", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMaximum( pbSuccess ); +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +/** + * \brief Fetch the minimum value for this band. + * + * For file formats that don't know this intrinsically, the minimum supported + * value for the data type will generally be returned. + * + * This method is the same as the C function GDALGetRasterMinimum(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is a tight minimum or not. May be NULL (default). + * + * @return the minimum raster value (excluding no data pixels) + */ + +double GDALRasterBand::GetMinimum( int *pbSuccess ) + +{ + const char *pszValue = NULL; + + if( (pszValue = GetMetadataItem("STATISTICS_MINIMUM")) != NULL ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return CPLAtofM(pszValue); + } + + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + switch( eDataType ) + { + case GDT_Byte: + { + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + if (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")) + return -128; + else + return 0; + } + + case GDT_UInt16: + return 0; + + case GDT_Int16: + return -32768; + + case GDT_Int32: + return -2147483648.0; + + case GDT_UInt32: + return 0; + + case GDT_Float32: + return -4294967295.0; /* not actually accurate */ + + case GDT_Float64: + return -4294967295.0; /* not actually accurate */ + + default: + return -4294967295.0; /* not actually accurate */ + } +} + +/************************************************************************/ +/* GDALGetRasterMinimum() */ +/************************************************************************/ + +/** + * \brief Fetch the minimum value for this band. + * + * @see GDALRasterBand::GetMinimum() + */ + +double CPL_STDCALL +GDALGetRasterMinimum( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterMinimum", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMinimum( pbSuccess ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +/** + * \brief How should this band be interpreted as color? + * + * GCI_Undefined is returned when the format doesn't know anything + * about the color interpretation. + * + * This method is the same as the C function + * GDALGetRasterColorInterpretation(). + * + * @return color interpretation value for band. + */ + +GDALColorInterp GDALRasterBand::GetColorInterpretation() + +{ + return GCI_Undefined; +} + +/************************************************************************/ +/* GDALGetRasterColorInterpretation() */ +/************************************************************************/ + +/** + * \brief How should this band be interpreted as color? + * + * @see GDALRasterBand::GetColorInterpretation() + */ + +GDALColorInterp CPL_STDCALL +GDALGetRasterColorInterpretation( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterColorInterpretation", GCI_Undefined ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetColorInterpretation(); +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +/** + * \brief Set color interpretation of a band. + * + * This method is the same as the C function GDALSetRasterColorInterpretation(). + * + * @param eColorInterp the new color interpretation to apply to this band. + * + * @return CE_None on success or CE_Failure if method is unsupported by format. + */ + +CPLErr GDALRasterBand::SetColorInterpretation( GDALColorInterp eColorInterp) + +{ + (void) eColorInterp; + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetColorInterpretation() not supported for this dataset." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterColorInterpretation() */ +/************************************************************************/ + +/** + * \brief Set color interpretation of a band. + * + * @see GDALRasterBand::SetColorInterpretation() + */ + +CPLErr CPL_STDCALL +GDALSetRasterColorInterpretation( GDALRasterBandH hBand, + GDALColorInterp eColorInterp ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterColorInterpretation", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetColorInterpretation(eColorInterp); +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +/** + * \brief Fetch the color table associated with band. + * + * If there is no associated color table, the return result is NULL. The + * returned color table remains owned by the GDALRasterBand, and can't + * be depended on for long, nor should it ever be modified by the caller. + * + * This method is the same as the C function GDALGetRasterColorTable(). + * + * @return internal color table, or NULL. + */ + +GDALColorTable *GDALRasterBand::GetColorTable() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetRasterColorTable() */ +/************************************************************************/ + +/** + * \brief Fetch the color table associated with band. + * + * @see GDALRasterBand::GetColorTable() + */ + +GDALColorTableH CPL_STDCALL GDALGetRasterColorTable( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterColorTable", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALColorTableH)poBand->GetColorTable(); +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +/** + * \brief Set the raster color table. + * + * The driver will make a copy of all desired data in the colortable. It + * remains owned by the caller after the call. + * + * This method is the same as the C function GDALSetRasterColorTable(). + * + * @param poCT the color table to apply. This may be NULL to clear the color + * table (where supported). + * + * @return CE_None on success, or CE_Failure on failure. If the action is + * unsupported by the driver, a value of CE_Failure is returned, but no + * error is issued. + */ + +CPLErr GDALRasterBand::SetColorTable( GDALColorTable * poCT ) + +{ + (void) poCT; + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetColorTable() not supported for this dataset." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterColorTable() */ +/************************************************************************/ + +/** + * \brief Set the raster color table. + * + * @see GDALRasterBand::SetColorTable() + */ + +CPLErr CPL_STDCALL +GDALSetRasterColorTable( GDALRasterBandH hBand, GDALColorTableH hCT ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterColorTable", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetColorTable( static_cast(hCT) ); +} + +/************************************************************************/ +/* HasArbitraryOverviews() */ +/************************************************************************/ + +/** + * \brief Check for arbitrary overviews. + * + * This returns TRUE if the underlying datastore can compute arbitrary + * overviews efficiently, such as is the case with OGDI over a network. + * Datastores with arbitrary overviews don't generally have any fixed + * overviews, but the RasterIO() method can be used in downsampling mode + * to get overview data efficiently. + * + * This method is the same as the C function GDALHasArbitraryOverviews(), + * + * @return TRUE if arbitrary overviews available (efficiently), otherwise + * FALSE. + */ + +int GDALRasterBand::HasArbitraryOverviews() + +{ + return FALSE; +} + +/************************************************************************/ +/* GDALHasArbitraryOverviews() */ +/************************************************************************/ + +/** + * \brief Check for arbitrary overviews. + * + * @see GDALRasterBand::HasArbitraryOverviews() + */ + +int CPL_STDCALL GDALHasArbitraryOverviews( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALHasArbitraryOverviews", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->HasArbitraryOverviews(); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +/** + * \brief Return the number of overview layers available. + * + * This method is the same as the C function GDALGetOverviewCount(). + * + * @return overview count, zero if none. + */ + +int GDALRasterBand::GetOverviewCount() + +{ + if( poDS != NULL && poDS->oOvManager.IsInitialized() ) + return poDS->oOvManager.GetOverviewCount( nBand ); + else + return 0; +} + +/************************************************************************/ +/* GDALGetOverviewCount() */ +/************************************************************************/ + +/** + * \brief Return the number of overview layers available. + * + * @see GDALRasterBand::GetOverviewCount() + */ + +int CPL_STDCALL GDALGetOverviewCount( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetOverviewCount", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetOverviewCount(); +} + + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +/** + * \brief Fetch overview raster band object. + * + * This method is the same as the C function GDALGetOverview(). + * + * @param i overview index between 0 and GetOverviewCount()-1. + * + * @return overview GDALRasterBand. + */ + +GDALRasterBand * GDALRasterBand::GetOverview( int i ) + +{ + if( poDS != NULL && poDS->oOvManager.IsInitialized() ) + return poDS->oOvManager.GetOverview( nBand, i ); + else + return NULL; +} + +/************************************************************************/ +/* GDALGetOverview() */ +/************************************************************************/ + +/** + * \brief Fetch overview raster band object. + * + * @see GDALRasterBand::GetOverview() + */ + +GDALRasterBandH CPL_STDCALL GDALGetOverview( GDALRasterBandH hBand, int i ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetOverview", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterBandH) poBand->GetOverview(i); +} + +/************************************************************************/ +/* GetRasterSampleOverview() */ +/************************************************************************/ + +/** + * \brief Fetch best sampling overview. + * + * Returns the most reduced overview of the given band that still satisfies + * the desired number of samples. This function can be used with zero + * as the number of desired samples to fetch the most reduced overview. + * The same band as was passed in will be returned if it has not overviews, + * or if none of the overviews have enough samples. + * + * This method is the same as the C function GDALGetRasterSampleOverview(). + * + * @param nDesiredSamples the returned band will have at least this many + * pixels. + * + * @return optimal overview or the band itself. + */ + +GDALRasterBand *GDALRasterBand::GetRasterSampleOverview( int nDesiredSamples ) + +{ + double dfBestSamples = 0; + GDALRasterBand *poBestBand = this; + + dfBestSamples = GetXSize() * (double)GetYSize(); + + for( int iOverview = 0; iOverview < GetOverviewCount(); iOverview++ ) + { + GDALRasterBand *poOBand = GetOverview( iOverview ); + double dfOSamples = 0; + + if (poOBand == NULL) + continue; + + dfOSamples = poOBand->GetXSize() * (double)poOBand->GetYSize(); + + if( dfOSamples < dfBestSamples && dfOSamples > nDesiredSamples ) + { + dfBestSamples = dfOSamples; + poBestBand = poOBand; + } + } + + return poBestBand; +} + +/************************************************************************/ +/* GDALGetRasterSampleOverview() */ +/************************************************************************/ + +/** + * \brief Fetch best sampling overview. + * + * @see GDALRasterBand::GetRasterSampleOverview() + */ + +GDALRasterBandH CPL_STDCALL +GDALGetRasterSampleOverview( GDALRasterBandH hBand, int nDesiredSamples ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterSampleOverview", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterBandH) + poBand->GetRasterSampleOverview( nDesiredSamples ); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +/** + * \brief Build raster overview(s) + * + * If the operation is unsupported for the indicated dataset, then + * CE_Failure is returned, and CPLGetLastErrorNo() will return + * CPLE_NotSupported. + * + * WARNING: It is not possible to build overviews for a single band in + * TIFF format, and thus this method does not work for TIFF format, or any + * formats that use the default overview building in TIFF format. Instead + * it is necessary to build overviews on the dataset as a whole using + * GDALDataset::BuildOverviews(). That makes this method pretty useless + * from a practical point of view. + * + * @param pszResampling one of "NEAREST", "GAUSS", "CUBIC", "AVERAGE", "MODE", + * "AVERAGE_MAGPHASE" or "NONE" controlling the downsampling method applied. + * @param nOverviews number of overviews to build. + * @param panOverviewList the list of overview decimation factors to build. + * @param pfnProgress a function to call to report progress, or NULL. + * @param pProgressData application data to pass to the progress function. + * + * @return CE_None on success or CE_Failure if the operation doesn't work. + */ + +CPLErr GDALRasterBand::BuildOverviews( const char * pszResampling, + int nOverviews, + int * panOverviewList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + (void) pszResampling; + (void) nOverviews; + (void) panOverviewList; + (void) pfnProgress; + (void) pProgressData; + + ReportError( CE_Failure, CPLE_NotSupported, + "BuildOverviews() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value offset. + * + * This value (in combination with the GetScale() value) is used to + * transform raw pixel values into the units returned by GetUnits(). + * For example this might be used to store elevations in GUInt16 bands + * with a precision of 0.1, and starting from -100. + * + * Units value = (raw value * scale) + offset + * + * For file formats that don't know this intrinsically a value of zero + * is returned. + * + * This method is the same as the C function GDALGetRasterOffset(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is meaningful or not. May be NULL (default). + * + * @return the raster offset. + */ + +double GDALRasterBand::GetOffset( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + return 0.0; +} + +/************************************************************************/ +/* GDALGetRasterOffset() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value offset. + * + * @see GDALRasterBand::GetOffset() + */ + +double CPL_STDCALL GDALGetRasterOffset( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterOffset", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetOffset( pbSuccess ); +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +/** + * \brief Set scaling offset. + * + * Very few formats implement this method. When not implemented it will + * issue a CPLE_NotSupported error and return CE_Failure. + * + * This method is the same as the C function GDALSetRasterOffset(). + * + * @param dfNewOffset the new offset. + * + * @return CE_None or success or CE_Failure on failure. + */ + +CPLErr GDALRasterBand::SetOffset( double dfNewOffset ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetOffset() not supported on this raster band." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterOffset() */ +/************************************************************************/ + +/** + * \brief Set scaling offset. + * + * @see GDALRasterBand::SetOffset() + */ + +CPLErr CPL_STDCALL +GDALSetRasterOffset( GDALRasterBandH hBand, double dfNewOffset ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterOffset", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetOffset( dfNewOffset ); +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value scale. + * + * This value (in combination with the GetOffset() value) is used to + * transform raw pixel values into the units returned by GetUnits(). + * For example this might be used to store elevations in GUInt16 bands + * with a precision of 0.1, and starting from -100. + * + * Units value = (raw value * scale) + offset + * + * For file formats that don't know this intrinsically a value of one + * is returned. + * + * This method is the same as the C function GDALGetRasterScale(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is meaningful or not. May be NULL (default). + * + * @return the raster scale. + */ + +double GDALRasterBand::GetScale( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + return 1.0; +} + +/************************************************************************/ +/* GDALGetRasterScale() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value scale. + * + * @see GDALRasterBand::GetScale() + */ + +double CPL_STDCALL GDALGetRasterScale( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterScale", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetScale( pbSuccess ); +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +/** + * \brief Set scaling ratio. + * + * Very few formats implement this method. When not implemented it will + * issue a CPLE_NotSupported error and return CE_Failure. + * + * This method is the same as the C function GDALSetRasterScale(). + * + * @param dfNewScale the new scale. + * + * @return CE_None or success or CE_Failure on failure. + */ + +CPLErr GDALRasterBand::SetScale( double dfNewScale ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetScale() not supported on this raster band." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterScale() */ +/************************************************************************/ + +/** + * \brief Set scaling ratio. + * + * @see GDALRasterBand::SetScale() + */ + +CPLErr CPL_STDCALL +GDALSetRasterScale( GDALRasterBandH hBand, double dfNewOffset ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterScale", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetScale( dfNewOffset ); +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +/** + * \brief Return raster unit type. + * + * Return a name for the units of this raster's values. For instance, it + * might be "m" for an elevation model in meters, or "ft" for feet. If no + * units are available, a value of "" will be returned. The returned string + * should not be modified, nor freed by the calling application. + * + * This method is the same as the C function GDALGetRasterUnitType(). + * + * @return unit name string. + */ + +const char *GDALRasterBand::GetUnitType() + +{ + return ""; +} + +/************************************************************************/ +/* GDALGetRasterUnitType() */ +/************************************************************************/ + +/** + * \brief Return raster unit type. + * + * @see GDALRasterBand::GetUnitType() + */ + +const char * CPL_STDCALL GDALGetRasterUnitType( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterUnitType", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetUnitType(); +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +/** + * \brief Set unit type. + * + * Set the unit type for a raster band. Values should be one of + * "" (the default indicating it is unknown), "m" indicating meters, + * or "ft" indicating feet, though other nonstandard values are allowed. + * + * This method is the same as the C function GDALSetRasterUnitType(). + * + * @param pszNewValue the new unit type value. + * + * @return CE_None on success or CE_Failure if not succuessful, or + * unsupported. + */ + +CPLErr GDALRasterBand::SetUnitType( const char *pszNewValue ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetUnitType() not supported on this raster band." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterUnitType() */ +/************************************************************************/ + +/** + * \brief Set unit type. + * + * @see GDALRasterBand::SetUnitType() + * + * @since GDAL 1.8.0 + */ + +CPLErr CPL_STDCALL GDALSetRasterUnitType( GDALRasterBandH hBand, const char *pszNewValue ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterUnitType", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetUnitType(pszNewValue); +} + +/************************************************************************/ +/* GetXSize() */ +/************************************************************************/ + +/** + * \brief Fetch XSize of raster. + * + * This method is the same as the C function GDALGetRasterBandXSize(). + * + * @return the width in pixels of this band. + */ + +int GDALRasterBand::GetXSize() + +{ + return nRasterXSize; +} + +/************************************************************************/ +/* GDALGetRasterBandXSize() */ +/************************************************************************/ + +/** + * \brief Fetch XSize of raster. + * + * @see GDALRasterBand::GetXSize() + */ + +int CPL_STDCALL GDALGetRasterBandXSize( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterBandXSize", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetXSize(); +} + +/************************************************************************/ +/* GetYSize() */ +/************************************************************************/ + +/** + * \brief Fetch YSize of raster. + * + * This method is the same as the C function GDALGetRasterBandYSize(). + * + * @return the height in pixels of this band. + */ + +int GDALRasterBand::GetYSize() + +{ + return nRasterYSize; +} + +/************************************************************************/ +/* GDALGetRasterBandYSize() */ +/************************************************************************/ + +/** + * \brief Fetch YSize of raster. + * + * @see GDALRasterBand::GetYSize() + */ + +int CPL_STDCALL GDALGetRasterBandYSize( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterBandYSize", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetYSize(); +} + +/************************************************************************/ +/* GetBand() */ +/************************************************************************/ + +/** + * \brief Fetch the band number. + * + * This method returns the band that this GDALRasterBand objects represents + * within it's dataset. This method may return a value of 0 to indicate + * GDALRasterBand objects without an apparently relationship to a dataset, + * such as GDALRasterBands serving as overviews. + * + * This method is the same as the C function GDALGetBandNumber(). + * + * @return band number (1+) or 0 if the band number isn't known. + */ + +int GDALRasterBand::GetBand() + +{ + return nBand; +} + +/************************************************************************/ +/* GDALGetBandNumber() */ +/************************************************************************/ + +/** + * \brief Fetch the band number. + * + * @see GDALRasterBand::GetBand() + */ + +int CPL_STDCALL GDALGetBandNumber( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetBandNumber", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetBand(); +} + +/************************************************************************/ +/* GetDataset() */ +/************************************************************************/ + +/** + * \brief Fetch the owning dataset handle. + * + * Note that some GDALRasterBands are not considered to be a part of a dataset, + * such as overviews or other "freestanding" bands. + * + * This method is the same as the C function GDALGetBandDataset(). + * + * @return the pointer to the GDALDataset to which this band belongs, or + * NULL if this cannot be determined. + */ + +GDALDataset *GDALRasterBand::GetDataset() + +{ + return poDS; +} + +/************************************************************************/ +/* GDALGetBandDataset() */ +/************************************************************************/ + +/** + * \brief Fetch the owning dataset handle. + * + * @see GDALRasterBand::GetDataset() + */ + +GDALDatasetH CPL_STDCALL GDALGetBandDataset( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetBandDataset", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALDatasetH) poBand->GetDataset(); +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +/** + * \brief Compute raster histogram. + * + * Note that the bucket size is (dfMax-dfMin) / nBuckets. + * + * For example to compute a simple 256 entry histogram of eight bit data, + * the following would be suitable. The unusual bounds are to ensure that + * bucket boundaries don't fall right on integer values causing possible errors + * due to rounding after scaling. +
    +    int anHistogram[256];
    +
    +    poBand->GetHistogram( -0.5, 255.5, 256, anHistogram, FALSE, FALSE, 
    +                          GDALDummyProgress, NULL );
    +
    + * + * Note that setting bApproxOK will generally result in a subsampling of the + * file, and will utilize overviews if available. It should generally + * produce a representative histogram for the data that is suitable for use + * in generating histogram based luts for instance. Generally bApproxOK is + * much faster than an exactly computed histogram. + * + * This method is the same as the C function GDALGetRasterHistogram(). + * + * @param dfMin the lower bound of the histogram. + * @param dfMax the upper bound of the histogram. + * @param nBuckets the number of buckets in panHistogram. + * @param panHistogram array into which the histogram totals are placed. + * @param bIncludeOutOfRange if TRUE values below the histogram range will + * mapped into panHistogram[0], and values above will be mapped into + * panHistogram[nBuckets-1] otherwise out of range values are discarded. + * @param bApproxOK TRUE if an approximate, or incomplete histogram OK. + * @param pfnProgress function to report progress to completion. + * @param pProgressData application data to pass to pfnProgress. + * + * @return CE_None on success, or CE_Failure if something goes wrong. + */ + +CPLErr GDALRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + CPLAssert( NULL != panHistogram ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* If we have overviews, use them for the histogram. */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + // FIXME: should we use the most reduced overview here or use some + // minimum number of samples like GDALRasterBand::ComputeStatistics() + // does? + GDALRasterBand *poBestOverview = GetRasterSampleOverview( 0 ); + + if( poBestOverview != this ) + { + return poBestOverview->GetHistogram( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Read actual data and build histogram. */ +/* -------------------------------------------------------------------- */ + double dfScale; + + if( !pfnProgress( 0.0, "Compute Histogram", pProgressData ) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + + dfScale = nBuckets / (dfMax - dfMin); + memset( panHistogram, 0, sizeof(int) * nBuckets ); + + double dfNoDataValue; + int bGotNoDataValue; + dfNoDataValue = GetNoDataValue( &bGotNoDataValue ); + bGotNoDataValue = bGotNoDataValue && !CPLIsNan(dfNoDataValue); + /* Not advertized. May be removed at any time. Just as a provision if the */ + /* old behaviour made sense somethimes... */ + bGotNoDataValue = bGotNoDataValue && + !CSLTestBoolean(CPLGetConfigOption("GDAL_NODATA_IN_HISTOGRAM", "NO")); + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if ( bApproxOK && HasArbitraryOverviews() ) + { +/* -------------------------------------------------------------------- */ +/* Figure out how much the image should be reduced to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + void *pData; + int nXReduced, nYReduced; + double dfReduction = sqrt( + (double)nRasterXSize * nRasterYSize / GDALSTAT_APPROX_NUMSAMPLES ); + + if ( dfReduction > 1.0 ) + { + nXReduced = (int)( nRasterXSize / dfReduction ); + nYReduced = (int)( nRasterYSize / dfReduction ); + + // Catch the case of huge resizing ratios here + if ( nXReduced == 0 ) + nXReduced = 1; + if ( nYReduced == 0 ) + nYReduced = 1; + } + else + { + nXReduced = nRasterXSize; + nYReduced = nRasterYSize; + } + + pData = + CPLMalloc(GDALGetDataTypeSize(eDataType)/8 * nXReduced * nYReduced); + + CPLErr eErr = IRasterIO( GF_Read, 0, 0, nRasterXSize, nRasterYSize, pData, + nXReduced, nYReduced, eDataType, 0, 0 ); + if ( eErr != CE_None ) + { + CPLFree(pData); + return eErr; + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYReduced; iY++ ) + { + for( int iX = 0; iX < nXReduced; iX++ ) + { + int iOffset = iX + iY * nXReduced; + int nIndex; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + { + double dfReal = ((GInt16 *)pData)[iOffset*2]; + double dfImag = ((GInt16 *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CInt32: + { + double dfReal = ((GInt32 *)pData)[iOffset*2]; + double dfImag = ((GInt32 *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat32: + { + double dfReal = ((float *)pData)[iOffset*2]; + double dfImag = ((float *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat64: + { + double dfReal = ((double *)pData)[iOffset*2]; + double dfImag = ((double *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + nIndex = (int) floor((dfValue - dfMin) * dfScale); + + if( nIndex < 0 ) + { + if( bIncludeOutOfRange ) + panHistogram[0]++; + } + else if( nIndex >= nBuckets ) + { + if( bIncludeOutOfRange ) + panHistogram[nBuckets-1]++; + } + else + { + panHistogram[nIndex]++; + } + } + } + + CPLFree( pData ); + } + + else // No arbitrary overviews + { + int nSampleRate; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + + if ( bApproxOK ) + { + nSampleRate = + (int) MAX(1,sqrt((double) nBlocksPerRow * nBlocksPerColumn)); + } + else + nSampleRate = 1; + +/* -------------------------------------------------------------------- */ +/* Read the blocks, and add to histogram. */ +/* -------------------------------------------------------------------- */ + for( int iSampleBlock = 0; + iSampleBlock < nBlocksPerRow * nBlocksPerColumn; + iSampleBlock += nSampleRate ) + { + int iXBlock, iYBlock, nXCheck, nYCheck; + GDALRasterBlock *poBlock; + void* pData; + + if( !pfnProgress( iSampleBlock + / ((double)nBlocksPerRow * nBlocksPerColumn), + "Compute Histogram", pProgressData ) ) + return CE_Failure; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + return CE_Failure; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + + pData = poBlock->GetDataRef(); + + if( (iXBlock+1) * nBlockXSize > GetXSize() ) + nXCheck = GetXSize() - iXBlock * nBlockXSize; + else + nXCheck = nBlockXSize; + + if( (iYBlock+1) * nBlockYSize > GetYSize() ) + nYCheck = GetYSize() - iYBlock * nBlockYSize; + else + nYCheck = nBlockYSize; + + /* this is a special case for a common situation */ + if( eDataType == GDT_Byte && !bSignedByte + && dfScale == 1.0 && (dfMin >= -0.5 && dfMin <= 0.5) + && nYCheck == nBlockYSize && nXCheck == nBlockXSize + && nBuckets == 256 ) + { + int nPixels = nXCheck * nYCheck; + GByte *pabyData = (GByte *) pData; + + for( int i = 0; i < nPixels; i++ ) + if (! (bGotNoDataValue && (pabyData[i] == (GByte)dfNoDataValue))) + { + panHistogram[pabyData[i]]++; + } + + poBlock->DropLock(); + continue; /* to next sample block */ + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYCheck; iY++ ) + { + for( int iX = 0; iX < nXCheck; iX++ ) + { + int iOffset = iX + iY * nBlockXSize; + int nIndex; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *) pData)[iOffset]; + else + dfValue = ((GByte *) pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *) pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *) pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *) pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *) pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *) pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *) pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + { + double dfReal = + ((GInt16 *) pData)[iOffset*2]; + double dfImag = + ((GInt16 *) pData)[iOffset*2+1]; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CInt32: + { + double dfReal = + ((GInt32 *) pData)[iOffset*2]; + double dfImag = + ((GInt32 *) pData)[iOffset*2+1]; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat32: + { + double dfReal = + ((float *) pData)[iOffset*2]; + double dfImag = + ((float *) pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat64: + { + double dfReal = + ((double *) pData)[iOffset*2]; + double dfImag = + ((double *) pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + default: + CPLAssert( FALSE ); + return CE_Failure; + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + nIndex = (int) floor((dfValue - dfMin) * dfScale); + + if( nIndex < 0 ) + { + if( bIncludeOutOfRange ) + panHistogram[0]++; + } + else if( nIndex >= nBuckets ) + { + if( bIncludeOutOfRange ) + panHistogram[nBuckets-1]++; + } + else + { + panHistogram[nIndex]++; + } + } + } + + poBlock->DropLock(); + } + } + + pfnProgress( 1.0, "Compute Histogram", pProgressData ); + + return CE_None; +} + +/************************************************************************/ +/* GDALGetRasterHistogram() */ +/************************************************************************/ + +/** + * \brief Compute raster histogram. + * + * @see GDALRasterBand::GetHistogram() + */ + +CPLErr CPL_STDCALL +GDALGetRasterHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterHistogram", CE_Failure ); + VALIDATE_POINTER1( panHistogram, "GDALGetRasterHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->GetHistogram( dfMin, dfMax, nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +/** + * \brief Fetch default raster histogram. + * + * The default method in GDALRasterBand will compute a default histogram. This + * method is overriden by derived classes (such as GDALPamRasterBand, VRTDataset, HFADataset...) + * that may be able to fetch efficiently an already stored histogram. + * + * This method is the same as the C function GDALGetDefaultHistogram(). + * + * @param pdfMin pointer to double value that will contain the lower bound of the histogram. + * @param pdfMax pointer to double value that will contain the upper bound of the histogram. + * @param pnBuckets pointer to int value that will contain the number of buckets in *ppanHistogram. + * @param ppanHistogram pointer to array into which the histogram totals are placed. To be freed with VSIFree + * @param bForce TRUE to force the computation. If FALSE and no default histogram is available, the method will return CE_Warning + * @param pfnProgress function to report progress to completion. + * @param pProgressData application data to pass to pfnProgress. + * + * @return CE_None on success, CE_Failure if something goes wrong, or + * CE_Warning if no default histogram is available. + */ + +CPLErr + GDALRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + CPLAssert( NULL != pnBuckets ); + CPLAssert( NULL != ppanHistogram ); + CPLAssert( NULL != pdfMin ); + CPLAssert( NULL != pdfMax ); + + *pnBuckets = 0; + *ppanHistogram = NULL; + + if( !bForce ) + return CE_Warning; + + int nBuckets = 256; + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if( GetRasterDataType() == GDT_Byte && !bSignedByte) + { + *pdfMin = -0.5; + *pdfMax = 255.5; + } + else + { + CPLErr eErr = CE_Failure; + double dfHalfBucket = 0; + + eErr = GetStatistics( TRUE, TRUE, pdfMin, pdfMax, NULL, NULL ); + dfHalfBucket = (*pdfMax - *pdfMin) / (2 * (nBuckets - 1)); + *pdfMin -= dfHalfBucket; + *pdfMax += dfHalfBucket; + + if( eErr != CE_None ) + return eErr; + } + + *ppanHistogram = (int *) VSICalloc(sizeof(int), nBuckets); + if( *ppanHistogram == NULL ) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in InitBlockInfo()." ); + return CE_Failure; + } + + *pnBuckets = nBuckets; + return GetHistogram( *pdfMin, *pdfMax, *pnBuckets, *ppanHistogram, + TRUE, FALSE, pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* GDALGetDefaultHistogram() */ +/************************************************************************/ + +/** + * \brief Fetch default raster histogram. + * + * @see GDALRasterBand::GetDefaultHistogram() + */ + +CPLErr CPL_STDCALL GDALGetDefaultHistogram( GDALRasterBandH hBand, + double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pdfMin, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pdfMax, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pnBuckets, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( ppanHistogram, "GDALGetDefaultHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetDefaultHistogram( pdfMin, pdfMax, + pnBuckets, ppanHistogram, bForce, pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +/** + * \brief Advise driver of upcoming read requests. + * + * Some GDAL drivers operate more efficiently if they know in advance what + * set of upcoming read requests will be made. The AdviseRead() method allows + * an application to notify the driver of the region of interest, + * and at what resolution the region will be read. + * + * Many drivers just ignore the AdviseRead() call, but it can dramatically + * accelerate access via some drivers. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param papszOptions a list of name=value strings with special control + * options. Normally this is NULL. + * + * @return CE_Failure if the request is invalid and CE_None if it works or + * is ignored. + */ + +CPLErr GDALRasterBand::AdviseRead( + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, GDALDataType eBufType, char **papszOptions ) + +{ + return CE_None; +} + +/************************************************************************/ +/* GDALRasterAdviseRead() */ +/************************************************************************/ + + +/** + * \brief Advise driver of upcoming read requests. + * + * @see GDALRasterBand::AdviseRead() + */ + +CPLErr CPL_STDCALL +GDALRasterAdviseRead( GDALRasterBandH hBand, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ) + +{ + VALIDATE_POINTER1( hBand, "GDALRasterAdviseRead", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, papszOptions ); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +/** + * \brief Fetch image statistics. + * + * Returns the minimum, maximum, mean and standard deviation of all + * pixel values in this band. If approximate statistics are sufficient, + * the bApproxOK flag can be set to true in which case overviews, or a + * subset of image tiles may be used in computing the statistics. + * + * If bForce is FALSE results will only be returned if it can be done + * quickly (ie. without scanning the data). If bForce is FALSE and + * results cannot be returned efficiently, the method will return CE_Warning + * but no warning will have been issued. This is a non-standard use of + * the CE_Warning return value to indicate "nothing done". + * + * Note that file formats using PAM (Persistent Auxilary Metadata) services + * will generally cache statistics in the .pam file allowing fast fetch + * after the first request. + * + * This method is the same as the C function GDALGetRasterStatistics(). + * + * @param bApproxOK If TRUE statistics may be computed based on overviews + * or a subset of all tiles. + * + * @param bForce If FALSE statistics will only be returned if it can + * be done without rescanning the image. + * + * @param pdfMin Location into which to load image minimum (may be NULL). + * + * @param pdfMax Location into which to load image maximum (may be NULL).- + * + * @param pdfMean Location into which to load image mean (may be NULL). + * + * @param pdfStdDev Location into which to load image standard deviation + * (may be NULL). + * + * @return CE_None on success, CE_Warning if no values returned, + * CE_Failure if an error occurs. + */ + +CPLErr GDALRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) + +{ + double dfMin=0.0, dfMax=0.0; + +/* -------------------------------------------------------------------- */ +/* Do we already have metadata items for the requested values? */ +/* -------------------------------------------------------------------- */ + if( (pdfMin == NULL || GetMetadataItem("STATISTICS_MINIMUM") != NULL) + && (pdfMax == NULL || GetMetadataItem("STATISTICS_MAXIMUM") != NULL) + && (pdfMean == NULL || GetMetadataItem("STATISTICS_MEAN") != NULL) + && (pdfStdDev == NULL || GetMetadataItem("STATISTICS_STDDEV") != NULL) ) + { + if( pdfMin != NULL ) + *pdfMin = CPLAtofM(GetMetadataItem("STATISTICS_MINIMUM")); + if( pdfMax != NULL ) + *pdfMax = CPLAtofM(GetMetadataItem("STATISTICS_MAXIMUM")); + if( pdfMean != NULL ) + *pdfMean = CPLAtofM(GetMetadataItem("STATISTICS_MEAN")); + if( pdfStdDev != NULL ) + *pdfStdDev = CPLAtofM(GetMetadataItem("STATISTICS_STDDEV")); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Does the driver already know the min/max? */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && pdfMean == NULL && pdfStdDev == NULL ) + { + int bSuccessMin, bSuccessMax; + + dfMin = GetMinimum( &bSuccessMin ); + dfMax = GetMaximum( &bSuccessMax ); + + if( bSuccessMin && bSuccessMax ) + { + if( pdfMin != NULL ) + *pdfMin = dfMin; + if( pdfMax != NULL ) + *pdfMax = dfMax; + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* Either return without results, or force computation. */ +/* -------------------------------------------------------------------- */ + if( !bForce ) + return CE_Warning; + else + return ComputeStatistics( bApproxOK, + pdfMin, pdfMax, pdfMean, pdfStdDev, + GDALDummyProgress, NULL ); +} + +/************************************************************************/ +/* GDALGetRasterStatistics() */ +/************************************************************************/ + +/** + * \brief Fetch image statistics. + * + * @see GDALRasterBand::GetStatistics() + */ + +CPLErr CPL_STDCALL GDALGetRasterStatistics( + GDALRasterBandH hBand, int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterStatistics", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetStatistics( + bApproxOK, bForce, pdfMin, pdfMax, pdfMean, pdfStdDev ); +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +/** + * \brief Compute image statistics. + * + * Returns the minimum, maximum, mean and standard deviation of all + * pixel values in this band. If approximate statistics are sufficient, + * the bApproxOK flag can be set to true in which case overviews, or a + * subset of image tiles may be used in computing the statistics. + * + * Once computed, the statistics will generally be "set" back on the + * raster band using SetStatistics(). + * + * This method is the same as the C function GDALComputeRasterStatistics(). + * + * @param bApproxOK If TRUE statistics may be computed based on overviews + * or a subset of all tiles. + * + * @param pdfMin Location into which to load image minimum (may be NULL). + * + * @param pdfMax Location into which to load image maximum (may be NULL).- + * + * @param pdfMean Location into which to load image mean (may be NULL). + * + * @param pdfStdDev Location into which to load image standard deviation + * (may be NULL). + * + * @param pfnProgress a function to call to report progress, or NULL. + * + * @param pProgressData application data to pass to the progress function. + * + * @return CE_None on success, or CE_Failure if an error occurs or processing + * is terminated by the user. + */ + +CPLErr +GDALRasterBand::ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* If we have overview bands, use them for statistics. */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + GDALRasterBand *poBand; + + poBand = GetRasterSampleOverview( GDALSTAT_APPROX_NUMSAMPLES ); + + if( poBand != this ) + return poBand->ComputeStatistics( FALSE, + pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData ); + } + +/* -------------------------------------------------------------------- */ +/* Read actual data and compute statistics. */ +/* -------------------------------------------------------------------- */ + double dfMin = 0.0, dfMax = 0.0; + int bGotNoDataValue, bFirstValue = TRUE; + /* Using Welford algorithm ( http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance ) */ + /* to compute standard deviation in a more numerically robust way than */ + /* the difference of the sum of square values with the square of the sum */ + /* dfMean and dfM2 are updated at each sample */ + /* dfM2 is the sum of square of differences to the current mean */ + double dfNoDataValue, dfMean = 0.0, dfM2 = 0.0; + GIntBig nSampleCount = 0; + + if( !pfnProgress( 0.0, "Compute Statistics", pProgressData ) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + + dfNoDataValue = GetNoDataValue( &bGotNoDataValue ); + bGotNoDataValue = bGotNoDataValue && !CPLIsNan(dfNoDataValue); + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if ( bApproxOK && HasArbitraryOverviews() ) + { +/* -------------------------------------------------------------------- */ +/* Figure out how much the image should be reduced to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + void *pData; + int nXReduced, nYReduced; + double dfReduction = sqrt( + (double)nRasterXSize * nRasterYSize / GDALSTAT_APPROX_NUMSAMPLES ); + + if ( dfReduction > 1.0 ) + { + nXReduced = (int)( nRasterXSize / dfReduction ); + nYReduced = (int)( nRasterYSize / dfReduction ); + + // Catch the case of huge resizing ratios here + if ( nXReduced == 0 ) + nXReduced = 1; + if ( nYReduced == 0 ) + nYReduced = 1; + } + else + { + nXReduced = nRasterXSize; + nYReduced = nRasterYSize; + } + + pData = + CPLMalloc(GDALGetDataTypeSize(eDataType)/8 * nXReduced * nYReduced); + + CPLErr eErr = IRasterIO( GF_Read, 0, 0, nRasterXSize, nRasterYSize, pData, + nXReduced, nYReduced, eDataType, 0, 0 ); + if ( eErr != CE_None ) + { + CPLFree(pData); + return eErr; + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYReduced; iY++ ) + { + for( int iX = 0; iX < nXReduced; iX++ ) + { + int iOffset = iX + iY * nXReduced; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *)pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *)pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + + nSampleCount++; + double dfDelta = dfValue - dfMean; + dfMean += dfDelta / nSampleCount; + dfM2 += dfDelta * (dfValue - dfMean); + } + } + + CPLFree( pData ); + } + + else // No arbitrary overviews + { + int nSampleRate; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK ) + { + nSampleRate = + (int)MAX( 1, sqrt((double)nBlocksPerRow * nBlocksPerColumn) ); + } + else + nSampleRate = 1; + + for( int iSampleBlock = 0; + iSampleBlock < nBlocksPerRow * nBlocksPerColumn; + iSampleBlock += nSampleRate ) + { + int iXBlock, iYBlock, nXCheck, nYCheck; + GDALRasterBlock *poBlock; + void* pData; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + continue; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + continue; + } + + pData = poBlock->GetDataRef(); + + if( (iXBlock+1) * nBlockXSize > GetXSize() ) + nXCheck = GetXSize() - iXBlock * nBlockXSize; + else + nXCheck = nBlockXSize; + + if( (iYBlock+1) * nBlockYSize > GetYSize() ) + nYCheck = GetYSize() - iYBlock * nBlockYSize; + else + nYCheck = nBlockYSize; + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYCheck; iY++ ) + { + for( int iX = 0; iX < nXCheck; iX++ ) + { + int iOffset = iX + iY * nBlockXSize; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *)pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *)pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + + nSampleCount++; + double dfDelta = dfValue - dfMean; + dfMean += dfDelta / nSampleCount; + dfM2 += dfDelta * (dfValue - dfMean); + } + } + + poBlock->DropLock(); + + if ( !pfnProgress(iSampleBlock + / ((double)(nBlocksPerRow*nBlocksPerColumn)), + "Compute Statistics", pProgressData) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + } + } + + if( !pfnProgress( 1.0, "Compute Statistics", pProgressData ) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Save computed information. */ +/* -------------------------------------------------------------------- */ + double dfStdDev = sqrt(dfM2 / nSampleCount); + + if( nSampleCount > 0 ) + SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + +/* -------------------------------------------------------------------- */ +/* Record results. */ +/* -------------------------------------------------------------------- */ + if( pdfMin != NULL ) + *pdfMin = dfMin; + if( pdfMax != NULL ) + *pdfMax = dfMax; + + if( pdfMean != NULL ) + *pdfMean = dfMean; + + if( pdfStdDev != NULL ) + *pdfStdDev = dfStdDev; + + if( nSampleCount > 0 ) + return CE_None; + else + { + ReportError( CE_Failure, CPLE_AppDefined, + "Failed to compute statistics, no valid pixels found in sampling." ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GDALComputeRasterStatistics() */ +/************************************************************************/ + +/** + * \brief Compute image statistics. + * + * @see GDALRasterBand::ComputeStatistics() + */ + +CPLErr CPL_STDCALL GDALComputeRasterStatistics( + GDALRasterBandH hBand, int bApproxOK, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALComputeRasterStatistics", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->ComputeStatistics( + bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +/** + * \brief Set statistics on band. + * + * This method can be used to store min/max/mean/standard deviation + * statistics on a raster band. + * + * The default implementation stores them as metadata, and will only work + * on formats that can save arbitrary metadata. This method cannot detect + * whether metadata will be properly saved and so may return CE_None even + * if the statistics will never be saved. + * + * This method is the same as the C function GDALSetRasterStatistics(). + * + * @param dfMin minimum pixel value. + * + * @param dfMax maximum pixel value. + * + * @param dfMean mean (average) of all pixel values. + * + * @param dfStdDev Standard deviation of all pixel values. + * + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr GDALRasterBand::SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ) + +{ + char szValue[128] = { 0 }; + + sprintf( szValue, "%.14g", dfMin ); + SetMetadataItem( "STATISTICS_MINIMUM", szValue ); + + sprintf( szValue, "%.14g", dfMax ); + SetMetadataItem( "STATISTICS_MAXIMUM", szValue ); + + sprintf( szValue, "%.14g", dfMean ); + SetMetadataItem( "STATISTICS_MEAN", szValue ); + + sprintf( szValue, "%.14g", dfStdDev ); + SetMetadataItem( "STATISTICS_STDDEV", szValue ); + + return CE_None; +} + +/************************************************************************/ +/* GDALSetRasterStatistics() */ +/************************************************************************/ + +/** + * \brief Set statistics on band. + * + * @see GDALRasterBand::SetStatistics() + */ + +CPLErr CPL_STDCALL GDALSetRasterStatistics( + GDALRasterBandH hBand, + double dfMin, double dfMax, double dfMean, double dfStdDev ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterStatistics", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +/** + * \brief Compute the min/max values for a band. + * + * If approximate is OK, then the band's GetMinimum()/GetMaximum() will + * be trusted. If it doesn't work, a subsample of blocks will be read to + * get an approximate min/max. If the band has a nodata value it will + * be excluded from the minimum and maximum. + * + * If bApprox is FALSE, then all pixels will be read and used to compute + * an exact range. + * + * This method is the same as the C function GDALComputeRasterMinMax(). + * + * @param bApproxOK TRUE if an approximate (faster) answer is OK, otherwise + * FALSE. + * @param adfMinMax the array in which the minimum (adfMinMax[0]) and the + * maximum (adfMinMax[1]) are returned. + * + * @return CE_None on success or CE_Failure on failure. + */ + + +CPLErr GDALRasterBand::ComputeRasterMinMax( int bApproxOK, + double adfMinMax[2] ) +{ + double dfMin = 0.0; + double dfMax = 0.0; + +/* -------------------------------------------------------------------- */ +/* Does the driver already know the min/max? */ +/* -------------------------------------------------------------------- */ + if( bApproxOK ) + { + int bSuccessMin, bSuccessMax; + + dfMin = GetMinimum( &bSuccessMin ); + dfMax = GetMaximum( &bSuccessMax ); + + if( bSuccessMin && bSuccessMax ) + { + adfMinMax[0] = dfMin; + adfMinMax[1] = dfMax; + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have overview bands, use them for min/max. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + GDALRasterBand *poBand; + + poBand = GetRasterSampleOverview( GDALSTAT_APPROX_NUMSAMPLES ); + + if ( poBand != this ) + return poBand->ComputeRasterMinMax( FALSE, adfMinMax ); + } + +/* -------------------------------------------------------------------- */ +/* Read actual data and compute minimum and maximum. */ +/* -------------------------------------------------------------------- */ + int bGotNoDataValue, bFirstValue = TRUE; + double dfNoDataValue; + + dfNoDataValue = GetNoDataValue( &bGotNoDataValue ); + bGotNoDataValue = bGotNoDataValue && !CPLIsNan(dfNoDataValue); + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if ( bApproxOK && HasArbitraryOverviews() ) + { +/* -------------------------------------------------------------------- */ +/* Figure out how much the image should be reduced to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + void *pData; + int nXReduced, nYReduced; + double dfReduction = sqrt( + (double)nRasterXSize * nRasterYSize / GDALSTAT_APPROX_NUMSAMPLES ); + + if ( dfReduction > 1.0 ) + { + nXReduced = (int)( nRasterXSize / dfReduction ); + nYReduced = (int)( nRasterYSize / dfReduction ); + + // Catch the case of huge resizing ratios here + if ( nXReduced == 0 ) + nXReduced = 1; + if ( nYReduced == 0 ) + nYReduced = 1; + } + else + { + nXReduced = nRasterXSize; + nYReduced = nRasterYSize; + } + + pData = + CPLMalloc(GDALGetDataTypeSize(eDataType)/8 * nXReduced * nYReduced); + + CPLErr eErr = IRasterIO( GF_Read, 0, 0, nRasterXSize, nRasterYSize, pData, + nXReduced, nYReduced, eDataType, 0, 0 ); + if ( eErr != CE_None ) + { + CPLFree(pData); + return eErr; + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYReduced; iY++ ) + { + for( int iX = 0; iX < nXReduced; iX++ ) + { + int iOffset = iX + iY * nXReduced; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *)pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *)pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + } + } + + CPLFree( pData ); + } + + else // No arbitrary overviews + { + int nSampleRate; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK ) + { + nSampleRate = + (int) MAX(1,sqrt((double) nBlocksPerRow * nBlocksPerColumn)); + } + else + nSampleRate = 1; + + for( int iSampleBlock = 0; + iSampleBlock < nBlocksPerRow * nBlocksPerColumn; + iSampleBlock += nSampleRate ) + { + int iXBlock, iYBlock, nXCheck, nYCheck; + GDALRasterBlock *poBlock; + void* pData; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + continue; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + continue; + } + + pData = poBlock->GetDataRef(); + + if( (iXBlock+1) * nBlockXSize > GetXSize() ) + nXCheck = GetXSize() - iXBlock * nBlockXSize; + else + nXCheck = nBlockXSize; + + if( (iYBlock+1) * nBlockYSize > GetYSize() ) + nYCheck = GetYSize() - iYBlock * nBlockYSize; + else + nYCheck = nBlockYSize; + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYCheck; iY++ ) + { + for( int iX = 0; iX < nXCheck; iX++ ) + { + int iOffset = iX + iY * nBlockXSize; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *) pData)[iOffset]; + else + dfValue = ((GByte *) pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *) pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *) pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *) pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *) pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *) pData)[iOffset]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_Float64: + dfValue = ((double *) pData)[iOffset]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *) pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *) pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *) pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *) pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + } + } + + poBlock->DropLock(); + } + } + + adfMinMax[0] = dfMin; + adfMinMax[1] = dfMax; + + if (bFirstValue) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Failed to compute min/max, no valid pixels found in sampling." ); + return CE_Failure; + } + else + { + return CE_None; + } +} + +/************************************************************************/ +/* GDALComputeRasterMinMax() */ +/************************************************************************/ + +/** + * \brief Compute the min/max values for a band. + * + * @see GDALRasterBand::ComputeRasterMinMax() + */ + +void CPL_STDCALL +GDALComputeRasterMinMax( GDALRasterBandH hBand, int bApproxOK, + double adfMinMax[2] ) + +{ + VALIDATE_POINTER0( hBand, "GDALComputeRasterMinMax" ); + + GDALRasterBand *poBand = static_cast(hBand); + poBand->ComputeRasterMinMax( bApproxOK, adfMinMax ); +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +/* FIXME : add proper documentation */ +/** + * \brief Set default histogram. + * + * This method is the same as the C function GDALSetDefaultHistogram(). + */ +CPLErr GDALRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, int *panHistogram ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetDefaultHistogram() not implemented for this format." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetDefaultHistogram() */ +/************************************************************************/ + +/** + * \brief Set default histogram. + * + * @see GDALRasterBand::SetDefaultHistogram() + */ + +CPLErr CPL_STDCALL GDALSetDefaultHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetDefaultHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets, panHistogram ); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Fetch default Raster Attribute Table. + * + * A RAT will be returned if there is a default one associated with the + * band, otherwise NULL is returned. The returned RAT is owned by the + * band and should not be deleted by the application. + * + * This method is the same as the C function GDALGetDefaultRAT(). + * + * @return NULL, or a pointer to an internal RAT owned by the band. + */ + +GDALRasterAttributeTable *GDALRasterBand::GetDefaultRAT() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Fetch default Raster Attribute Table. + * + * @see GDALRasterBand::GetDefaultRAT() + */ + +GDALRasterAttributeTableH CPL_STDCALL GDALGetDefaultRAT( GDALRasterBandH hBand) + +{ + VALIDATE_POINTER1( hBand, "GDALGetDefaultRAT", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterAttributeTableH) poBand->GetDefaultRAT(); +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Set default Raster Attribute Table. + * + * Associates a default RAT with the band. If not implemented for the + * format a CPLE_NotSupported error will be issued. If successful a copy + * of the RAT is made, the original remains owned by the caller. + * + * This method is the same as the C function GDALSetDefaultRAT(). + * + * @param poRAT the RAT to assign to the band. + * + * @return CE_None on success or CE_Failure if unsupported or otherwise + * failing. + */ + +CPLErr GDALRasterBand::SetDefaultRAT( const GDALRasterAttributeTable *poRAT ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetDefaultRAT() not implemented for this format." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Set default Raster Attribute Table. + * + * @see GDALRasterBand::GDALSetDefaultRAT() + */ + +CPLErr CPL_STDCALL GDALSetDefaultRAT( GDALRasterBandH hBand, + GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetDefaultRAT", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->SetDefaultRAT( + static_cast(hRAT) ); +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +/** + * \brief Return the mask band associated with the band. + * + * The GDALRasterBand class includes a default implementation of GetMaskBand() that + * returns one of four default implementations : + *
      + *
    • If a corresponding .msk file exists it will be used for the mask band.
    • + *
    • If the dataset has a NODATA_VALUES metadata item, an instance of the + * new GDALNoDataValuesMaskBand class will be returned. + * GetMaskFlags() will return GMF_NODATA | GMF_PER_DATASET. @since GDAL 1.6.0
    • + *
    • If the band has a nodata value set, an instance of the new + * GDALNodataMaskRasterBand class will be returned. + * GetMaskFlags() will return GMF_NODATA.
    • + *
    • If there is no nodata value, but the dataset has an alpha band that seems + * to apply to this band (specific rules yet to be determined) and that is + * of type GDT_Byte then that alpha band will be returned, and the flags + * GMF_PER_DATASET and GMF_ALPHA will be returned in the flags.
    • + *
    • If neither of the above apply, an instance of the new GDALAllValidRasterBand + * class will be returned that has 255 values for all pixels. + * The null flags will return GMF_ALL_VALID.
    • + *
    + * + * Note that the GetMaskBand() should always return a GDALRasterBand mask, even if it is only + * an all 255 mask with the flags indicating GMF_ALL_VALID. + * + * This method is the same as the C function GDALGetMaskBand(). + * + * @return a valid mask band. + * + * @since GDAL 1.5.0 + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ +GDALRasterBand *GDALRasterBand::GetMaskBand() + +{ + if( poMask != NULL ) + return poMask; + +/* -------------------------------------------------------------------- */ +/* Check for a mask in a .msk file. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS = GetDataset(); + + if( poDS != NULL && poDS->oOvManager.HaveMaskFile() ) + { + poMask = poDS->oOvManager.GetMaskBand( nBand ); + if( poMask != NULL ) + { + nMaskFlags = poDS->oOvManager.GetMaskFlags( nBand ); + return poMask; + } + } + +/* -------------------------------------------------------------------- */ +/* Check for NODATA_VALUES metadata. */ +/* -------------------------------------------------------------------- */ + if (poDS != NULL) + { + const char* pszNoDataValues = poDS->GetMetadataItem("NODATA_VALUES"); + if (pszNoDataValues != NULL) + { + char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE); + + /* Make sure we have as many values as bands */ + if (CSLCount(papszNoDataValues) == poDS->GetRasterCount() && poDS->GetRasterCount() != 0) + { + /* Make sure that all bands have the same data type */ + /* This is cleraly not a fundamental condition, just a condition to make implementation */ + /* easier. */ + int i; + GDALDataType eDT = GDT_Unknown; + for(i=0;iGetRasterCount();i++) + { + if (i == 0) + eDT = poDS->GetRasterBand(1)->GetRasterDataType(); + else if (eDT != poDS->GetRasterBand(i + 1)->GetRasterDataType()) + { + break; + } + } + if (i == poDS->GetRasterCount()) + { + nMaskFlags = GMF_NODATA | GMF_PER_DATASET; + poMask = new GDALNoDataValuesMaskBand ( poDS ); + bOwnMask = true; + CSLDestroy(papszNoDataValues); + return poMask; + } + else + { + ReportError(CE_Warning, CPLE_AppDefined, + "All bands should have the same type in order the NODATA_VALUES metadata item to be used as a mask."); + } + } + else + { + ReportError(CE_Warning, CPLE_AppDefined, + "NODATA_VALUES metadata item doesn't have the same number of values as the number of bands.\n" + "Ignoring it for mask."); + } + + CSLDestroy(papszNoDataValues); + } + } + +/* -------------------------------------------------------------------- */ +/* Check for nodata case. */ +/* -------------------------------------------------------------------- */ + int bHaveNoData; + + GetNoDataValue( &bHaveNoData ); + + if( bHaveNoData ) + { + nMaskFlags = GMF_NODATA; + poMask = new GDALNoDataMaskBand( this ); + bOwnMask = true; + return poMask; + } + +/* -------------------------------------------------------------------- */ +/* Check for alpha case. */ +/* -------------------------------------------------------------------- */ + if( poDS != NULL + && poDS->GetRasterCount() == 2 + && this == poDS->GetRasterBand(1) + && poDS->GetRasterBand(2)->GetColorInterpretation() == GCI_AlphaBand + && poDS->GetRasterBand(2)->GetRasterDataType() == GDT_Byte ) + { + nMaskFlags = GMF_ALPHA | GMF_PER_DATASET; + poMask = poDS->GetRasterBand(2); + return poMask; + } + + if( poDS != NULL + && poDS->GetRasterCount() == 4 + && (this == poDS->GetRasterBand(1) + || this == poDS->GetRasterBand(2) + || this == poDS->GetRasterBand(3)) + && poDS->GetRasterBand(4)->GetColorInterpretation() == GCI_AlphaBand + && poDS->GetRasterBand(4)->GetRasterDataType() == GDT_Byte ) + { + nMaskFlags = GMF_ALPHA | GMF_PER_DATASET; + poMask = poDS->GetRasterBand(4); + return poMask; + } + +/* -------------------------------------------------------------------- */ +/* Fallback to all valid case. */ +/* -------------------------------------------------------------------- */ + nMaskFlags = GMF_ALL_VALID; + poMask = new GDALAllValidMaskBand( this ); + bOwnMask = true; + + return poMask; +} + +/************************************************************************/ +/* GDALGetMaskBand() */ +/************************************************************************/ + +/** + * \brief Return the mask band associated with the band. + * + * @see GDALRasterBand::GetMaskBand() + */ + +GDALRasterBandH CPL_STDCALL GDALGetMaskBand( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetMaskBand", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMaskBand(); +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +/** + * \brief Return the status flags of the mask band associated with the band. + * + * The GetMaskFlags() method returns an bitwise OR-ed set of status flags with + * the following available definitions that may be extended in the future: + *
      + *
    • GMF_ALL_VALID(0x01): There are no invalid pixels, all mask values will be 255. + * When used this will normally be the only flag set.
    • + *
    • GMF_PER_DATASET(0x02): The mask band is shared between all bands on the dataset.
    • + *
    • GMF_ALPHA(0x04): The mask band is actually an alpha band and may have values + * other than 0 and 255.
    • + *
    • GMF_NODATA(0x08): Indicates the mask is actually being generated from nodata values. + * (mutually exclusive of GMF_ALPHA)
    • + *
    + * + * The GDALRasterBand class includes a default implementation of GetMaskBand() that + * returns one of four default implementations : + *
      + *
    • If a corresponding .msk file exists it will be used for the mask band.
    • + *
    • If the dataset has a NODATA_VALUES metadata item, an instance of the + * new GDALNoDataValuesMaskBand class will be returned. + * GetMaskFlags() will return GMF_NODATA | GMF_PER_DATASET. @since GDAL 1.6.0
    • + *
    • If the band has a nodata value set, an instance of the new + * GDALNodataMaskRasterBand class will be returned. + * GetMaskFlags() will return GMF_NODATA.
    • + *
    • If there is no nodata value, but the dataset has an alpha band that seems + * to apply to this band (specific rules yet to be determined) and that is + * of type GDT_Byte then that alpha band will be returned, and the flags + * GMF_PER_DATASET and GMF_ALPHA will be returned in the flags.
    • + *
    • If neither of the above apply, an instance of the new GDALAllValidRasterBand + * class will be returned that has 255 values for all pixels. + * The null flags will return GMF_ALL_VALID.
    • + *
    + * + * This method is the same as the C function GDALGetMaskFlags(). + * + * @since GDAL 1.5.0 + * + * @return a valid mask band. + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ +int GDALRasterBand::GetMaskFlags() + +{ + // If we don't have a band yet, force this now so that the masks value + // will be initialized. + + if( poMask == NULL ) + GetMaskBand(); + + return nMaskFlags; +} + +/************************************************************************/ +/* GDALGetMaskFlags() */ +/************************************************************************/ + +/** + * \brief Return the status flags of the mask band associated with the band. + * + * @see GDALRasterBand::GetMaskFlags() + */ + +int CPL_STDCALL GDALGetMaskFlags( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetMaskFlags", GMF_ALL_VALID ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMaskFlags(); +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the current band + * + * The default implementation of the CreateMaskBand() method is implemented + * based on similar rules to the .ovr handling implemented using the + * GDALDefaultOverviews object. A TIFF file with the extension .msk will + * be created with the same basename as the original file, and it will have + * as many bands as the original image (or just one for GMF_PER_DATASET). + * The mask images will be deflate compressed tiled images with the same + * block size as the original image if possible. + * + * Note that if you got a mask band with a previous call to GetMaskBand(), + * it might be invalidated by CreateMaskBand(). So you have to call GetMaskBand() + * again. + * + * This method is the same as the C function GDALCreateMaskBand(). + * + * @since GDAL 1.5.0 + * + * @return CE_None on success or CE_Failure on an error. + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ + +CPLErr GDALRasterBand::CreateMaskBand( int nFlags ) + +{ + if( poDS != NULL && poDS->oOvManager.IsInitialized() ) + { + CPLErr eErr = poDS->oOvManager.CreateMaskBand( nFlags, nBand ); + if (eErr != CE_None) + return eErr; + + /* Invalidate existing raster band mask */ + if (bOwnMask) + delete poMask; + bOwnMask = false; + poMask = NULL; + + return CE_None; + } + + ReportError( CE_Failure, CPLE_NotSupported, + "CreateMaskBand() not supported for this band." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALCreateMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the current band + * + * @see GDALRasterBand::CreateMaskBand() + */ + +CPLErr CPL_STDCALL GDALCreateMaskBand( GDALRasterBandH hBand, int nFlags ) + +{ + VALIDATE_POINTER1( hBand, "GDALCreateMaskBand", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->CreateMaskBand( nFlags ); +} + +/************************************************************************/ +/* GetIndexColorTranslationTo() */ +/************************************************************************/ + +/** + * \brief Compute translation table for color tables. + * + * When the raster band has a palette index, it may be usefull to compute + * the "translation" of this palette to the palette of another band. + * The translation tries to do exact matching first, and then approximate + * matching if no exact matching is possible. + * This method returns a table such that table[i] = j where i is an index + * of the 'this' rasterband and j the corresponding index for the reference + * rasterband. + * + * This method is thought as internal to GDAL and is used for drivers + * like RPFTOC. + * + * The implementation only supports 1-byte palette rasterbands. + * + * @param poReferenceBand the raster band + * @param pTranslationTable an already allocated translation table (at least 256 bytes), + * or NULL to let the method allocate it + * @param pApproximateMatching a pointer to a flag that is set if the matching + * is approximate. May be NULL. + * + * @return a translation table if the two bands are palette index and that they do + * not match or NULL in other cases. + * The table must be freed with CPLFree if NULL was passed for pTranslationTable. + */ + +unsigned char* GDALRasterBand::GetIndexColorTranslationTo(GDALRasterBand* poReferenceBand, + unsigned char* pTranslationTable, + int* pApproximateMatching ) +{ + if (poReferenceBand == NULL) + return NULL; + + if (poReferenceBand->GetColorInterpretation() == GCI_PaletteIndex && + GetColorInterpretation() == GCI_PaletteIndex && + poReferenceBand->GetRasterDataType() == GDT_Byte && + GetRasterDataType() == GDT_Byte) + { + GDALColorTable* srcColorTable = GetColorTable(); + GDALColorTable* destColorTable = poReferenceBand->GetColorTable(); + if (srcColorTable != NULL && destColorTable != NULL) + { + int nEntries = srcColorTable->GetColorEntryCount(); + int nRefEntries = destColorTable->GetColorEntryCount(); + int bHasNoDataValueSrc; + int noDataValueSrc = (int)GetNoDataValue(&bHasNoDataValueSrc); + int bHasNoDataValueRef; + int noDataValueRef = (int)poReferenceBand->GetNoDataValue(&bHasNoDataValueRef); + int samePalette; + int i, j; + + if (pApproximateMatching) + *pApproximateMatching = FALSE; + + if (nEntries == nRefEntries && bHasNoDataValueSrc == bHasNoDataValueRef && + (bHasNoDataValueSrc == FALSE || noDataValueSrc == noDataValueRef)) + { + samePalette = TRUE; + for(i=0;iGetColorEntry(i); + const GDALColorEntry* entryRef = destColorTable->GetColorEntry(i); + if (entry->c1 != entryRef->c1 || + entry->c2 != entryRef->c2 || + entry->c3 != entryRef->c3) + { + samePalette = FALSE; + } + } + } + else + { + samePalette = FALSE; + } + if (samePalette == FALSE) + { + if (pTranslationTable == NULL) + pTranslationTable = (unsigned char*)CPLMalloc(256); + + /* Trying to remap the product palette on the subdataset palette */ + for(i=0;iGetColorEntry(i); + for(j=0;jGetColorEntry(j); + if (entry->c1 == entryRef->c1 && + entry->c2 == entryRef->c2 && + entry->c3 == entryRef->c3) + { + pTranslationTable[i] = (unsigned char) j; + break; + } + } + if (j == nEntries) + { + /* No exact match. Looking for closest color now... */ + int best_j = 0; + int best_distance = 0; + if (pApproximateMatching) + *pApproximateMatching = TRUE; + for(j=0;jGetColorEntry(j); + int distance = (entry->c1 - entryRef->c1) * (entry->c1 - entryRef->c1) + + (entry->c2 - entryRef->c2) * (entry->c2 - entryRef->c2) + + (entry->c3 - entryRef->c3) * (entry->c3 - entryRef->c3); + if (j == 0 || distance < best_distance) + { + best_j = j; + best_distance = distance; + } + } + pTranslationTable[i] = (unsigned char) best_j; + } + } + if (bHasNoDataValueRef && bHasNoDataValueSrc) + pTranslationTable[noDataValueSrc] = (unsigned char) noDataValueRef; + + return pTranslationTable; + } + } + } + return NULL; +} + +/************************************************************************/ +/* SetFlushBlockErr() */ +/************************************************************************/ + +/** + * \brief Store that an error occured while writing a dirty block. + * + * This function stores the fact that an error occured while writing a dirty + * block from GDALRasterBlock::FlushCacheBlock(). Indeed when dirty blocks are + * flushed when the block cache get full, it is not convenient/possible to + * report that a dirty block could not be written correctly. This function + * remembers the error and re-issue it from GDALRasterBand::FlushCache(), + * GDALRasterBand::WriteBlock() and GDALRasterBand::RasterIO(), which are + * places where the user can easily match the error with the relevant dataset. + */ + +void GDALRasterBand::SetFlushBlockErr( CPLErr eErr ) +{ + eFlushBlockErr = eErr; +} + +/************************************************************************/ +/* ReportError() */ +/************************************************************************/ + +/** + * \brief Emits an error related to a raster band. + * + * This function is a wrapper for regular CPLError(). The only difference + * with CPLError() is that it prepends the error message with the dataset + * name and the band number. + * + * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. + * @param err_no the error number (CPLE_*) from cpl_error.h. + * @param fmt a printf() style format string. Any additional arguments + * will be treated as arguments to fill in this format in a manner + * similar to printf(). + * + * @since GDAL 1.9.0 + */ + +void GDALRasterBand::ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + char szNewFmt[256]; + const char* pszDSName = poDS ? poDS->GetDescription() : ""; + if (strlen(fmt) + strlen(pszDSName) + 20 >= sizeof(szNewFmt) - 1) + pszDSName = CPLGetFilename(pszDSName); + if (pszDSName[0] != '\0' && + strlen(fmt) + strlen(pszDSName) + 20 < sizeof(szNewFmt) - 1) + { + snprintf(szNewFmt, sizeof(szNewFmt), "%s, band %d: %s", + pszDSName, GetBand(), fmt); + CPLErrorV( eErrClass, err_no, szNewFmt, args ); + } + else + { + CPLErrorV( eErrClass, err_no, fmt, args ); + } + va_end(args); +} + + +/************************************************************************/ +/* GetVirtualMemAuto() */ +/************************************************************************/ + +/** \brief Create a CPLVirtualMem object from a GDAL raster band object. + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a GDALRasterBand, + * that exposes the whole image data as a virtual array. + * + * The default implementation relies on GDALRasterBandGetVirtualMem(), but specialized + * implementation, such as for raw files, may also directly use mechanisms of the + * operating system to create a view of the underlying file into virtual memory + * ( CPLVirtualMemFileMapNew() ) + * + * At the time of writing, the GeoTIFF driver and "raw" drivers (EHdr, ...) offer + * a specialized implementation with direct file mapping, provided that some + * requirements are met : + * - for all drivers, the dataset must be backed by a "real" file in the file + * system, and the byte ordering of multi-byte datatypes (Int16, etc.) + * must match the native ordering of the CPU. + * - in addition, for the GeoTIFF driver, the GeoTIFF file must be uncompressed, scanline + * oriented (i.e. not tiled). Strips must be organized in the file in sequential + * order, and be equally spaced (which is generally the case). Only power-of-two + * bit depths are supported (8 for GDT_Bye, 16 for GDT_Int16/GDT_UInt16, + * 32 for GDT_Float32 and 64 for GDT_Float64) + * + * The pointer returned remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the raster band object is destroyed. + * + * If p is such a pointer and base_type the type matching GDALGetRasterDataType(), + * the element of image coordinates (x, y) can be accessed with + * *(base_type*) ((GByte*)p + x * *pnPixelSpace + y * *pnLineSpace) + * + * This method is the same as the C GDALGetVirtualMemAuto() function. + * + * @param eRWFlag Either GF_Read to read the band, or GF_Write to + * read/write the band. + * + * @param pnPixelSpace Output parameter giving the byte offset from the start of one pixel value in + * the buffer to the start of the next pixel value within a scanline. + * + * @param pnLineSpace Output parameter giving the byte offset from the start of one scanline in + * the buffer to the start of the next. + * + * @param papszOptions NULL terminated list of options. + * If a specialized implementation exists, defining USE_DEFAULT_IMPLEMENTATION=YES + * will cause the default implementation to be used. + * When requiring or falling back to the default implementation, the following + * options are available : CACHE_SIZE (in bytes, defaults to 40 MB), + * PAGE_SIZE_HINT (in bytes), + * SINGLE_THREAD ("FALSE" / "TRUE", defaults to FALSE) + * + * @return a virtual memory object that must be unreferenced by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem *GDALRasterBand::GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ) +{ + int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8; + GIntBig nLineSpace = (GIntBig)nRasterXSize * nPixelSpace; + if( pnPixelSpace ) + *pnPixelSpace = nPixelSpace; + if( pnLineSpace ) + *pnLineSpace = nLineSpace; + size_t nCacheSize = atoi(CSLFetchNameValueDef(papszOptions, + "CACHE_SIZE", "40000000")); + size_t nPageSizeHint = atoi(CSLFetchNameValueDef(papszOptions, + "PAGE_SIZE_HINT", "0")); + int bSingleThreadUsage = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, + "SINGLE_THREAD", "FALSE")); + return GDALRasterBandGetVirtualMem( (GDALRasterBandH) this, + eRWFlag, + 0, 0, nRasterXSize, nRasterYSize, + nRasterXSize, nRasterYSize, + eDataType, + nPixelSpace, nLineSpace, + nCacheSize, + nPageSizeHint, + bSingleThreadUsage, + papszOptions ); +} + +/************************************************************************/ +/* GDALGetVirtualMemAuto() */ +/************************************************************************/ + +/** + * \brief Create a CPLVirtualMem object from a GDAL raster band object. + * + * @see GDALRasterBand::GetVirtualMemAuto() + */ + +CPLVirtualMem * GDALGetVirtualMemAuto( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ) +{ + VALIDATE_POINTER1( hBand, "GDALGetVirtualMemAuto", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->GetVirtualMemAuto(eRWFlag, pnPixelSpace, + pnLineSpace, papszOptions); +} + diff --git a/ogr/gdalrasterblock.cpp b/ogr/gdalrasterblock.cpp new file mode 100644 index 0000000..5e396ec --- /dev/null +++ b/ogr/gdalrasterblock.cpp @@ -0,0 +1,715 @@ +/****************************************************************************** + * $Id: gdalrasterblock.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALRasterBlock class and related global + * raster block cache management. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdalrasterblock.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +static int bCacheMaxInitialized = FALSE; +static GIntBig nCacheMax = 40 * 1024*1024; +static volatile GIntBig nCacheUsed = 0; + +static volatile GDALRasterBlock *poOldest = NULL; /* tail */ +static volatile GDALRasterBlock *poNewest = NULL; /* head */ + +static void *hRBMutex = NULL; + +/************************************************************************/ +/* GDALSetCacheMax() */ +/************************************************************************/ + +/** + * \brief Set maximum cache memory. + * + * This function sets the maximum amount of memory that GDAL is permitted + * to use for GDALRasterBlock caching. The unit of the value is bytes. + * + * The maximum value is 2GB, due to the use of a signed 32 bit integer. + * Use GDALSetCacheMax64() to be able to set a higher value. + * + * @param nNewSizeInBytes the maximum number of bytes for caching. + */ + +void CPL_STDCALL GDALSetCacheMax( int nNewSizeInBytes ) + +{ + GDALSetCacheMax64(nNewSizeInBytes); +} + + +/************************************************************************/ +/* GDALSetCacheMax64() */ +/************************************************************************/ + +/** + * \brief Set maximum cache memory. + * + * This function sets the maximum amount of memory that GDAL is permitted + * to use for GDALRasterBlock caching. The unit of the value is bytes. + * + * Note: On 32 bit platforms, the maximum amount of memory that can be addressed + * by a process might be 2 GB or 3 GB, depending on the operating system + * capabilities. This function will not make any attempt to check the + * consistency of the passed value with the effective capabilities of the OS. + * + * @param nNewSizeInBytes the maximum number of bytes for caching. + * + * @since GDAL 1.8.0 + */ + +void CPL_STDCALL GDALSetCacheMax64( GIntBig nNewSizeInBytes ) + +{ + bCacheMaxInitialized = TRUE; + nCacheMax = nNewSizeInBytes; + +/* -------------------------------------------------------------------- */ +/* Flush blocks till we are under the new limit or till we */ +/* can't seem to flush anymore. */ +/* -------------------------------------------------------------------- */ + while( nCacheUsed > nCacheMax ) + { + GIntBig nOldCacheUsed = nCacheUsed; + + GDALFlushCacheBlock(); + + if( nCacheUsed == nOldCacheUsed ) + break; + } +} + +/************************************************************************/ +/* GDALGetCacheMax() */ +/************************************************************************/ + +/** + * \brief Get maximum cache memory. + * + * Gets the maximum amount of memory available to the GDALRasterBlock + * caching system for caching GDAL read/write imagery. + * + * The first type this function is called, it will read the GDAL_CACHEMAX + * configuation option to initialize the maximum cache memory. + * + * This function cannot return a value higher than 2 GB. Use + * GDALGetCacheMax64() to get a non-truncated value. + * + * @return maximum in bytes. + */ + +int CPL_STDCALL GDALGetCacheMax() +{ + GIntBig nRes = GDALGetCacheMax64(); + if (nRes > INT_MAX) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cache max value doesn't fit on a 32 bit integer. " + "Call GDALGetCacheMax64() instead"); + bHasWarned = TRUE; + } + nRes = INT_MAX; + } + return (int)nRes; +} + +/************************************************************************/ +/* GDALGetCacheMax64() */ +/************************************************************************/ + +/** + * \brief Get maximum cache memory. + * + * Gets the maximum amount of memory available to the GDALRasterBlock + * caching system for caching GDAL read/write imagery. + * + * The first type this function is called, it will read the GDAL_CACHEMAX + * configuation option to initialize the maximum cache memory. + * + * @return maximum in bytes. + * + * @since GDAL 1.8.0 + */ + +GIntBig CPL_STDCALL GDALGetCacheMax64() +{ + if( !bCacheMaxInitialized ) + { + const char* pszCacheMax = CPLGetConfigOption("GDAL_CACHEMAX",NULL); + bCacheMaxInitialized = TRUE; + if( pszCacheMax != NULL ) + { + GIntBig nNewCacheMax = (GIntBig)CPLScanUIntBig(pszCacheMax, strlen(pszCacheMax)); + if( nNewCacheMax < 100000 ) + { + if (nNewCacheMax < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Invalid value for GDAL_CACHEMAX. Using default value."); + return nCacheMax; + } + nNewCacheMax *= 1024 * 1024; + } + nCacheMax = nNewCacheMax; + } + } + + return nCacheMax; +} + +/************************************************************************/ +/* GDALGetCacheUsed() */ +/************************************************************************/ + +/** + * \brief Get cache memory used. + * + * @return the number of bytes of memory currently in use by the + * GDALRasterBlock memory caching. + */ + +int CPL_STDCALL GDALGetCacheUsed() +{ + if (nCacheUsed > INT_MAX) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cache used value doesn't fit on a 32 bit integer. " + "Call GDALGetCacheUsed64() instead"); + bHasWarned = TRUE; + } + return INT_MAX; + } + return (int)nCacheUsed; +} + +/************************************************************************/ +/* GDALGetCacheUsed64() */ +/************************************************************************/ + +/** + * \brief Get cache memory used. + * + * @return the number of bytes of memory currently in use by the + * GDALRasterBlock memory caching. + * + * @since GDAL 1.8.0 + */ + +GIntBig CPL_STDCALL GDALGetCacheUsed64() +{ + return nCacheUsed; +} + +/************************************************************************/ +/* GDALFlushCacheBlock() */ +/* */ +/* The workhorse of cache management! */ +/************************************************************************/ + +/** + * \brief Try to flush one cached raster block + * + * This function will search the first unlocked raster block and will + * flush it to release the associated memory. + * + * @return TRUE if one block was flushed, FALSE if there are no cached blocks + * or if they are currently locked. + */ +int CPL_STDCALL GDALFlushCacheBlock() + +{ + return GDALRasterBlock::FlushCacheBlock(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALRasterBlock */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \class GDALRasterBlock "gdal_priv.h" + * + * GDALRasterBlock objects hold one block of raster data for one band + * that is currently stored in the GDAL raster cache. The cache holds + * some blocks of raster data for zero or more GDALRasterBand objects + * across zero or more GDALDataset objects in a global raster cache with + * a least recently used (LRU) list and an upper cache limit (see + * GDALSetCacheMax()) under which the cache size is normally kept. + * + * Some blocks in the cache may be modified relative to the state on disk + * (they are marked "Dirty") and must be flushed to disk before they can + * be discarded. Other (Clean) blocks may just be discarded if their memory + * needs to be recovered. + * + * In normal situations applications do not interact directly with the + * GDALRasterBlock - instead it it utilized by the RasterIO() interfaces + * to implement caching. + * + * Some driver classes are implemented in a fashion that completely avoids + * use of the GDAL raster cache (and GDALRasterBlock) though this is not very + * common. + */ + +/************************************************************************/ +/* FlushCacheBlock() */ +/* */ +/* Note, if we have alot of blocks locked for a long time, this */ +/* method is going to get slow because it will have to traverse */ +/* the linked list a long ways looking for a flushing */ +/* candidate. It might help to re-touch locked blocks to push */ +/* them to the top of the list. */ +/************************************************************************/ + +/** + * \brief Attempt to flush at least one block from the cache. + * + * This static method is normally used to recover memory when a request + * for a new cache block would put cache memory use over the established + * limit. + * + * C++ analog to the C function GDALFlushCacheBlock(). + * + * @return TRUE if successful or FALSE if no flushable block is found. + */ + +int GDALRasterBlock::FlushCacheBlock() + +{ + int nXOff, nYOff; + GDALRasterBand *poBand; + + { + CPLMutexHolderD( &hRBMutex ); + GDALRasterBlock *poTarget = (GDALRasterBlock *) poOldest; + + while( poTarget != NULL && poTarget->GetLockCount() > 0 ) + poTarget = poTarget->poPrevious; + + if( poTarget == NULL ) + return FALSE; + + poTarget->Detach(); + + nXOff = poTarget->GetXOff(); + nYOff = poTarget->GetYOff(); + poBand = poTarget->GetBand(); + } + + CPLErr eErr = poBand->FlushBlock( nXOff, nYOff ); + if (eErr != CE_None) + { + /* Save the error for later reporting */ + poBand->SetFlushBlockErr(eErr); + } + + return TRUE; +} + +/************************************************************************/ +/* GDALRasterBlock() */ +/************************************************************************/ + +/** + * @brief GDALRasterBlock Constructor + * + * Normally only called from GDALRasterBand::GetLockedBlockRef(). + * + * @param poBandIn the raster band used as source of raster block + * being constructed. + * + * @param nXOffIn the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYOffIn the vertical block offset, with zero indicating + * the top most block, 1 the next block and so forth. + */ + +GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn, + int nXOffIn, int nYOffIn ) + +{ + CPLAssert( NULL != poBandIn ); + + poBand = poBandIn; + + poBand->GetBlockSize( &nXSize, &nYSize ); + eType = poBand->GetRasterDataType(); + pData = NULL; + bDirty = FALSE; + nLockCount = 0; + + poNext = poPrevious = NULL; + + nXOff = nXOffIn; + nYOff = nYOffIn; +} + +/************************************************************************/ +/* ~GDALRasterBlock() */ +/************************************************************************/ + +/** + * Block destructor. + * + * Normally called from GDALRasterBand::FlushBlock(). + */ + +GDALRasterBlock::~GDALRasterBlock() + +{ + Detach(); + + if( pData != NULL ) + { + int nSizeInBytes; + + VSIFree( pData ); + + nSizeInBytes = (nXSize * nYSize * GDALGetDataTypeSize(eType)+7)/8; + + { + CPLMutexHolderD( &hRBMutex ); + nCacheUsed -= nSizeInBytes; + } + } + + CPLAssert( nLockCount == 0 ); + +#ifdef ENABLE_DEBUG + Verify(); +#endif +} + +/************************************************************************/ +/* Detach() */ +/************************************************************************/ + +/** + * Remove block from cache. + * + * This method removes the current block from the linked list used to keep + * track of all cached blocks in order of age. It does not affect whether + * the block is referenced by a GDALRasterBand nor does it destroy or flush + * the block. + */ + +void GDALRasterBlock::Detach() + +{ + CPLMutexHolderD( &hRBMutex ); + + if( poOldest == this ) + poOldest = poPrevious; + + if( poNewest == this ) + { + poNewest = poNext; + } + + if( poPrevious != NULL ) + poPrevious->poNext = poNext; + + if( poNext != NULL ) + poNext->poPrevious = poPrevious; + + poPrevious = NULL; + poNext = NULL; +} + +/************************************************************************/ +/* Verify() */ +/************************************************************************/ + +/** + * Confirms (via assertions) that the block cache linked list is in a + * consistent state. + */ + +void GDALRasterBlock::Verify() + +{ + CPLMutexHolderD( &hRBMutex ); + + CPLAssert( (poNewest == NULL && poOldest == NULL) + || (poNewest != NULL && poOldest != NULL) ); + + if( poNewest != NULL ) + { + CPLAssert( poNewest->poPrevious == NULL ); + CPLAssert( poOldest->poNext == NULL ); + + for( GDALRasterBlock *poBlock = (GDALRasterBlock *) poNewest; + poBlock != NULL; + poBlock = poBlock->poNext ) + { + if( poBlock->poPrevious ) + { + CPLAssert( poBlock->poPrevious->poNext == poBlock ); + } + + if( poBlock->poNext ) + { + CPLAssert( poBlock->poNext->poPrevious == poBlock ); + } + } + } +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +/** + * Force writing of the current block, if dirty. + * + * The block is written using GDALRasterBand::IWriteBlock() on it's + * corresponding band object. Even if the write fails the block will + * be marked clean. + * + * @return CE_None otherwise the error returned by IWriteBlock(). + */ + +CPLErr GDALRasterBlock::Write() + +{ + if( !GetDirty() ) + return CE_None; + + if( poBand == NULL ) + return CE_Failure; + + MarkClean(); + + if (poBand->eFlushBlockErr == CE_None) + return poBand->IWriteBlock( nXOff, nYOff, pData ); + else + return poBand->eFlushBlockErr; +} + +/************************************************************************/ +/* Touch() */ +/************************************************************************/ + +/** + * Push block to top of LRU (least-recently used) list. + * + * This method is normally called when a block is used to keep track + * that it has been recently used. + */ + +void GDALRasterBlock::Touch() + +{ + CPLMutexHolderD( &hRBMutex ); + + if( poNewest == this ) + return; + + if( poOldest == this ) + poOldest = this->poPrevious; + + if( poPrevious != NULL ) + poPrevious->poNext = poNext; + + if( poNext != NULL ) + poNext->poPrevious = poPrevious; + + poPrevious = NULL; + poNext = (GDALRasterBlock *) poNewest; + + if( poNewest != NULL ) + { + CPLAssert( poNewest->poPrevious == NULL ); + poNewest->poPrevious = this; + } + poNewest = this; + + if( poOldest == NULL ) + { + CPLAssert( poPrevious == NULL && poNext == NULL ); + poOldest = this; + } +#ifdef ENABLE_DEBUG + Verify(); +#endif +} + +/************************************************************************/ +/* Internalize() */ +/************************************************************************/ + +/** + * Allocate memory for block. + * + * This method allocates memory for the block, and attempts to flush other + * blocks, if necessary, to bring the total cache size back within the limits. + * The newly allocated block is touched and will be considered most recently + * used in the LRU list. + * + * @return CE_None on success or CE_Failure if memory allocation fails. + */ + +CPLErr GDALRasterBlock::Internalize() + +{ + CPLMutexHolderD( &hRBMutex ); + void *pNewData; + int nSizeInBytes; + GIntBig nCurCacheMax = GDALGetCacheMax64(); + + /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */ + nSizeInBytes = nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8); + + pNewData = VSIMalloc( nSizeInBytes ); + if( pNewData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.", + nSizeInBytes); + return( CE_Failure ); + } + + if( pData != NULL ) + memcpy( pNewData, pData, nSizeInBytes ); + + pData = pNewData; + +/* -------------------------------------------------------------------- */ +/* Flush old blocks if we are nearing our memory limit. */ +/* -------------------------------------------------------------------- */ + AddLock(); /* don't flush this block! */ + + nCacheUsed += nSizeInBytes; + while( nCacheUsed > nCurCacheMax ) + { + GIntBig nOldCacheUsed = nCacheUsed; + + GDALFlushCacheBlock(); + + if( nCacheUsed == nOldCacheUsed ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Add this block to the list. */ +/* -------------------------------------------------------------------- */ + Touch(); + DropLock(); + + return( CE_None ); +} + +/************************************************************************/ +/* MarkDirty() */ +/************************************************************************/ + +/** + * Mark the block as modified. + * + * A dirty block is one that has been modified and will need to be written + * to disk before it can be flushed. + */ + +void GDALRasterBlock::MarkDirty() + +{ + bDirty = TRUE; +} + + +/************************************************************************/ +/* MarkClean() */ +/************************************************************************/ + +/** + * Mark the block as unmodified. + * + * A dirty block is one that has been modified and will need to be written + * to disk before it can be flushed. + */ + +void GDALRasterBlock::MarkClean() + +{ + bDirty = FALSE; +} + +/************************************************************************/ +/* SafeLockBlock() */ +/************************************************************************/ + +/** + * \brief Safely lock block. + * + * This method locks a GDALRasterBlock (and touches it) in a thread-safe + * manner. The global block cache mutex is held while locking the block, + * in order to avoid race conditions with other threads that might be + * trying to expire the block at the same time. The block pointer may be + * safely NULL, in which case this method does nothing. + * + * @param ppBlock Pointer to the block pointer to try and lock/touch. + */ + +int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock ) + +{ + CPLAssert( NULL != ppBlock ); + + CPLMutexHolderD( &hRBMutex ); + + if( *ppBlock != NULL ) + { + (*ppBlock)->AddLock(); + (*ppBlock)->Touch(); + + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* DestroyRBMutex() */ +/************************************************************************/ + +void GDALRasterBlock::DestroyRBMutex() +{ + if( hRBMutex != NULL ) + CPLDestroyMutex(hRBMutex); + hRBMutex = NULL; +} diff --git a/ogr/gdaltransformer.cpp b/ogr/gdaltransformer.cpp new file mode 100644 index 0000000..78d0c71 --- /dev/null +++ b/ogr/gdaltransformer.cpp @@ -0,0 +1,3183 @@ +/****************************************************************************** + * $Id: gdaltransformer.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Mapinfo Image Warper + * Purpose: Implementation of one or more GDALTrasformerFunc types, including + * the GenImgProj (general image reprojector) transformer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, i3 - information integration and imaging + * Fort Collin, CO + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "gdal_alg_priv.h" +#include "cpl_list.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdaltransformer.cpp 27044 2014-03-16 23:41:27Z rouault $"); +CPL_C_START +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeGeoLocTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeRPCTransformer( CPLXMLNode *psTree ); +CPL_C_END + +static CPLXMLNode *GDALSerializeReprojectionTransformer( void *pTransformArg ); +static void *GDALDeserializeReprojectionTransformer( CPLXMLNode *psTree ); + +static CPLXMLNode *GDALSerializeGenImgProjTransformer( void *pTransformArg ); +static void *GDALDeserializeGenImgProjTransformer( CPLXMLNode *psTree ); + +static void GDALRefreshGenImgProjTransformer(void* hTransformArg); + +/************************************************************************/ +/* GDALTransformFunc */ +/* */ +/* Documentation for GDALTransformFunc typedef. */ +/************************************************************************/ + +/*! + +\typedef int GDALTransformerFunc + +Generic signature for spatial point transformers. + +This function signature is used for a variety of functions that accept +passed in functions used to transform point locations between two coordinate +spaces. + +The GDALCreateGenImgProjTransformer(), GDALCreateReprojectionTransformer(), +GDALCreateGCPTransformer() and GDALCreateApproxTransformer() functions can +be used to prepare argument data for some built-in transformers. As well, +applications can implement their own transformers to the following signature. + +\code +typedef int +(*GDALTransformerFunc)( void *pTransformerArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); +\endcode + +@param pTransformerArg application supplied callback data used by the +transformer. + +@param bDstToSrc if TRUE the transformation will be from the destination +coordinate space to the source coordinate system, otherwise the transformation +will be from the source coordinate system to the destination coordinate system. + +@param nPointCount number of points in the x, y and z arrays. + +@param x input X coordinates. Results returned in same array. + +@param y input Y coordinates. Results returned in same array. + +@param z input Z coordinates. Results returned in same array. + +@param panSuccess array of ints in which success (TRUE) or failure (FALSE) +flags are returned for the translation of each point. + +@return TRUE if the overall transformation succeeds (though some individual +points may have failed) or FALSE if the overall transformation fails. + +*/ + +/************************************************************************/ +/* GDALSuggestedWarpOutput() */ +/************************************************************************/ + +/** + * Suggest output file size. + * + * This function is used to suggest the size, and georeferenced extents + * appropriate given the indicated transformation and input file. It walks + * the edges of the input file (approximately 20 sample points along each + * edge) transforming into output coordinates in order to get an extents box. + * + * Then a resolution is computed with the intent that the length of the + * distance from the top left corner of the output imagery to the bottom right + * corner would represent the same number of pixels as in the source image. + * Note that if the image is somewhat rotated the diagonal taken isnt of the + * whole output bounding rectangle, but instead of the locations where the + * top/left and bottom/right corners transform. The output pixel size is + * always square. This is intended to approximately preserve the resolution + * of the input data in the output file. + * + * The values returned in padfGeoTransformOut, pnPixels and pnLines are + * the suggested number of pixels and lines for the output file, and the + * geotransform relating those pixels to the output georeferenced coordinates. + * + * The trickiest part of using the function is ensuring that the + * transformer created is from source file pixel/line coordinates to + * output file georeferenced coordinates. This can be accomplished with + * GDALCreateGenImgProjTransformer() by passing a NULL for the hDstDS. + * + * @param hSrcDS the input image (it is assumed the whole input images is + * being transformed). + * @param pfnTransformer the transformer function. + * @param pTransformArg the callback data for the transformer function. + * @param padfGeoTransformOut the array of six doubles in which the suggested + * geotransform is returned. + * @param pnPixels int in which the suggest pixel width of output is returned. + * @param pnLines int in which the suggest pixel height of output is returned. + * + * @return CE_None if successful or CE_Failure otherwise. + */ + + +CPLErr CPL_STDCALL +GDALSuggestedWarpOutput( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALSuggestedWarpOutput", CE_Failure ); + + double adfExtent[4] = { 0 }; + + return GDALSuggestedWarpOutput2( hSrcDS, pfnTransformer, pTransformArg, + padfGeoTransformOut, pnPixels, pnLines, + adfExtent, 0 ); +} + + + static int GDALSuggestedWarpOutput2_MustAdjustForRightBorder( + GDALTransformerFunc pfnTransformer, void *pTransformArg, + double* padfExtent, int nPixels, int nLines, + double dfPixelSizeX, double dfPixelSizeY) + { + int nSamplePoints; + double dfRatio; + int bErr; + int nBadCount; + int abSuccess[21] = { 0 }; + double adfX[21] = { 0 }; + double adfY[21] = { 0 }; + double adfZ[21] = { 0 }; + + //double dfMinXOut = padfExtent[0]; + //double dfMinYOut = padfExtent[1]; + double dfMaxXOut = padfExtent[2]; + double dfMaxYOut = padfExtent[3]; + + // Take 20 steps + nSamplePoints = 0; + for( dfRatio = 0.0; dfRatio <= 1.01; dfRatio += 0.05 ) + { + // Ensure we end exactly at the end. + if( dfRatio > 0.99 ) + dfRatio = 1.0; + + // Along right + adfX[nSamplePoints] = dfMaxXOut; + adfY[nSamplePoints] = dfMaxYOut - dfPixelSizeY * dfRatio * nLines; + adfZ[nSamplePoints++] = 0.0; + } + + bErr = FALSE; + if( !pfnTransformer( pTransformArg, TRUE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + if( !bErr && !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + nSamplePoints = 0; + nBadCount = 0; + for( dfRatio = 0.0; !bErr && dfRatio <= 1.01; dfRatio += 0.05 ) + { + double expected_x = dfMaxXOut; + double expected_y = dfMaxYOut - dfPixelSizeY * dfRatio * nLines; + if (fabs(adfX[nSamplePoints] - expected_x) > dfPixelSizeX || + fabs(adfY[nSamplePoints] - expected_y) > dfPixelSizeY) + nBadCount ++; + nSamplePoints ++; + } + + return (nBadCount == nSamplePoints); +} + + + static int GDALSuggestedWarpOutput2_MustAdjustForBottomBorder( + GDALTransformerFunc pfnTransformer, void *pTransformArg, + double* padfExtent, int nPixels, int nLines, + double dfPixelSizeX, double dfPixelSizeY) + { + int nSamplePoints; + double dfRatio; + int bErr; + int nBadCount; + int abSuccess[21] = { 0 }; + double adfX[21] = { 0 }; + double adfY[21] = { 0 }; + double adfZ[21] = { 0 }; + + double dfMinXOut = padfExtent[0]; + double dfMinYOut = padfExtent[1]; + //double dfMaxXOut = padfExtent[2]; + //double dfMaxYOut = padfExtent[3]; + + // Take 20 steps + nSamplePoints = 0; + for( dfRatio = 0.0; dfRatio <= 1.01; dfRatio += 0.05 ) + { + // Ensure we end exactly at the end. + if( dfRatio > 0.99 ) + dfRatio = 1.0; + + // Along right + adfX[nSamplePoints] = dfMinXOut + dfPixelSizeX * dfRatio * nPixels; + adfY[nSamplePoints] = dfMinYOut; + adfZ[nSamplePoints++] = 0.0; + } + + bErr = FALSE; + if( !pfnTransformer( pTransformArg, TRUE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + if( !bErr && !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + nSamplePoints = 0; + nBadCount = 0; + for( dfRatio = 0.0; !bErr && dfRatio <= 1.01; dfRatio += 0.05 ) + { + double expected_x = dfMinXOut + dfPixelSizeX * dfRatio * nPixels; + double expected_y = dfMinYOut; + if (fabs(adfX[nSamplePoints] - expected_x) > dfPixelSizeX || + fabs(adfY[nSamplePoints] - expected_y) > dfPixelSizeY) + nBadCount ++; + nSamplePoints ++; + } + + return (nBadCount == nSamplePoints); +} + +/************************************************************************/ +/* GDALSuggestedWarpOutput2() */ +/************************************************************************/ + +/** + * Suggest output file size. + * + * This function is used to suggest the size, and georeferenced extents + * appropriate given the indicated transformation and input file. It walks + * the edges of the input file (approximately 20 sample points along each + * edge) transforming into output coordinates in order to get an extents box. + * + * Then a resolution is computed with the intent that the length of the + * distance from the top left corner of the output imagery to the bottom right + * corner would represent the same number of pixels as in the source image. + * Note that if the image is somewhat rotated the diagonal taken isnt of the + * whole output bounding rectangle, but instead of the locations where the + * top/left and bottom/right corners transform. The output pixel size is + * always square. This is intended to approximately preserve the resolution + * of the input data in the output file. + * + * The values returned in padfGeoTransformOut, pnPixels and pnLines are + * the suggested number of pixels and lines for the output file, and the + * geotransform relating those pixels to the output georeferenced coordinates. + * + * The trickiest part of using the function is ensuring that the + * transformer created is from source file pixel/line coordinates to + * output file georeferenced coordinates. This can be accomplished with + * GDALCreateGenImgProjTransformer() by passing a NULL for the hDstDS. + * + * @param hSrcDS the input image (it is assumed the whole input images is + * being transformed). + * @param pfnTransformer the transformer function. + * @param pTransformArg the callback data for the transformer function. + * @param padfGeoTransformOut the array of six doubles in which the suggested + * geotransform is returned. + * @param pnPixels int in which the suggest pixel width of output is returned. + * @param pnLines int in which the suggest pixel height of output is returned. + * @param padfExtent Four entry array to return extents as (xmin, ymin, xmax, ymax). + * @param nOptions Options, currently always zero. + * + * @return CE_None if successful or CE_Failure otherwise. + */ + +CPLErr CPL_STDCALL +GDALSuggestedWarpOutput2( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines, + double *padfExtent, int nOptions ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALSuggestedWarpOutput2", CE_Failure ); + +/* -------------------------------------------------------------------- */ +/* Setup sample points all around the edge of the input raster. */ +/* -------------------------------------------------------------------- */ + int nSamplePoints = 0; + int nInXSize = GDALGetRasterXSize( hSrcDS ); + int nInYSize = GDALGetRasterYSize( hSrcDS ); + + if (pfnTransformer == GDALGenImgProjTransform) + { + /* In case CHECK_WITH_INVERT_PROJ has been modified */ + GDALRefreshGenImgProjTransformer(pTransformArg); + } + +#define N_PIXELSTEP 50 + int nSteps = (int) (double(MIN(nInYSize, nInXSize)) / N_PIXELSTEP + .5); + if (nSteps < 20) + nSteps = 20; + nSteps = MIN(nSteps,100); + +retry: + int nSampleMax = (nSteps + 1)*(nSteps + 1); + int *pabSuccess = NULL; + double *padfX, *padfY, *padfZ; + double *padfXRevert, *padfYRevert, *padfZRevert; + + double dfRatio = 0.0; + double dfStep = 1. / nSteps; + + pabSuccess = (int *) VSIMalloc3(sizeof(int), nSteps + 1, nSteps + 1); + padfX = (double *) VSIMalloc3(sizeof(double) * 3, nSteps + 1, nSteps + 1); + padfXRevert = (double *) VSIMalloc3(sizeof(double) * 3, nSteps + 1, nSteps + 1); + if (pabSuccess == NULL || padfX == NULL || padfXRevert == NULL) + { + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + if (nSteps > 20) + { + nSteps = 20; + goto retry; + } + return CE_Failure; + } + padfY = padfX + nSampleMax; + padfZ = padfX + nSampleMax * 2; + padfYRevert = padfXRevert + nSampleMax; + padfZRevert = padfXRevert + nSampleMax * 2; + + + // Take N_STEPS steps + int iStep; + for( iStep = 0; iStep <= nSteps; iStep ++ ) + { + dfRatio = (iStep == nSteps) ? 1.0 : iStep * dfStep; + + // Along top + padfX[nSamplePoints] = dfRatio * nInXSize; + padfY[nSamplePoints] = 0.0; + padfZ[nSamplePoints++] = 0.0; + + // Along bottom + padfX[nSamplePoints] = dfRatio * nInXSize; + padfY[nSamplePoints] = nInYSize; + padfZ[nSamplePoints++] = 0.0; + + // Along left + padfX[nSamplePoints] = 0.0; + padfY[nSamplePoints] = dfRatio * nInYSize; + padfZ[nSamplePoints++] = 0.0; + + // Along right + padfX[nSamplePoints] = nInXSize; + padfY[nSamplePoints] = dfRatio * nInYSize; + padfZ[nSamplePoints++] = 0.0; + } + + CPLAssert( nSamplePoints == 4 * (nSteps + 1) ); + + memset( pabSuccess, 1, sizeof(int) * nSampleMax ); + +/* -------------------------------------------------------------------- */ +/* Transform them to the output coordinate system. */ +/* -------------------------------------------------------------------- */ + int nFailedCount = 0, i; + + if( !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + padfX, padfY, padfZ, pabSuccess ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALSuggestedWarpOutput() failed because the passed\n" + "transformer failed." ); + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + return CE_Failure; + } + + for( i = 0; i < nSamplePoints; i++ ) + { + if( !pabSuccess[i] ) + nFailedCount++; + } + +/* -------------------------------------------------------------------- */ +/* Check if the computed target coordinates are revertable. */ +/* If not, try the detailed grid sampling. */ +/* -------------------------------------------------------------------- */ + if (nFailedCount == 0 ) + { + memcpy(padfXRevert, padfX, nSamplePoints * sizeof(double)); + memcpy(padfYRevert, padfY, nSamplePoints * sizeof(double)); + memcpy(padfZRevert, padfZ, nSamplePoints * sizeof(double)); + if( !pfnTransformer( pTransformArg, TRUE, nSamplePoints, + padfXRevert, padfYRevert, padfZRevert, pabSuccess ) ) + { + nFailedCount = 1; + } + else + { + for( i = 0; nFailedCount == 0 && i < nSamplePoints; i++ ) + { + if( !pabSuccess[i] ) + nFailedCount++; + + dfRatio = 0.0 + (i/4) * dfStep; + if (dfRatio>0.99) + dfRatio = 1.0; + + double dfExpectedX, dfExpectedY; + if ((i % 4) == 0) + { + dfExpectedX = dfRatio * nInXSize; + dfExpectedY = 0.0; + } + else if ((i % 4) == 1) + { + dfExpectedX = dfRatio * nInXSize; + dfExpectedY = nInYSize; + } + else if ((i % 4) == 2) + { + dfExpectedX = 0.0; + dfExpectedY = dfRatio * nInYSize; + } + else + { + dfExpectedX = nInXSize; + dfExpectedY = dfRatio * nInYSize; + } + + if (fabs(padfXRevert[i] - dfExpectedX) > nInXSize / nSteps || + fabs(padfYRevert[i] - dfExpectedY) > nInYSize / nSteps) + nFailedCount ++; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If any of the edge points failed to transform, we need to */ +/* build a fairly detailed internal grid of points instead to */ +/* help identify the area that is transformable. */ +/* -------------------------------------------------------------------- */ + if( nFailedCount > 0 ) + { + int iStep2; + double dfRatio2; + nSamplePoints = 0; + + // Take N_STEPS steps + for( iStep = 0; iStep <= nSteps; iStep ++ ) + { + dfRatio = (iStep == nSteps) ? 1.0 : iStep * dfStep; + + for( iStep2 = 0; iStep2 <= nSteps; iStep2 ++ ) + { + dfRatio2 = (iStep2 == nSteps) ? 1.0 : iStep2 * dfStep; + + // Along top + padfX[nSamplePoints] = dfRatio2 * nInXSize; + padfY[nSamplePoints] = dfRatio * nInYSize; + padfZ[nSamplePoints++] = 0.0; + } + } + + CPLAssert( nSamplePoints == nSampleMax ); + + if( !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + padfX, padfY, padfZ, pabSuccess ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALSuggestedWarpOutput() failed because the passed\n" + "transformer failed." ); + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Collect the bounds, ignoring any failed points. */ +/* -------------------------------------------------------------------- */ + double dfMinXOut=0, dfMinYOut=0, dfMaxXOut=0, dfMaxYOut=0; + int bGotInitialPoint = FALSE; + + nFailedCount = 0; + for( i = 0; i < nSamplePoints; i++ ) + { + + int x_i = i % (nSteps + 1); + int y_i = i / (nSteps + 1); + + if (x_i > 0 && (pabSuccess[i-1] || pabSuccess[i])) + { + double x_out_before = padfX[i-1]; + double x_out_after = padfX[i]; + int nIter = 0; + double x_in_before = (x_i - 1) * nInXSize * 1.0 / nSteps; + double x_in_after = x_i * nInXSize * 1.0 / nSteps; + int valid_before = pabSuccess[i-1]; + int valid_after = pabSuccess[i]; + + /* Detect discontinuity in target coordinates when the target x coordinates */ + /* change sign. This may be a false positive when the targe tx is around 0 */ + /* Dichotomic search to reduce the interval to near the discontinuity and */ + /* get a better out extent */ + while ( (!valid_before || !valid_after || + x_out_before * x_out_after < 0) && nIter < 16 ) + { + double x = (x_in_before + x_in_after) / 2; + double y = y_i * nInYSize * 1.0 / nSteps; + double z= 0; + //fprintf(stderr, "[%d] (%f, %f) -> ", nIter, x, y); + int bSuccess = TRUE; + if( !pfnTransformer( pTransformArg, FALSE, 1, + &x, &y, &z, &bSuccess ) || !bSuccess ) + { + //fprintf(stderr, "invalid\n"); + if (!valid_before) + { + x_in_before = (x_in_before + x_in_after) / 2; + } + else if (!valid_after) + { + x_in_after = (x_in_before + x_in_after) / 2; + } + else + break; + } + else + { + //fprintf(stderr, "(%f, %f)\n", x, y); + + if( !bGotInitialPoint ) + { + bGotInitialPoint = TRUE; + dfMinXOut = dfMaxXOut = x; + dfMinYOut = dfMaxYOut = y; + } + else + { + dfMinXOut = MIN(dfMinXOut,x); + dfMinYOut = MIN(dfMinYOut,y); + dfMaxXOut = MAX(dfMaxXOut,x); + dfMaxYOut = MAX(dfMaxYOut,y); + } + + if (!valid_before || x_out_before * x < 0) + { + valid_after = TRUE; + x_in_after = (x_in_before + x_in_after) / 2; + x_out_after = x; + } + else + { + valid_before = TRUE; + x_out_before = x; + x_in_before = (x_in_before + x_in_after) / 2; + } + } + nIter ++; + } + } + + if( !pabSuccess[i] ) + { + nFailedCount++; + continue; + } + + if( !bGotInitialPoint ) + { + bGotInitialPoint = TRUE; + dfMinXOut = dfMaxXOut = padfX[i]; + dfMinYOut = dfMaxYOut = padfY[i]; + } + else + { + dfMinXOut = MIN(dfMinXOut, padfX[i]); + dfMinYOut = MIN(dfMinYOut, padfY[i]); + dfMaxXOut = MAX(dfMaxXOut, padfX[i]); + dfMaxYOut = MAX(dfMaxYOut, padfY[i]); + } + } + + if( nFailedCount > nSamplePoints - 10 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many points (%d out of %d) failed to transform,\n" + "unable to compute output bounds.", + nFailedCount, nSamplePoints ); + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_Failure; + } + + if( nFailedCount > 0 ) + CPLDebug( "GDAL", + "GDALSuggestedWarpOutput(): %d out of %d points failed to transform.", + nFailedCount, nSamplePoints ); + +/* -------------------------------------------------------------------- */ +/* Compute the distance in "georeferenced" units from the top */ +/* corner of the transformed input image to the bottom left */ +/* corner of the transformed input. Use this distance to */ +/* compute an approximate pixel size in the output */ +/* georeferenced coordinates. */ +/* -------------------------------------------------------------------- */ + double dfDiagonalDist, dfDeltaX, dfDeltaY; + + if( pabSuccess[0] && pabSuccess[nSamplePoints - 1] ) + { + dfDeltaX = padfX[nSamplePoints-1] - padfX[0]; + dfDeltaY = padfY[nSamplePoints-1] - padfY[0]; + } + else + { + dfDeltaX = dfMaxXOut - dfMinXOut; + dfDeltaY = dfMaxYOut - dfMinYOut; + } + + dfDiagonalDist = sqrt( dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY ); + +/* -------------------------------------------------------------------- */ +/* Compute a pixel size from this. */ +/* -------------------------------------------------------------------- */ + double dfPixelSize; + + dfPixelSize = dfDiagonalDist + / sqrt(((double)nInXSize)*nInXSize + ((double)nInYSize)*nInYSize); + + double dfPixels = (dfMaxXOut - dfMinXOut) / dfPixelSize; + double dfLines = (dfMaxYOut - dfMinYOut) / dfPixelSize; + + if( dfPixels > INT_MAX - 1 || dfLines > INT_MAX - 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Computed dimensions are too big : %.0f x %.0f", + dfPixels + 0.5, dfLines + 0.5 ); + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_Failure; + } + + *pnPixels = (int) (dfPixels + 0.5); + *pnLines = (int) (dfLines + 0.5); + + double dfPixelSizeX = dfPixelSize; + double dfPixelSizeY = dfPixelSize; + + double adfExtent[4]; + const double adfRatioArray[] = { 0, 0.001, 0.01, 0.1, 1 }; + size_t nRetry; + +#define N_ELEMENTS(x) (sizeof(x) / sizeof(x[0])) + +/* -------------------------------------------------------------------- */ +/* Check that the right border is not completely out of source */ +/* image. If so, adjust the x pixel size a bit in the hope it will */ +/* fit. */ +/* -------------------------------------------------------------------- */ + for( nRetry = 0; nRetry < N_ELEMENTS(adfRatioArray); nRetry ++ ) + { + double dfTryPixelSizeX = + dfPixelSizeX - dfPixelSizeX * adfRatioArray[nRetry] / *pnPixels; + adfExtent[0] = dfMinXOut; + adfExtent[1] = dfMaxYOut - (*pnLines) * dfPixelSizeY; + adfExtent[2] = dfMinXOut + (*pnPixels) * dfTryPixelSizeX; + adfExtent[3] = dfMaxYOut; + if (!GDALSuggestedWarpOutput2_MustAdjustForRightBorder( + pfnTransformer, pTransformArg, + adfExtent, *pnPixels, *pnLines, + dfTryPixelSizeX, dfPixelSizeY)) + { + dfPixelSizeX = dfTryPixelSizeX; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Check that the bottom border is not completely out of source */ +/* image. If so, adjust the y pixel size a bit in the hope it will */ +/* fit. */ +/* -------------------------------------------------------------------- */ + for( nRetry = 0; nRetry < N_ELEMENTS(adfRatioArray); nRetry ++ ) + { + double dfTryPixelSizeY = + dfPixelSizeY - dfPixelSizeY * adfRatioArray[nRetry] / *pnLines; + adfExtent[0] = dfMinXOut; + adfExtent[1] = dfMaxYOut - (*pnLines) * dfTryPixelSizeY; + adfExtent[2] = dfMinXOut + (*pnPixels) * dfPixelSizeX; + adfExtent[3] = dfMaxYOut; + if (!GDALSuggestedWarpOutput2_MustAdjustForBottomBorder( + pfnTransformer, pTransformArg, + adfExtent, *pnPixels, *pnLines, + dfPixelSizeX, dfTryPixelSizeY)) + { + dfPixelSizeY = dfTryPixelSizeY; + break; + } + } + + +/* -------------------------------------------------------------------- */ +/* Recompute some bounds so that all return values are consistant */ +/* -------------------------------------------------------------------- */ + dfMaxXOut = dfMinXOut + (*pnPixels) * dfPixelSizeX; + dfMinYOut = dfMaxYOut - (*pnLines) * dfPixelSizeY; + + /* -------------------------------------------------------------------- */ + /* Return raw extents. */ + /* -------------------------------------------------------------------- */ + padfExtent[0] = dfMinXOut; + padfExtent[1] = dfMinYOut; + padfExtent[2] = dfMaxXOut; + padfExtent[3] = dfMaxYOut; + + /* -------------------------------------------------------------------- */ + /* Set the output geotransform. */ + /* -------------------------------------------------------------------- */ + padfGeoTransformOut[0] = dfMinXOut; + padfGeoTransformOut[1] = dfPixelSizeX; + padfGeoTransformOut[2] = 0.0; + padfGeoTransformOut[3] = dfMaxYOut; + padfGeoTransformOut[4] = 0.0; + padfGeoTransformOut[5] = - dfPixelSizeY; + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALGenImgProjTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct { + + GDALTransformerInfo sTI; + + double adfSrcGeoTransform[6]; + double adfSrcInvGeoTransform[6]; + + void *pSrcGCPTransformArg; + void *pSrcRPCTransformArg; + void *pSrcTPSTransformArg; + void *pSrcGeoLocTransformArg; + + void *pReprojectArg; + + double adfDstGeoTransform[6]; + double adfDstInvGeoTransform[6]; + + void *pDstGCPTransformArg; + void *pDstRPCTransformArg; + void *pDstTPSTransformArg; + +} GDALGenImgProjTransformInfo; + +/************************************************************************/ +/* GDALCloneGenImgProjTransformer() */ +/************************************************************************/ + +void* GDALCloneGenImgProjTransformer( void *hTransformArg ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCloneGenImgProjTransformer", NULL ); + + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) hTransformArg; + + GDALGenImgProjTransformInfo *psClonedInfo = (GDALGenImgProjTransformInfo *) + CPLMalloc(sizeof(GDALGenImgProjTransformInfo)); + + memcpy(psClonedInfo, psInfo, sizeof(GDALGenImgProjTransformInfo)); + if( psClonedInfo->pSrcGCPTransformArg ) + psClonedInfo->pSrcGCPTransformArg = GDALCloneTransformer( psInfo->pSrcGCPTransformArg ); + if( psClonedInfo->pSrcRPCTransformArg ) + psClonedInfo->pSrcRPCTransformArg = GDALCloneTransformer( psInfo->pSrcRPCTransformArg ); + if( psClonedInfo->pSrcTPSTransformArg ) + psClonedInfo->pSrcTPSTransformArg = GDALCloneTransformer( psInfo->pSrcTPSTransformArg ); + if( psClonedInfo->pSrcGeoLocTransformArg ) + psClonedInfo->pSrcGeoLocTransformArg = GDALCloneTransformer( psInfo->pSrcGeoLocTransformArg ); + if( psClonedInfo->pReprojectArg ) + psClonedInfo->pReprojectArg = GDALCloneTransformer( psInfo->pReprojectArg ); + if( psClonedInfo->pDstGCPTransformArg ) + psClonedInfo->pDstGCPTransformArg = GDALCloneTransformer( psInfo->pDstGCPTransformArg ); + if( psClonedInfo->pDstRPCTransformArg ) + psClonedInfo->pDstRPCTransformArg = GDALCloneTransformer( psInfo->pDstRPCTransformArg ); + if( psClonedInfo->pDstTPSTransformArg ) + psClonedInfo->pDstTPSTransformArg = GDALCloneTransformer( psInfo->pDstTPSTransformArg ); + + return psClonedInfo; +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformer() */ +/************************************************************************/ + +/** + * Create image to image transformer. + * + * This function creates a transformation object that maps from pixel/line + * coordinates on one image to pixel/line coordinates on another image. The + * images may potentially be georeferenced in different coordinate systems, + * and may used GCPs to map between their pixel/line coordinates and + * georeferenced coordinates (as opposed to the default assumption that their + * geotransform should be used). + * + * This transformer potentially performs three concatenated transformations. + * + * The first stage is from source image pixel/line coordinates to source + * image georeferenced coordinates, and may be done using the geotransform, + * or if not defined using a polynomial model derived from GCPs. If GCPs + * are used this stage is accomplished using GDALGCPTransform(). + * + * The second stage is to change projections from the source coordinate system + * to the destination coordinate system, assuming they differ. This is + * accomplished internally using GDALReprojectionTransform(). + * + * The third stage is converting from destination image georeferenced + * coordinates to destination image coordinates. This is done using the + * destination image geotransform, or if not available, using a polynomial + * model derived from GCPs. If GCPs are used this stage is accomplished using + * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the + * transformation is created. + * + * @param hSrcDS source dataset, or NULL. + * @param pszSrcWKT the coordinate system for the source dataset. If NULL, + * it will be read from the dataset itself. + * @param hDstDS destination dataset (or NULL). + * @param pszDstWKT the coordinate system for the destination dataset. If + * NULL, and hDstDS not NULL, it will be read from the destination dataset. + * @param bGCPUseOK TRUE if GCPs should be used if the geotransform is not + * available on the source dataset (not destination). + * @param dfGCPErrorThreshold ignored/deprecated. + * @param nOrder the maximum order to use for GCP derived polynomials if + * possible. Use 0 to autoselect, or -1 for thin plate splines. + * + * @return handle suitable for use GDALGenImgProjTransform(), and to be + * deallocated with GDALDestroyGenImgProjTransformer(). + */ + +void * +GDALCreateGenImgProjTransformer( GDALDatasetH hSrcDS, const char *pszSrcWKT, + GDALDatasetH hDstDS, const char *pszDstWKT, + int bGCPUseOK, double dfGCPErrorThreshold, + int nOrder ) + +{ + char **papszOptions = NULL; + void *pRet; + + if( pszSrcWKT != NULL ) + papszOptions = CSLSetNameValue( papszOptions, "SRC_SRS", pszSrcWKT ); + if( pszDstWKT != NULL ) + papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", pszDstWKT ); + if( !bGCPUseOK ) + papszOptions = CSLSetNameValue( papszOptions, "GCPS_OK", "FALSE" ); + if( nOrder != 0 ) + papszOptions = CSLSetNameValue( papszOptions, "MAX_GCP_ORDER", + CPLString().Printf("%d",nOrder) ); + + pRet = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions ); + CSLDestroy( papszOptions ); + + return pRet; +} + + + +/************************************************************************/ +/* InsertCenterLong() */ +/* */ +/* Insert a CENTER_LONG Extension entry on a GEOGCS to indicate */ +/* the center longitude of the dataset for wrapping purposes. */ +/************************************************************************/ + +static CPLString InsertCenterLong( GDALDatasetH hDS, CPLString osWKT ) + +{ + if( !EQUALN(osWKT.c_str(), "GEOGCS[", 7) ) + return osWKT; + + if( strstr(osWKT,"EXTENSION[\"CENTER_LONG") != NULL ) + return osWKT; + +/* -------------------------------------------------------------------- */ +/* For now we only do this if we have a geotransform since */ +/* other forms require a bunch of extra work. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + if( GDALGetGeoTransform( hDS, adfGeoTransform ) != CE_None ) + return osWKT; + +/* -------------------------------------------------------------------- */ +/* Compute min/max longitude based on testing the four corners. */ +/* -------------------------------------------------------------------- */ + double dfMinLong, dfMaxLong; + int nXSize = GDALGetRasterXSize( hDS ); + int nYSize = GDALGetRasterYSize( hDS ); + + dfMinLong = + MIN(MIN(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + 0 * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + 0 * adfGeoTransform[2]), + MIN(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + nYSize * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + nYSize * adfGeoTransform[2])); + dfMaxLong = + MAX(MAX(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + 0 * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + 0 * adfGeoTransform[2]), + MAX(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + nYSize * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + nYSize * adfGeoTransform[2])); + + if( dfMaxLong - dfMinLong > 360.0 ) + return osWKT; + +/* -------------------------------------------------------------------- */ +/* Insert center long. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS( osWKT ); + double dfCenterLong = (dfMaxLong + dfMinLong) / 2.0; + OGR_SRSNode *poExt; + + poExt = new OGR_SRSNode( "EXTENSION" ); + poExt->AddChild( new OGR_SRSNode( "CENTER_LONG" ) ); + poExt->AddChild( new OGR_SRSNode( CPLString().Printf("%g",dfCenterLong) )); + + oSRS.GetRoot()->AddChild( poExt->Clone() ); + delete poExt; + +/* -------------------------------------------------------------------- */ +/* Convert back to wkt. */ +/* -------------------------------------------------------------------- */ + char *pszWKT = NULL; + oSRS.exportToWkt( &pszWKT ); + + osWKT = pszWKT; + CPLFree( pszWKT ); + + return osWKT; +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformer2() */ +/************************************************************************/ + +/** + * Create image to image transformer. + * + * This function creates a transformation object that maps from pixel/line + * coordinates on one image to pixel/line coordinates on another image. The + * images may potentially be georeferenced in different coordinate systems, + * and may used GCPs to map between their pixel/line coordinates and + * georeferenced coordinates (as opposed to the default assumption that their + * geotransform should be used). + * + * This transformer potentially performs three concatenated transformations. + * + * The first stage is from source image pixel/line coordinates to source + * image georeferenced coordinates, and may be done using the geotransform, + * or if not defined using a polynomial model derived from GCPs. If GCPs + * are used this stage is accomplished using GDALGCPTransform(). + * + * The second stage is to change projections from the source coordinate system + * to the destination coordinate system, assuming they differ. This is + * accomplished internally using GDALReprojectionTransform(). + * + * The third stage is converting from destination image georeferenced + * coordinates to destination image coordinates. This is done using the + * destination image geotransform, or if not available, using a polynomial + * model derived from GCPs. If GCPs are used this stage is accomplished using + * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the + * transformation is created. + * + * Supported Options: + *
      + *
    • SRC_SRS: WKT SRS to be used as an override for hSrcDS. + *
    • DST_SRS: WKT SRS to be used as an override for hDstDS. + *
    • GCPS_OK: If false, GCPs will not be used, default is TRUE. + *
    • REFINE_MINIMUM_GCPS: The minimum amount of GCPs that should be available after the refinement. + *
    • REFINE_TOLERANCE: The tolernace that specifies when a GCP will be eliminated. + *
    • MAX_GCP_ORDER: the maximum order to use for GCP derived polynomials if + * possible. The default is to autoselect based on the number of GCPs. + * A value of -1 triggers use of Thin Plate Spline instead of polynomials. + *
    • SRC_METHOD: may have a value which is one of GEOTRANSFORM, + * GCP_POLYNOMIAL, GCP_TPS, GEOLOC_ARRAY, RPC to force only one geolocation + * method to be considered on the source dataset. Will be used for pixel/line + * to georef transformation on the source dataset. + *
    • DST_METHOD: may have a value which is one of GEOTRANSFORM, + * GCP_POLYNOMIAL, GCP_TPS, GEOLOC_ARRAY, RPC to force only one geolocation + * method to be considered on the source dataset. Will be used for pixel/line + * to georef transformation on the destination dataset. + *
    • RPC_HEIGHT: A fixed height to be used with RPC calculations. + *
    • RPC_DEM: The name of a DEM file to be used with RPC calculations. + *
    • INSERT_CENTER_LONG: May be set to FALSE to disable setting up a + * CENTER_LONG value on the coordinate system to rewrap things around the + * center of the image. + *
    + * + * @param hSrcDS source dataset, or NULL. + * @param hDstDS destination dataset (or NULL). + * @param papszOptions NULL-terminated list of string options (or NULL). + * + * @return handle suitable for use GDALGenImgProjTransform(), and to be + * deallocated with GDALDestroyGenImgProjTransformer() or NULL on failure. + */ + +void * +GDALCreateGenImgProjTransformer2( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, + char **papszOptions ) + +{ + GDALGenImgProjTransformInfo *psInfo; + char **papszMD; + GDALRPCInfo sRPCInfo; + const char *pszMethod = CSLFetchNameValue( papszOptions, "SRC_METHOD" ); + if( pszMethod == NULL ) + pszMethod = CSLFetchNameValue( papszOptions, "METHOD" ); + const char *pszValue; + int nOrder = 0, bGCPUseOK = TRUE, nMinimumGcps = -1, bRefine = FALSE; + double dfTolerance = 0.0; + const char *pszSrcWKT = CSLFetchNameValue( papszOptions, "SRC_SRS" ); + const char *pszDstWKT = CSLFetchNameValue( papszOptions, "DST_SRS" ); + + pszValue = CSLFetchNameValue( papszOptions, "MAX_GCP_ORDER" ); + if( pszValue ) + nOrder = atoi(pszValue); + + pszValue = CSLFetchNameValue( papszOptions, "GCPS_OK" ); + if( pszValue ) + bGCPUseOK = CSLTestBoolean(pszValue); + + pszValue = CSLFetchNameValue( papszOptions, "REFINE_MINIMUM_GCPS" ); + if( pszValue ) + { + if( atoi(pszValue) != -1) + nMinimumGcps = atoi(pszValue); + } + + pszValue = CSLFetchNameValue( papszOptions, "REFINE_TOLERANCE" ); + if( pszValue ) + { + dfTolerance = atof(pszValue); + bRefine = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = (GDALGenImgProjTransformInfo *) + CPLCalloc(sizeof(GDALGenImgProjTransformInfo),1); + + strcpy( psInfo->sTI.szSignature, "GTI" ); + psInfo->sTI.pszClassName = "GDALGenImgProjTransformer"; + psInfo->sTI.pfnTransform = GDALGenImgProjTransform; + psInfo->sTI.pfnCleanup = GDALDestroyGenImgProjTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeGenImgProjTransformer; + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for the source image. */ +/* -------------------------------------------------------------------- */ + if( hSrcDS == NULL ) + { + psInfo->adfSrcGeoTransform[0] = 0.0; + psInfo->adfSrcGeoTransform[1] = 1.0; + psInfo->adfSrcGeoTransform[2] = 0.0; + psInfo->adfSrcGeoTransform[3] = 0.0; + psInfo->adfSrcGeoTransform[4] = 0.0; + psInfo->adfSrcGeoTransform[5] = 1.0; + memcpy( psInfo->adfSrcInvGeoTransform, psInfo->adfSrcGeoTransform, + sizeof(double) * 6 ); + } + + else if( (pszMethod == NULL || EQUAL(pszMethod,"GEOTRANSFORM")) + && GDALGetGeoTransform( hSrcDS, psInfo->adfSrcGeoTransform ) + == CE_None + && (psInfo->adfSrcGeoTransform[0] != 0.0 + || psInfo->adfSrcGeoTransform[1] != 1.0 + || psInfo->adfSrcGeoTransform[2] != 0.0 + || psInfo->adfSrcGeoTransform[3] != 0.0 + || psInfo->adfSrcGeoTransform[4] != 0.0 + || ABS(psInfo->adfSrcGeoTransform[5]) != 1.0) ) + { + if( !GDALInvGeoTransform( psInfo->adfSrcGeoTransform, + psInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetProjectionRef( hSrcDS ); + } + + else if( bGCPUseOK + && (pszMethod == NULL || EQUAL(pszMethod,"GCP_POLYNOMIAL") ) + && GDALGetGCPCount( hSrcDS ) > 0 && nOrder >= 0 ) + { + if(bRefine) + { + psInfo->pSrcGCPTransformArg = + GDALCreateGCPRefineTransformer( GDALGetGCPCount( hSrcDS ), + GDALGetGCPs( hSrcDS ), nOrder, + FALSE, dfTolerance, nMinimumGcps ); + } + else + { + psInfo->pSrcGCPTransformArg = + GDALCreateGCPTransformer( GDALGetGCPCount( hSrcDS ), + GDALGetGCPs( hSrcDS ), nOrder, + FALSE ); + } + + if( psInfo->pSrcGCPTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetGCPProjection( hSrcDS ); + } + + else if( bGCPUseOK + && GDALGetGCPCount( hSrcDS ) > 0 + && nOrder <= 0 + && (pszMethod == NULL || EQUAL(pszMethod,"GCP_TPS")) ) + { + psInfo->pSrcTPSTransformArg = + GDALCreateTPSTransformerInt( GDALGetGCPCount( hSrcDS ), + GDALGetGCPs( hSrcDS ), FALSE, + papszOptions); + if( psInfo->pSrcTPSTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetGCPProjection( hSrcDS ); + } + + else if( (pszMethod == NULL || EQUAL(pszMethod,"RPC")) + && (papszMD = GDALGetMetadata( hSrcDS, "RPC" )) != NULL + && GDALExtractRPCInfo( papszMD, &sRPCInfo ) ) + { + psInfo->pSrcRPCTransformArg = + GDALCreateRPCTransformer( &sRPCInfo, FALSE, 0.1, papszOptions ); + if( psInfo->pSrcRPCTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszSrcWKT == NULL ) + pszSrcWKT = SRS_WKT_WGS84; + } + + else if( (pszMethod == NULL || EQUAL(pszMethod,"GEOLOC_ARRAY")) + && (papszMD = GDALGetMetadata( hSrcDS, "GEOLOCATION" )) != NULL ) + { + psInfo->pSrcGeoLocTransformArg = + GDALCreateGeoLocTransformer( hSrcDS, papszMD, FALSE ); + + if( psInfo->pSrcGeoLocTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszSrcWKT == NULL ) + pszSrcWKT = CSLFetchNameValue( papszMD, "SRS" ); + } + + else if( pszMethod != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to compute a %s based transformation between pixel/line\n" + "and georeferenced coordinates for %s.\n", + pszMethod, GDALGetDescription( hSrcDS ) ); + + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to compute a transformation between pixel/line\n" + "and georeferenced coordinates for %s.\n" + "There is no affine transformation and no GCPs.", + GDALGetDescription( hSrcDS ) ); + + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for destination image. */ +/* If we have no destination use a unit transform. */ +/* -------------------------------------------------------------------- */ + const char *pszDstMethod = CSLFetchNameValue( papszOptions, "DST_METHOD" ); + + if( !hDstDS ) + { + psInfo->adfDstGeoTransform[0] = 0.0; + psInfo->adfDstGeoTransform[1] = 1.0; + psInfo->adfDstGeoTransform[2] = 0.0; + psInfo->adfDstGeoTransform[3] = 0.0; + psInfo->adfDstGeoTransform[4] = 0.0; + psInfo->adfDstGeoTransform[5] = 1.0; + memcpy( psInfo->adfDstInvGeoTransform, psInfo->adfDstGeoTransform, + sizeof(double) * 6 ); + } + else if( (pszDstMethod == NULL || EQUAL(pszDstMethod,"GEOTRANSFORM")) + && GDALGetGeoTransform( hDstDS, psInfo->adfDstGeoTransform ) == CE_None) + { + if( pszDstWKT == NULL ) + pszDstWKT = GDALGetProjectionRef( hDstDS ); + + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + else if( bGCPUseOK + && (pszDstMethod == NULL || EQUAL(pszDstMethod,"GCP_POLYNOMIAL") ) + && GDALGetGCPCount( hDstDS ) > 0 && nOrder >= 0 ) + { + if(bRefine) + { + psInfo->pDstGCPTransformArg = + GDALCreateGCPRefineTransformer( GDALGetGCPCount( hDstDS ), + GDALGetGCPs( hDstDS ), nOrder, + FALSE, dfTolerance, + nMinimumGcps ); + } + else + { + psInfo->pDstGCPTransformArg = + GDALCreateGCPTransformer( GDALGetGCPCount( hDstDS ), + GDALGetGCPs( hDstDS ), nOrder, + FALSE ); + } + + if( psInfo->pDstGCPTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszDstWKT == NULL ) + pszDstWKT = GDALGetGCPProjection( hDstDS ); + } + else if( bGCPUseOK + && GDALGetGCPCount( hDstDS ) > 0 + && nOrder <= 0 + && (pszDstMethod == NULL || EQUAL(pszDstMethod,"GCP_TPS")) ) + { + psInfo->pDstTPSTransformArg = + GDALCreateTPSTransformerInt( GDALGetGCPCount( hDstDS ), + GDALGetGCPs( hDstDS ), FALSE, + papszOptions ); + if( psInfo->pDstTPSTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszDstWKT == NULL ) + pszDstWKT = GDALGetGCPProjection( hDstDS ); + } + else if( (pszDstMethod == NULL || EQUAL(pszDstMethod,"RPC")) + && (papszMD = GDALGetMetadata( hDstDS, "RPC" )) != NULL + && GDALExtractRPCInfo( papszMD, &sRPCInfo ) ) + { + psInfo->pDstRPCTransformArg = + GDALCreateRPCTransformer( &sRPCInfo, FALSE, 0.1, papszOptions ); + if( psInfo->pDstRPCTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszDstWKT == NULL ) + pszDstWKT = SRS_WKT_WGS84; + } + + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to compute a transformation between pixel/line\n" + "and georeferenced coordinates for %s.\n" + "There is no affine transformation and no GCPs.", + GDALGetDescription( hDstDS ) ); + + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup reprojection. */ +/* -------------------------------------------------------------------- */ + if( pszSrcWKT != NULL && strlen(pszSrcWKT) > 0 + && pszDstWKT != NULL && strlen(pszDstWKT) > 0 + && !EQUAL(pszSrcWKT,pszDstWKT) ) + { + CPLString osSrcWKT = pszSrcWKT; + if (hSrcDS + && CSLFetchBoolean( papszOptions, "INSERT_CENTER_LONG", TRUE ) ) + osSrcWKT = InsertCenterLong( hSrcDS, osSrcWKT ); + + psInfo->pReprojectArg = + GDALCreateReprojectionTransformer( osSrcWKT.c_str(), pszDstWKT ); + if( psInfo->pReprojectArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + + return psInfo; +} + +/************************************************************************/ +/* GDALRefreshGenImgProjTransformer() */ +/************************************************************************/ + +void GDALRefreshGenImgProjTransformer(void* hTransformArg) +{ + GDALGenImgProjTransformInfo *psInfo = + static_cast( hTransformArg ); + + if (psInfo->pReprojectArg) + { + CPLXMLNode* psXML = GDALSerializeReprojectionTransformer(psInfo->pReprojectArg); + GDALDestroyReprojectionTransformer(psInfo->pReprojectArg); + psInfo->pReprojectArg = GDALDeserializeReprojectionTransformer(psXML); + CPLDestroyXMLNode(psXML); + } +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformer3() */ +/************************************************************************/ + +/** + * Create image to image transformer. + * + * This function creates a transformation object that maps from pixel/line + * coordinates on one image to pixel/line coordinates on another image. The + * images may potentially be georeferenced in different coordinate systems, + * and may used GCPs to map between their pixel/line coordinates and + * georeferenced coordinates (as opposed to the default assumption that their + * geotransform should be used). + * + * This transformer potentially performs three concatenated transformations. + * + * The first stage is from source image pixel/line coordinates to source + * image georeferenced coordinates, and may be done using the geotransform, + * or if not defined using a polynomial model derived from GCPs. If GCPs + * are used this stage is accomplished using GDALGCPTransform(). + * + * The second stage is to change projections from the source coordinate system + * to the destination coordinate system, assuming they differ. This is + * accomplished internally using GDALReprojectionTransform(). + * + * The third stage is converting from destination image georeferenced + * coordinates to destination image coordinates. This is done using the + * destination image geotransform, or if not available, using a polynomial + * model derived from GCPs. If GCPs are used this stage is accomplished using + * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the + * transformation is created. + * + * @param pszSrcWKT source WKT (or NULL). + * @param padfSrcGeoTransform source geotransform (or NULL). + * @param pszDstWKT destination WKT (or NULL). + * @param padfDstGeoTransform destination geotransform (or NULL). + * + * @return handle suitable for use GDALGenImgProjTransform(), and to be + * deallocated with GDALDestroyGenImgProjTransformer() or NULL on failure. + */ + +void * +GDALCreateGenImgProjTransformer3( const char *pszSrcWKT, + const double *padfSrcGeoTransform, + const char *pszDstWKT, + const double *padfDstGeoTransform ) + +{ + GDALGenImgProjTransformInfo *psInfo; + +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = (GDALGenImgProjTransformInfo *) + CPLCalloc(sizeof(GDALGenImgProjTransformInfo),1); + + strcpy( psInfo->sTI.szSignature, "GTI" ); + psInfo->sTI.pszClassName = "GDALGenImgProjTransformer"; + psInfo->sTI.pfnTransform = GDALGenImgProjTransform; + psInfo->sTI.pfnCleanup = GDALDestroyGenImgProjTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeGenImgProjTransformer; + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for the source image. */ +/* -------------------------------------------------------------------- */ + if( padfSrcGeoTransform ) + { + memcpy( psInfo->adfSrcGeoTransform, padfSrcGeoTransform, + sizeof(psInfo->adfSrcGeoTransform) ); + if( !GDALInvGeoTransform( psInfo->adfSrcGeoTransform, + psInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + else + { + psInfo->adfSrcGeoTransform[0] = 0.0; + psInfo->adfSrcGeoTransform[1] = 1.0; + psInfo->adfSrcGeoTransform[2] = 0.0; + psInfo->adfSrcGeoTransform[3] = 0.0; + psInfo->adfSrcGeoTransform[4] = 0.0; + psInfo->adfSrcGeoTransform[5] = 1.0; + memcpy( psInfo->adfSrcInvGeoTransform, psInfo->adfSrcGeoTransform, + sizeof(double) * 6 ); + } + +/* -------------------------------------------------------------------- */ +/* Setup reprojection. */ +/* -------------------------------------------------------------------- */ + if( pszSrcWKT != NULL && strlen(pszSrcWKT) > 0 + && pszDstWKT != NULL && strlen(pszDstWKT) > 0 + && !EQUAL(pszSrcWKT, pszDstWKT) ) + { + psInfo->pReprojectArg = + GDALCreateReprojectionTransformer( pszSrcWKT, pszDstWKT ); + if( psInfo->pReprojectArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for destination image. */ +/* If we have no destination matrix use a unit transform. */ +/* -------------------------------------------------------------------- */ + if( padfDstGeoTransform ) + { + memcpy( psInfo->adfDstGeoTransform, padfDstGeoTransform, + sizeof(psInfo->adfDstGeoTransform) ); + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + else + { + psInfo->adfDstGeoTransform[0] = 0.0; + psInfo->adfDstGeoTransform[1] = 1.0; + psInfo->adfDstGeoTransform[2] = 0.0; + psInfo->adfDstGeoTransform[3] = 0.0; + psInfo->adfDstGeoTransform[4] = 0.0; + psInfo->adfDstGeoTransform[5] = 1.0; + memcpy( psInfo->adfDstInvGeoTransform, psInfo->adfDstGeoTransform, + sizeof(double) * 6 ); + } + + return psInfo; +} + +/************************************************************************/ +/* GDALSetGenImgProjTransformerDstGeoTransform() */ +/************************************************************************/ + +/** + * Set GenImgProj output geotransform. + * + * Normally the "destination geotransform", or transformation between + * georeferenced output coordinates and pixel/line coordinates on the + * destination file is extracted from the destination file by + * GDALCreateGenImgProjTransformer() and stored in the GenImgProj private + * info. However, sometimes it is inconvenient to have an output file + * handle with appropriate geotransform information when creating the + * transformation. For these cases, this function can be used to apply + * the destination geotransform. + * + * @param hTransformArg the handle to update. + * @param padfGeoTransform the destination geotransform to apply (six doubles). + */ + +void GDALSetGenImgProjTransformerDstGeoTransform( + void *hTransformArg, const double *padfGeoTransform ) + +{ + VALIDATE_POINTER0( hTransformArg, "GDALSetGenImgProjTransformerDstGeoTransform" ); + + GDALGenImgProjTransformInfo *psInfo = + static_cast( hTransformArg ); + + memcpy( psInfo->adfDstGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + } +} + +/************************************************************************/ +/* GDALDestroyGenImgProjTransformer() */ +/************************************************************************/ + +/** + * GenImgProjTransformer deallocator. + * + * This function is used to deallocate the handle created with + * GDALCreateGenImgProjTransformer(). + * + * @param hTransformArg the handle to deallocate. + */ + +void GDALDestroyGenImgProjTransformer( void *hTransformArg ) + +{ + VALIDATE_POINTER0( hTransformArg, "GDALDestroyGenImgProjTransformer" ); + + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) hTransformArg; + + if( psInfo->pSrcGCPTransformArg != NULL ) + GDALDestroyGCPTransformer( psInfo->pSrcGCPTransformArg ); + + if( psInfo->pSrcTPSTransformArg != NULL ) + GDALDestroyTPSTransformer( psInfo->pSrcTPSTransformArg ); + + if( psInfo->pSrcRPCTransformArg != NULL ) + GDALDestroyRPCTransformer( psInfo->pSrcRPCTransformArg ); + + if( psInfo->pSrcGeoLocTransformArg != NULL ) + GDALDestroyGeoLocTransformer( psInfo->pSrcGeoLocTransformArg ); + + if( psInfo->pDstGCPTransformArg != NULL ) + GDALDestroyGCPTransformer( psInfo->pDstGCPTransformArg ); + + if( psInfo->pDstRPCTransformArg != NULL ) + GDALDestroyRPCTransformer( psInfo->pDstRPCTransformArg ); + + if( psInfo->pDstTPSTransformArg != NULL ) + GDALDestroyTPSTransformer( psInfo->pDstTPSTransformArg ); + + if( psInfo->pReprojectArg != NULL ) + GDALDestroyReprojectionTransformer( psInfo->pReprojectArg ); + + CPLFree( psInfo ); +} + +/************************************************************************/ +/* GDALGenImgProjTransform() */ +/************************************************************************/ + +/** + * Perform general image reprojection transformation. + * + * Actually performs the transformation setup in + * GDALCreateGenImgProjTransformer(). This function matches the signature + * required by the GDALTransformerFunc(), and more details on the arguments + * can be found in that topic. + */ + +int GDALGenImgProjTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ) +{ + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) pTransformArg; + int i; + double *padfGeoTransform; + void *pGCPTransformArg; + void *pRPCTransformArg; + void *pTPSTransformArg; + void *pGeoLocTransformArg; + + for( i = 0; i < nPointCount; i++ ) + { + panSuccess[i] = ( padfX[i] != HUGE_VAL && padfY[i] != HUGE_VAL ); + } + +/* -------------------------------------------------------------------- */ +/* Convert from src (dst) pixel/line to src (dst) */ +/* georeferenced coordinates. */ +/* -------------------------------------------------------------------- */ + if( bDstToSrc ) + { + padfGeoTransform = psInfo->adfDstGeoTransform; + pGCPTransformArg = psInfo->pDstGCPTransformArg; + pRPCTransformArg = psInfo->pDstRPCTransformArg; + pTPSTransformArg = psInfo->pDstTPSTransformArg; + pGeoLocTransformArg = NULL; + } + else + { + padfGeoTransform = psInfo->adfSrcGeoTransform; + pGCPTransformArg = psInfo->pSrcGCPTransformArg; + pRPCTransformArg = psInfo->pSrcRPCTransformArg; + pTPSTransformArg = psInfo->pSrcTPSTransformArg; + pGeoLocTransformArg = psInfo->pSrcGeoLocTransformArg; + } + + if( pGCPTransformArg != NULL ) + { + if( !GDALGCPTransform( pGCPTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pTPSTransformArg != NULL ) + { + if( !GDALTPSTransform( pTPSTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pRPCTransformArg != NULL ) + { + if( !GDALRPCTransform( pRPCTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pGeoLocTransformArg != NULL ) + { + if( !GDALGeoLocTransform( pGeoLocTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else + { + for( i = 0; i < nPointCount; i++ ) + { + double dfNewX, dfNewY; + + if( padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL ) + { + panSuccess[i] = FALSE; + continue; + } + + dfNewX = padfGeoTransform[0] + + padfX[i] * padfGeoTransform[1] + + padfY[i] * padfGeoTransform[2]; + dfNewY = padfGeoTransform[3] + + padfX[i] * padfGeoTransform[4] + + padfY[i] * padfGeoTransform[5]; + + padfX[i] = dfNewX; + padfY[i] = dfNewY; + } + } + +/* -------------------------------------------------------------------- */ +/* Reproject if needed. */ +/* -------------------------------------------------------------------- */ + if( psInfo->pReprojectArg ) + { + if( !GDALReprojectionTransform( psInfo->pReprojectArg, bDstToSrc, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Convert dst (src) georef coordinates back to pixel/line. */ +/* -------------------------------------------------------------------- */ + if( bDstToSrc ) + { + padfGeoTransform = psInfo->adfSrcInvGeoTransform; + pGCPTransformArg = psInfo->pSrcGCPTransformArg; + pRPCTransformArg = psInfo->pSrcRPCTransformArg; + pTPSTransformArg = psInfo->pSrcTPSTransformArg; + pGeoLocTransformArg = psInfo->pSrcGeoLocTransformArg; + } + else + { + padfGeoTransform = psInfo->adfDstInvGeoTransform; + pGCPTransformArg = psInfo->pDstGCPTransformArg; + pRPCTransformArg = psInfo->pDstRPCTransformArg; + pTPSTransformArg = psInfo->pDstTPSTransformArg; + pGeoLocTransformArg = NULL; + } + + if( pGCPTransformArg != NULL ) + { + if( !GDALGCPTransform( pGCPTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pTPSTransformArg != NULL ) + { + if( !GDALTPSTransform( pTPSTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pRPCTransformArg != NULL ) + { + if( !GDALRPCTransform( pRPCTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pGeoLocTransformArg != NULL ) + { + if( !GDALGeoLocTransform( pGeoLocTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else + { + for( i = 0; i < nPointCount; i++ ) + { + double dfNewX, dfNewY; + + if( !panSuccess[i] ) + continue; + + dfNewX = padfGeoTransform[0] + + padfX[i] * padfGeoTransform[1] + + padfY[i] * padfGeoTransform[2]; + dfNewY = padfGeoTransform[3] + + padfX[i] * padfGeoTransform[4] + + padfY[i] * padfGeoTransform[5]; + + padfX[i] = dfNewX; + padfY[i] = dfNewY; + } + } + + return TRUE; +} + +/************************************************************************/ +/* GDALSerializeGenImgProjTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +GDALSerializeGenImgProjTransformer( void *pTransformArg ) + +{ + char szWork[200]; + CPLXMLNode *psTree; + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GenImgProjTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Handle GCP transformation. */ +/* -------------------------------------------------------------------- */ + if( psInfo->pSrcGCPTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcGCPTransformer" ); + + psTransformer = GDALSerializeTransformer( GDALGCPTransform, + psInfo->pSrcGCPTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle TPS transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pSrcTPSTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcTPSTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pSrcTPSTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle GeoLoc transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pSrcGeoLocTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcGeoLocTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pSrcGeoLocTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle RPC transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pSrcRPCTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcRPCTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pSrcRPCTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle source geotransforms. */ +/* -------------------------------------------------------------------- */ + else + { + sprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfSrcGeoTransform[0], + psInfo->adfSrcGeoTransform[1], + psInfo->adfSrcGeoTransform[2], + psInfo->adfSrcGeoTransform[3], + psInfo->adfSrcGeoTransform[4], + psInfo->adfSrcGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "SrcGeoTransform", szWork ); + + sprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfSrcInvGeoTransform[0], + psInfo->adfSrcInvGeoTransform[1], + psInfo->adfSrcInvGeoTransform[2], + psInfo->adfSrcInvGeoTransform[3], + psInfo->adfSrcInvGeoTransform[4], + psInfo->adfSrcInvGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "SrcInvGeoTransform", szWork ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Dest GCP transformation. */ +/* -------------------------------------------------------------------- */ + if( psInfo->pDstGCPTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "DstGCPTransformer" ); + + psTransformer = GDALSerializeTransformer( GDALGCPTransform, + psInfo->pDstGCPTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Dest TPS transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pDstTPSTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "DstTPSTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pDstTPSTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Dest RPC transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pDstRPCTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "DstRPCTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pDstRPCTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle destination geotransforms. */ +/* -------------------------------------------------------------------- */ + else + { + sprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfDstGeoTransform[0], + psInfo->adfDstGeoTransform[1], + psInfo->adfDstGeoTransform[2], + psInfo->adfDstGeoTransform[3], + psInfo->adfDstGeoTransform[4], + psInfo->adfDstGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "DstGeoTransform", szWork ); + + sprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfDstInvGeoTransform[0], + psInfo->adfDstInvGeoTransform[1], + psInfo->adfDstInvGeoTransform[2], + psInfo->adfDstInvGeoTransform[3], + psInfo->adfDstInvGeoTransform[4], + psInfo->adfDstInvGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "DstInvGeoTransform", szWork ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a reprojection transformer? */ +/* -------------------------------------------------------------------- */ + if( psInfo->pReprojectArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "ReprojectTransformer" ); + + psTransformer = GDALSerializeTransformer( GDALReprojectionTransform, + psInfo->pReprojectArg ); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeGenImgProjTransformer() */ +/************************************************************************/ + +void *GDALDeserializeGenImgProjTransformer( CPLXMLNode *psTree ) + +{ + GDALGenImgProjTransformInfo *psInfo; + CPLXMLNode *psSubtree; + +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = (GDALGenImgProjTransformInfo *) + CPLCalloc(sizeof(GDALGenImgProjTransformInfo),1); + + strcpy( psInfo->sTI.szSignature, "GTI" ); + psInfo->sTI.pszClassName = "GDALGenImgProjTransformer"; + psInfo->sTI.pfnTransform = GDALGenImgProjTransform; + psInfo->sTI.pfnCleanup = GDALDestroyGenImgProjTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeGenImgProjTransformer; + +/* -------------------------------------------------------------------- */ +/* SrcGeotransform */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "SrcGeoTransform" ) != NULL ) + { + sscanf( CPLGetXMLValue( psTree, "SrcGeoTransform", "" ), + "%lg,%lg,%lg,%lg,%lg,%lg", + psInfo->adfSrcGeoTransform + 0, + psInfo->adfSrcGeoTransform + 1, + psInfo->adfSrcGeoTransform + 2, + psInfo->adfSrcGeoTransform + 3, + psInfo->adfSrcGeoTransform + 4, + psInfo->adfSrcGeoTransform + 5 ); + + if( CPLGetXMLNode( psTree, "SrcInvGeoTransform" ) != NULL ) + { + sscanf( CPLGetXMLValue( psTree, "SrcInvGeoTransform", "" ), + "%lg,%lg,%lg,%lg,%lg,%lg", + psInfo->adfSrcInvGeoTransform + 0, + psInfo->adfSrcInvGeoTransform + 1, + psInfo->adfSrcInvGeoTransform + 2, + psInfo->adfSrcInvGeoTransform + 3, + psInfo->adfSrcInvGeoTransform + 4, + psInfo->adfSrcInvGeoTransform + 5 ); + + } + else + { + if( !GDALInvGeoTransform( psInfo->adfSrcGeoTransform, + psInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Src GCP Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcGCPTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcGCPTransformArg = + GDALDeserializeGCPTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Src TPS Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcTPSTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcTPSTransformArg = + GDALDeserializeTPSTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Src GeoLoc Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcGeoLocTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcGeoLocTransformArg = + GDALDeserializeGeoLocTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Src RPC Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcRPCTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcRPCTransformArg = + GDALDeserializeRPCTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Dst TPS Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "DstTPSTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pDstTPSTransformArg = + GDALDeserializeTPSTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Dst RPC Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "DstRPCTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pDstRPCTransformArg = + GDALDeserializeRPCTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* DstGeotransform */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "DstGeoTransform" ) != NULL ) + { + sscanf( CPLGetXMLValue( psTree, "DstGeoTransform", "" ), + "%lg,%lg,%lg,%lg,%lg,%lg", + psInfo->adfDstGeoTransform + 0, + psInfo->adfDstGeoTransform + 1, + psInfo->adfDstGeoTransform + 2, + psInfo->adfDstGeoTransform + 3, + psInfo->adfDstGeoTransform + 4, + psInfo->adfDstGeoTransform + 5 ); + + if( CPLGetXMLNode( psTree, "DstInvGeoTransform" ) != NULL ) + { + sscanf( CPLGetXMLValue( psTree, "DstInvGeoTransform", "" ), + "%lg,%lg,%lg,%lg,%lg,%lg", + psInfo->adfDstInvGeoTransform + 0, + psInfo->adfDstInvGeoTransform + 1, + psInfo->adfDstInvGeoTransform + 2, + psInfo->adfDstInvGeoTransform + 3, + psInfo->adfDstInvGeoTransform + 4, + psInfo->adfDstInvGeoTransform + 5 ); + + } + else + { + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Reproject transformer */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "ReprojectTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pReprojectArg = + GDALDeserializeReprojectionTransformer( psSubtree->psChild ); + } + + return psInfo; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALReprojectionTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct { + GDALTransformerInfo sTI; + + OGRCoordinateTransformation *poForwardTransform; + OGRCoordinateTransformation *poReverseTransform; +} GDALReprojectionTransformInfo; + +/************************************************************************/ +/* GDALCreateReprojectionTransformer() */ +/************************************************************************/ + +/** + * Create reprojection transformer. + * + * Creates a callback data structure suitable for use with + * GDALReprojectionTransformation() to represent a transformation from + * one geographic or projected coordinate system to another. On input + * the coordinate systems are described in OpenGIS WKT format. + * + * Internally the OGRCoordinateTransformation object is used to implement + * the reprojection. + * + * @param pszSrcWKT the coordinate system for the source coordinate system. + * @param pszDstWKT the coordinate system for the destination coordinate + * system. + * + * @return Handle for use with GDALReprojectionTransform(), or NULL if the + * system fails to initialize the reprojection. + **/ + +void *GDALCreateReprojectionTransformer( const char *pszSrcWKT, + const char *pszDstWKT ) + +{ + OGRSpatialReference oSrcSRS, oDstSRS; + OGRCoordinateTransformation *poForwardTransform; + +/* -------------------------------------------------------------------- */ +/* Ingest the SRS definitions. */ +/* -------------------------------------------------------------------- */ + if( oSrcSRS.importFromWkt( (char **) &pszSrcWKT ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import coordinate system `%s'.", + pszSrcWKT ); + return NULL; + } + if( oDstSRS.importFromWkt( (char **) &pszDstWKT ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import coordinate system `%s'.", + pszSrcWKT ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build the forward coordinate transformation. */ +/* -------------------------------------------------------------------- */ + poForwardTransform = OGRCreateCoordinateTransformation(&oSrcSRS,&oDstSRS); + + if( poForwardTransform == NULL ) + // OGRCreateCoordinateTransformation() will report errors on its own. + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a structure to hold the transform info, and also */ +/* build reverse transform. We assume that if the forward */ +/* transform can be created, then so can the reverse one. */ +/* -------------------------------------------------------------------- */ + GDALReprojectionTransformInfo *psInfo; + + psInfo = (GDALReprojectionTransformInfo *) + CPLCalloc(sizeof(GDALReprojectionTransformInfo),1); + + psInfo->poForwardTransform = poForwardTransform; + psInfo->poReverseTransform = + OGRCreateCoordinateTransformation(&oDstSRS,&oSrcSRS); + + strcpy( psInfo->sTI.szSignature, "GTI" ); + psInfo->sTI.pszClassName = "GDALReprojectionTransformer"; + psInfo->sTI.pfnTransform = GDALReprojectionTransform; + psInfo->sTI.pfnCleanup = GDALDestroyReprojectionTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeReprojectionTransformer; + + return psInfo; +} + +/************************************************************************/ +/* GDALDestroyReprojectionTransformer() */ +/************************************************************************/ + +/** + * Destroy reprojection transformation. + * + * @param pTransformArg the transformation handle returned by + * GDALCreateReprojectionTransformer(). + */ + +void GDALDestroyReprojectionTransformer( void *pTransformArg ) + +{ + VALIDATE_POINTER0( pTransformArg, "GDALDestroyReprojectionTransformer" ); + + GDALReprojectionTransformInfo *psInfo = + (GDALReprojectionTransformInfo *) pTransformArg; + + if( psInfo->poForwardTransform ) + delete psInfo->poForwardTransform; + + if( psInfo->poReverseTransform ) + delete psInfo->poReverseTransform; + + CPLFree( psInfo ); +} + +/************************************************************************/ +/* GDALReprojectionTransform() */ +/************************************************************************/ + +/** + * Perform reprojection transformation. + * + * Actually performs the reprojection transformation described in + * GDALCreateReprojectionTransformer(). This function matches the + * GDALTransformerFunc() signature. Details of the arguments are described + * there. + */ + +int GDALReprojectionTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ) + +{ + GDALReprojectionTransformInfo *psInfo = + (GDALReprojectionTransformInfo *) pTransformArg; + int bSuccess; + + if( bDstToSrc ) + bSuccess = psInfo->poReverseTransform->TransformEx( + nPointCount, padfX, padfY, padfZ, panSuccess ); + else + bSuccess = psInfo->poForwardTransform->TransformEx( + nPointCount, padfX, padfY, padfZ, panSuccess ); + + return bSuccess; +} + +/************************************************************************/ +/* GDALSerializeReprojectionTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +GDALSerializeReprojectionTransformer( void *pTransformArg ) + +{ + CPLXMLNode *psTree; + GDALReprojectionTransformInfo *psInfo = + (GDALReprojectionTransformInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "ReprojectionTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Handle SourceCS. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference *poSRS; + char *pszWKT = NULL; + + poSRS = psInfo->poForwardTransform->GetSourceCS(); + poSRS->exportToWkt( &pszWKT ); + CPLCreateXMLElementAndValue( psTree, "SourceSRS", pszWKT ); + CPLFree( pszWKT ); + +/* -------------------------------------------------------------------- */ +/* Handle DestinationCS. */ +/* -------------------------------------------------------------------- */ + poSRS = psInfo->poForwardTransform->GetTargetCS(); + poSRS->exportToWkt( &pszWKT ); + CPLCreateXMLElementAndValue( psTree, "TargetSRS", pszWKT ); + CPLFree( pszWKT ); + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeReprojectionTransformer() */ +/************************************************************************/ + +static void * +GDALDeserializeReprojectionTransformer( CPLXMLNode *psTree ) + +{ + const char *pszSourceSRS = CPLGetXMLValue( psTree, "SourceSRS", NULL ); + const char *pszTargetSRS= CPLGetXMLValue( psTree, "TargetSRS", NULL ); + char *pszSourceWKT = NULL, *pszTargetWKT = NULL; + void *pResult = NULL; + + if( pszSourceSRS != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszSourceSRS ) == OGRERR_NONE ) + oSRS.exportToWkt( &pszSourceWKT ); + } + + if( pszTargetSRS != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszTargetSRS ) == OGRERR_NONE ) + oSRS.exportToWkt( &pszTargetWKT ); + } + + if( pszSourceWKT != NULL && pszTargetWKT != NULL ) + { + pResult = GDALCreateReprojectionTransformer( pszSourceWKT, + pszTargetWKT ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "ReprojectionTransformer definition missing either\n" + "SourceSRS or TargetSRS definition." ); + } + + CPLFree( pszSourceWKT ); + CPLFree( pszTargetWKT ); + + return pResult; +} + +/************************************************************************/ +/* ==================================================================== */ +/* Approximate transformer. */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct +{ + GDALTransformerInfo sTI; + + GDALTransformerFunc pfnBaseTransformer; + void *pBaseCBData; + double dfMaxError; + + int bOwnSubtransformer; +} ApproxTransformInfo; + +/************************************************************************/ +/* GDALCloneApproxTransformer() */ +/************************************************************************/ + +void* GDALCloneApproxTransformer( void *hTransformArg ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCloneApproxTransformer", NULL ); + + ApproxTransformInfo *psInfo = + (ApproxTransformInfo *) hTransformArg; + + ApproxTransformInfo *psClonedInfo = (ApproxTransformInfo *) + CPLMalloc(sizeof(ApproxTransformInfo)); + + memcpy(psClonedInfo, psInfo, sizeof(ApproxTransformInfo)); + if( psClonedInfo->pBaseCBData ) + { + psClonedInfo->pBaseCBData = GDALCloneTransformer( psInfo->pBaseCBData ); + if( psClonedInfo->pBaseCBData == NULL ) + { + CPLFree(psClonedInfo); + return NULL; + } + } + psClonedInfo->bOwnSubtransformer = TRUE; + + return psClonedInfo; +} + +/************************************************************************/ +/* GDALSerializeApproxTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +GDALSerializeApproxTransformer( void *pTransformArg ) + +{ + CPLXMLNode *psTree; + ApproxTransformInfo *psInfo = (ApproxTransformInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "ApproxTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Attach max error. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psTree, "MaxError", + CPLString().Printf("%g",psInfo->dfMaxError) ); + +/* -------------------------------------------------------------------- */ +/* Capture underlying transformer. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "BaseTransformer" ); + + psTransformer = GDALSerializeTransformer( psInfo->pfnBaseTransformer, + psInfo->pBaseCBData ); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + + return psTree; +} + +/************************************************************************/ +/* GDALCreateApproxTransformer() */ +/************************************************************************/ + +/** + * Create an approximating transformer. + * + * This function creates a context for an approximated transformer. Basically + * a high precision transformer is supplied as input and internally linear + * approximations are computed to generate results to within a defined + * precision. + * + * The approximation is actually done at the point where GDALApproxTransform() + * calls are made, and depend on the assumption that the roughly linear. The + * first and last point passed in must be the extreme values and the + * intermediate values should describe a curve between the end points. The + * approximator transforms and center using the approximate transformer, and + * then compares the true middle transformed value to a linear approximation + * based on the end points. If the error is within the supplied threshold + * then the end points are used to linearly approximate all the values + * otherwise the inputs points are split into two smaller sets, and the + * function recursively called till a sufficiently small set of points if found + * that the linear approximation is OK, or that all the points are exactly + * computed. + * + * This function is very suitable for approximating transformation results + * from output pixel/line space to input coordinates for warpers that operate + * on one input scanline at a time. Care should be taken using it in other + * circumstances as little internal validation is done, in order to keep things + * fast. + * + * @param pfnBaseTransformer the high precision transformer which should be + * approximated. + * @param pBaseTransformArg the callback argument for the high precision + * transformer. + * @param dfMaxError the maximum cartesian error in the "output" space that + * is to be accepted in the linear approximation. + * + * @return callback pointer suitable for use with GDALApproxTransform(). It + * should be deallocated with GDALDestroyApproxTransformer(). + */ + +void *GDALCreateApproxTransformer( GDALTransformerFunc pfnBaseTransformer, + void *pBaseTransformArg, double dfMaxError) + +{ + ApproxTransformInfo *psATInfo; + + psATInfo = (ApproxTransformInfo*) CPLMalloc(sizeof(ApproxTransformInfo)); + psATInfo->pfnBaseTransformer = pfnBaseTransformer; + psATInfo->pBaseCBData = pBaseTransformArg; + psATInfo->dfMaxError = dfMaxError; + psATInfo->bOwnSubtransformer = FALSE; + + strcpy( psATInfo->sTI.szSignature, "GTI" ); + psATInfo->sTI.pszClassName = "GDALApproxTransformer"; + psATInfo->sTI.pfnTransform = GDALApproxTransform; + psATInfo->sTI.pfnCleanup = GDALDestroyApproxTransformer; + psATInfo->sTI.pfnSerialize = GDALSerializeApproxTransformer; + + return psATInfo; +} + +/************************************************************************/ +/* GDALApproxTransformerOwnsSubtransformer() */ +/************************************************************************/ + +void GDALApproxTransformerOwnsSubtransformer( void *pCBData, int bOwnFlag ) + +{ + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + + psATInfo->bOwnSubtransformer = bOwnFlag; +} + +/************************************************************************/ +/* GDALDestroyApproxTransformer() */ +/************************************************************************/ + +/** + * Cleanup approximate transformer. + * + * Deallocates the resources allocated by GDALCreateApproxTransformer(). + * + * @param pCBData callback data originally returned by + * GDALCreateApproxTransformer(). + */ + +void GDALDestroyApproxTransformer( void * pCBData ) + +{ + VALIDATE_POINTER0( pCBData, "GDALDestroyApproxTransformer" ); + + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + + if( psATInfo->bOwnSubtransformer ) + GDALDestroyTransformer( psATInfo->pBaseCBData ); + + CPLFree( pCBData ); +} + +/************************************************************************/ +/* GDALApproxTransform() */ +/************************************************************************/ + +/** + * Perform approximate transformation. + * + * Actually performs the approximate transformation described in + * GDALCreateApproxTransformer(). This function matches the + * GDALTransformerFunc() signature. Details of the arguments are described + * there. + */ + +int GDALApproxTransform( void *pCBData, int bDstToSrc, int nPoints, + double *x, double *y, double *z, int *panSuccess ) + +{ + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + double x2[3], y2[3], z2[3], dfDeltaX, dfDeltaY, dfError, dfDist, dfDeltaZ; + int nMiddle, anSuccess2[3], i, bSuccess; + + nMiddle = (nPoints-1)/2; + +/* -------------------------------------------------------------------- */ +/* Bail if our preconditions are not met, or if error is not */ +/* acceptable. */ +/* -------------------------------------------------------------------- */ + if( y[0] != y[nPoints-1] || y[0] != y[nMiddle] + || x[0] == x[nPoints-1] || x[0] == x[nMiddle] + || psATInfo->dfMaxError == 0.0 || nPoints <= 5 ) + { + return psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nPoints, x, y, z, panSuccess ); + } + +/* -------------------------------------------------------------------- */ +/* Transform first, last and middle point. */ +/* -------------------------------------------------------------------- */ + x2[0] = x[0]; + y2[0] = y[0]; + z2[0] = z[0]; + x2[1] = x[nMiddle]; + y2[1] = y[nMiddle]; + z2[1] = z[nMiddle]; + x2[2] = x[nPoints-1]; + y2[2] = y[nPoints-1]; + z2[2] = z[nPoints-1]; + + bSuccess = + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, 3, + x2, y2, z2, anSuccess2 ); + if( !bSuccess || !anSuccess2[0] || !anSuccess2[1] || !anSuccess2[2] ) + return psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nPoints, x, y, z, panSuccess ); + +/* -------------------------------------------------------------------- */ +/* Is the error at the middle acceptable relative to an */ +/* interpolation of the middle position? */ +/* -------------------------------------------------------------------- */ + dfDeltaX = (x2[2] - x2[0]) / (x[nPoints-1] - x[0]); + dfDeltaY = (y2[2] - y2[0]) / (x[nPoints-1] - x[0]); + dfDeltaZ = (z2[2] - z2[0]) / (x[nPoints-1] - x[0]); + + dfError = fabs((x2[0] + dfDeltaX * (x[nMiddle] - x[0])) - x2[1]) + + fabs((y2[0] + dfDeltaY * (x[nMiddle] - x[0])) - y2[1]); + + if( dfError > psATInfo->dfMaxError ) + { +#ifdef notdef + CPLDebug( "GDAL", "ApproxTransformer - " + "error %g over threshold %g, subdivide %d points.", + dfError, psATInfo->dfMaxError, nPoints ); +#endif + + bSuccess = + GDALApproxTransform( psATInfo, bDstToSrc, nMiddle, + x, y, z, panSuccess ); + + if( !bSuccess ) + return FALSE; + + bSuccess = + GDALApproxTransform( psATInfo, bDstToSrc, nPoints - nMiddle, + x+nMiddle, y+nMiddle, z+nMiddle, + panSuccess+nMiddle ); + + if( !bSuccess ) + return FALSE; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Error is OK since this is just used to compute output bounds */ +/* of newly created file for gdalwarper. So just use affine */ +/* approximation of the reverse transform. Eventually we */ +/* should implement iterative searching to find a result within */ +/* our error threshold. */ +/* -------------------------------------------------------------------- */ + for( i = nPoints-1; i >= 0; i-- ) + { + dfDist = (x[i] - x[0]); + y[i] = y2[0] + dfDeltaY * dfDist; + x[i] = x2[0] + dfDeltaX * dfDist; + z[i] = z2[0] + dfDeltaZ * dfDist; + panSuccess[i] = TRUE; + } + + return TRUE; +} + +/************************************************************************/ +/* GDALDeserializeApproxTransformer() */ +/************************************************************************/ + +static void * +GDALDeserializeApproxTransformer( CPLXMLNode *psTree ) + +{ + double dfMaxError = atof(CPLGetXMLValue( psTree, "MaxError", "0.25" )); + CPLXMLNode *psContainer; + GDALTransformerFunc pfnBaseTransform = NULL; + void *pBaseCBData = NULL; + + psContainer = CPLGetXMLNode( psTree, "BaseTransformer" ); + + if( psContainer != NULL && psContainer->psChild != NULL ) + { + GDALDeserializeTransformer( psContainer->psChild, + &pfnBaseTransform, + &pBaseCBData ); + + } + + if( pfnBaseTransform == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot get base transform for approx transformer." ); + return NULL; + } + else + { + void *pApproxCBData = GDALCreateApproxTransformer( pfnBaseTransform, + pBaseCBData, + dfMaxError ); + GDALApproxTransformerOwnsSubtransformer( pApproxCBData, TRUE ); + + return pApproxCBData; + } +} + +/************************************************************************/ +/* GDALApplyGeoTransform() */ +/************************************************************************/ + +/** + * Apply GeoTransform to x/y coordinate. + * + * Applies the following computation, converting a (pixel,line) coordinate + * into a georeferenced (geo_x,geo_y) location. + * + * *pdfGeoX = padfGeoTransform[0] + dfPixel * padfGeoTransform[1] + * + dfLine * padfGeoTransform[2]; + * *pdfGeoY = padfGeoTransform[3] + dfPixel * padfGeoTransform[4] + * + dfLine * padfGeoTransform[5]; + * + * @param padfGeoTransform Six coefficient GeoTransform to apply. + * @param dfPixel Input pixel position. + * @param dfLine Input line position. + * @param pdfGeoX output location where geo_x (easting/longitude) location is placed. + * @param pdfGeoY output location where geo_y (northing/latitude) location is placed. + */ + +void CPL_STDCALL GDALApplyGeoTransform( double *padfGeoTransform, + double dfPixel, double dfLine, + double *pdfGeoX, double *pdfGeoY ) +{ + *pdfGeoX = padfGeoTransform[0] + dfPixel * padfGeoTransform[1] + + dfLine * padfGeoTransform[2]; + *pdfGeoY = padfGeoTransform[3] + dfPixel * padfGeoTransform[4] + + dfLine * padfGeoTransform[5]; +} + +/************************************************************************/ +/* GDALInvGeoTransform() */ +/************************************************************************/ + +/** + * Invert Geotransform. + * + * This function will invert a standard 3x2 set of GeoTransform coefficients. + * This converts the equation from being pixel to geo to being geo to pixel. + * + * @param gt_in Input geotransform (six doubles - unaltered). + * @param gt_out Output geotransform (six doubles - updated). + * + * @return TRUE on success or FALSE if the equation is uninvertable. + */ + +int CPL_STDCALL GDALInvGeoTransform( double *gt_in, double *gt_out ) + +{ + double det, inv_det; + + /* Special case - no rotation - to avoid computing determinate */ + /* and potential precision issues. */ + if( gt_in[2] == 0.0 && gt_in[4] == 0.0 && + gt_in[1] != 0.0 && gt_in[5] != 0.0 ) + { + /*X = gt_in[0] + x * gt_in[1] + Y = gt_in[3] + y * gt_in[5] + --> + x = -gt_in[0] / gt_in[1] + (1 / gt_in[1]) * X + y = -gt_in[3] / gt_in[5] + (1 / gt_in[5]) * Y + */ + gt_out[0] = -gt_in[0] / gt_in[1]; + gt_out[1] = 1.0 / gt_in[1]; + gt_out[2] = 0.0; + gt_out[3] = -gt_in[3] / gt_in[5]; + gt_out[4] = 0.0; + gt_out[5] = 1.0 / gt_in[5]; + return 1; + } + + /* we assume a 3rd row that is [1 0 0] */ + + /* Compute determinate */ + + det = gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4]; + + if( fabs(det) < 0.000000000000001 ) + return 0; + + inv_det = 1.0 / det; + + /* compute adjoint, and devide by determinate */ + + gt_out[1] = gt_in[5] * inv_det; + gt_out[4] = -gt_in[4] * inv_det; + + gt_out[2] = -gt_in[2] * inv_det; + gt_out[5] = gt_in[1] * inv_det; + + gt_out[0] = ( gt_in[2] * gt_in[3] - gt_in[0] * gt_in[5]) * inv_det; + gt_out[3] = (-gt_in[1] * gt_in[3] + gt_in[0] * gt_in[4]) * inv_det; + + return 1; +} + +/************************************************************************/ +/* GDALSerializeTransformer() */ +/************************************************************************/ + +CPLXMLNode *GDALSerializeTransformer( GDALTransformerFunc pfnFunc, + void *pTransformArg ) + +{ + VALIDATE_POINTER1( pTransformArg, "GDALSerializeTransformer", NULL ); + + GDALTransformerInfo *psInfo = static_cast(pTransformArg); + + if( psInfo == NULL || !EQUAL(psInfo->szSignature,"GTI") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to serialize non-GTI transformer." ); + return NULL; + } + else if ( psInfo->pfnSerialize == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No serialization function available for this transformer." ); + return NULL; + } + else + { + return psInfo->pfnSerialize( pTransformArg ); + } +} + +/************************************************************************/ +/* GDALRegisterTransformDeserializer() */ +/************************************************************************/ + +static CPLList* psListDeserializer = NULL; +static void* hDeserializerMutex = NULL; + +typedef struct +{ + char* pszTransformName; + GDALTransformerFunc pfnTransformerFunc; + GDALTransformDeserializeFunc pfnDeserializeFunc; +} TransformDeserializerInfo; + +void* GDALRegisterTransformDeserializer(const char* pszTransformName, + GDALTransformerFunc pfnTransformerFunc, + GDALTransformDeserializeFunc pfnDeserializeFunc) +{ + TransformDeserializerInfo* psInfo = + (TransformDeserializerInfo*)CPLMalloc(sizeof(TransformDeserializerInfo)); + psInfo->pszTransformName = CPLStrdup(pszTransformName); + psInfo->pfnTransformerFunc = pfnTransformerFunc; + psInfo->pfnDeserializeFunc = pfnDeserializeFunc; + + CPLMutexHolderD(&hDeserializerMutex); + psListDeserializer = CPLListInsert(psListDeserializer, psInfo, 0); + + return psInfo; +} + +/************************************************************************/ +/* GDALUnregisterTransformDeserializer() */ +/************************************************************************/ + +void GDALUnregisterTransformDeserializer(void* pData) +{ + CPLMutexHolderD(&hDeserializerMutex); + CPLList* psList = psListDeserializer; + CPLList* psLast = NULL; + while(psList) + { + if (psList->pData == pData) + { + TransformDeserializerInfo* psInfo = + (TransformDeserializerInfo*)pData; + CPLFree(psInfo->pszTransformName); + CPLFree(pData); + if (psLast) + psLast->psNext = psList->psNext; + else + psListDeserializer = NULL; + CPLFree(psList); + break; + } + psLast = psList; + psList = psList->psNext; + } +} + +/************************************************************************/ +/* GDALUnregisterTransformDeserializer() */ +/************************************************************************/ + +void GDALCleanupTransformDeserializerMutex() +{ + if( hDeserializerMutex != NULL ) + { + CPLDestroyMutex(hDeserializerMutex); + hDeserializerMutex = NULL; + } +} + +/************************************************************************/ +/* GDALDeserializeTransformer() */ +/************************************************************************/ + +CPLErr GDALDeserializeTransformer( CPLXMLNode *psTree, + GDALTransformerFunc *ppfnFunc, + void **ppTransformArg ) + +{ + *ppfnFunc = NULL; + *ppTransformArg = NULL; + + CPLErrorReset(); + + if( psTree == NULL || psTree->eType != CXT_Element ) + CPLError( CE_Failure, CPLE_AppDefined, + "Malformed element in GDALDeserializeTransformer" ); + else if( EQUAL(psTree->pszValue,"GenImgProjTransformer") ) + { + *ppfnFunc = GDALGenImgProjTransform; + *ppTransformArg = GDALDeserializeGenImgProjTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"ReprojectionTransformer") ) + { + *ppfnFunc = GDALReprojectionTransform; + *ppTransformArg = GDALDeserializeReprojectionTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"GCPTransformer") ) + { + *ppfnFunc = GDALGCPTransform; + *ppTransformArg = GDALDeserializeGCPTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"TPSTransformer") ) + { + *ppfnFunc = GDALTPSTransform; + *ppTransformArg = GDALDeserializeTPSTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"GeoLocTransformer") ) + { + *ppfnFunc = GDALGeoLocTransform; + *ppTransformArg = GDALDeserializeGeoLocTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"RPCTransformer") ) + { + *ppfnFunc = GDALRPCTransform; + *ppTransformArg = GDALDeserializeRPCTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"ApproxTransformer") ) + { + *ppfnFunc = GDALApproxTransform; + *ppTransformArg = GDALDeserializeApproxTransformer( psTree ); + } + else + { + GDALTransformDeserializeFunc pfnDeserializeFunc = NULL; + { + CPLMutexHolderD(&hDeserializerMutex); + CPLList* psList = psListDeserializer; + while(psList) + { + TransformDeserializerInfo* psInfo = + (TransformDeserializerInfo*)psList->pData; + if (strcmp(psInfo->pszTransformName, psTree->pszValue) == 0) + { + *ppfnFunc = psInfo->pfnTransformerFunc; + pfnDeserializeFunc = psInfo->pfnDeserializeFunc; + break; + } + psList = psList->psNext; + } + } + + if (pfnDeserializeFunc != NULL) + { + *ppTransformArg = pfnDeserializeFunc( psTree ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised element '%s' GDALDeserializeTransformer", + psTree->pszValue ); + } + } + + return CPLGetLastErrorType(); +} + +/************************************************************************/ +/* GDALDestroyTransformer() */ +/************************************************************************/ + +void GDALDestroyTransformer( void *pTransformArg ) + +{ + GDALTransformerInfo *psInfo = (GDALTransformerInfo *) pTransformArg; + + if( psInfo == NULL || !EQUAL(psInfo->szSignature,"GTI") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to destroy non-GTI transformer." ); + } + else + psInfo->pfnCleanup( pTransformArg ); +} + +/************************************************************************/ +/* GDALUseTransformer() */ +/************************************************************************/ + +int GDALUseTransformer( void *pTransformArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, + int *panSuccess ) +{ + GDALTransformerInfo *psInfo = (GDALTransformerInfo *) pTransformArg; + + if( psInfo == NULL || !EQUAL(psInfo->szSignature,"GTI") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to use non-GTI transformer." ); + return FALSE; + } + else + return psInfo->pfnTransform( pTransformArg, bDstToSrc, nPointCount, + x, y, z, panSuccess ); +} + +/************************************************************************/ +/* GDALCloneTransformer() */ +/************************************************************************/ + +/* TODO GDAL 2.0 : use a void* (*pfnClone) (void *) member */ +void *GDALCloneTransformer( void *pTransformArg ) +{ + VALIDATE_POINTER1( pTransformArg, "GDALCloneTransformer", NULL ); + + GDALTransformerInfo *psInfo = static_cast(pTransformArg); + + if( psInfo == NULL || !EQUAL(psInfo->szSignature,"GTI") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to clone non-GTI transformer." ); + return NULL; + } + + void* (*pfnClone)(void*) = NULL; + if( EQUAL(psInfo->pszClassName,"GDALTPSTransformer") ) + { + pfnClone = GDALCloneTPSTransformer; + } + /* TODO + else if( EQUAL(psInfo->pszClassName,"GDALGeoLocTransformer") ) + { + pfnClone = GDALCloneGeoLocTransformer; + } + else if( EQUAL(psInfo->pszClassName,"GDALRPCTransformer") ) + { + pfnClone = GDALCloneRPCTransformer; + } + */ + else if( EQUAL(psInfo->pszClassName,"GDALGenImgProjTransformer") ) + { + pfnClone = GDALCloneGenImgProjTransformer; + } + /* Useless : serialization/deserialization is fine */ + /* + else if( EQUAL(psInfo->pszClassName,"GDALReprojectionTransformer") ) + { + pfnClone = GDALCloneReprojectionTransformer; + } + */ + else if( EQUAL(psInfo->pszClassName,"GDALApproxTransformer") ) + { + pfnClone = GDALCloneApproxTransformer; + } + + if( pfnClone != NULL ) + { + void* pRet = pfnClone(pTransformArg); + if( pRet != NULL ) + return pRet; + } + + if ( psInfo->pfnSerialize == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No serialization function available for this transformer." ); + return NULL; + } + else + { + CPLXMLNode* pSerialized = psInfo->pfnSerialize( pTransformArg ); + if( pSerialized == NULL ) + return NULL; + GDALTransformerFunc pfnTransformer = NULL; + void *pClonedTransformArg = NULL; + if( GDALDeserializeTransformer( pSerialized, &pfnTransformer, &pClonedTransformArg ) != + CE_None ) + { + CPLDestroyXMLNode(pSerialized); + return NULL; + } + CPLDestroyXMLNode(pSerialized); + return pClonedTransformArg; + } +} diff --git a/ogr/gdalvirtualmem.cpp b/ogr/gdalvirtualmem.cpp new file mode 100644 index 0000000..d8aff4b --- /dev/null +++ b/ogr/gdalvirtualmem.cpp @@ -0,0 +1,1541 @@ +/********************************************************************** + * $Id: gdalvirtualmem.cpp 27110 2014-03-28 21:29:20Z rouault $ + * + * Name: gdalvirtualmem.cpp + * Project: GDAL + * Purpose: Dataset and rasterband exposed as a virtual memory mapping. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal.h" +#include "cpl_conv.h" +#include "cpl_virtualmem.h" + +/************************************************************************/ +/* GDALVirtualMem */ +/************************************************************************/ + +class GDALVirtualMem +{ + GDALDatasetH hDS; + GDALRasterBandH hBand; + int nXOff; + int nYOff; + /*int nXSize; + int nYSize;*/ + int nBufXSize; + int nBufYSize; + GDALDataType eBufType; + int nBandCount; + int* panBandMap; + int nPixelSpace; + GIntBig nLineSpace; + GIntBig nBandSpace; + + int bIsCompact; + int bIsBandSequential; + + int IsCompact() const { return bIsCompact; } + int IsBandSequential() const { return bIsBandSequential; } + + void GetXYBand( size_t nOffset, int& x, int& y, int& band ) const; + size_t GetOffset(int x, int y, int band) const; + int GotoNextPixel(int& x, int& y, int& band) const; + + void DoIOBandSequential( GDALRWFlag eRWFlag, size_t nOffset, + void* pPage, size_t nBytes ) const; + void DoIOPixelInterleaved( GDALRWFlag eRWFlag, size_t nOffset, + void* pPage, size_t nBytes ) const; + +public: + GDALVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace ); + ~GDALVirtualMem(); + + static void FillCacheBandSequential(CPLVirtualMem* ctxt, size_t nOffset, + void* pPageToFill, + size_t nToFill, void* pUserData); + static void SaveFromCacheBandSequential(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData); + + static void FillCachePixelInterleaved(CPLVirtualMem* ctxt, size_t nOffset, + void* pPageToFill, + size_t nToFill, void* pUserData); + static void SaveFromCachePixelInterleaved(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData); + + static void Destroy(void* pUserData); +}; + +/************************************************************************/ +/* GDALVirtualMem() */ +/************************************************************************/ + +GDALVirtualMem::GDALVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace ) : + hDS(hDS), hBand(hBand), nXOff(nXOff), nYOff(nYOff), /*nXSize(nXSize), nYSize(nYSize),*/ + nBufXSize(nBufXSize), nBufYSize(nBufYSize), eBufType(eBufType), + nBandCount(nBandCount), nPixelSpace(nPixelSpace), nLineSpace(nLineSpace), + nBandSpace(nBandSpace) +{ + if( hDS != NULL ) + { + if( panBandMapIn ) + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + memcpy(panBandMap, panBandMapIn, nBandCount * sizeof(int)); + } + else + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + for(int i=0;i= nBufYSize * nLineSpace ); +} + +/************************************************************************/ +/* ~GDALVirtualMem() */ +/************************************************************************/ + +GDALVirtualMem::~GDALVirtualMem() +{ + CPLFree(panBandMap); +} + +/************************************************************************/ +/* GetXYBand() */ +/************************************************************************/ + +void GDALVirtualMem::GetXYBand( size_t nOffset, int& x, int& y, int& band ) const +{ + if( IsBandSequential() ) + { + if( nBandCount == 1 ) + band = 0; + else + band = nOffset / nBandSpace; + y = (nOffset - band * nBandSpace) / nLineSpace; + x = (nOffset - band * nBandSpace - y * nLineSpace) / nPixelSpace; + } + else + { + y = nOffset / nLineSpace; + x = (nOffset - y * nLineSpace) / nPixelSpace; + if( nBandCount == 1 ) + band = 0; + else + band = (nOffset - y * nLineSpace - x * nPixelSpace) / nBandSpace; + } +} + +/************************************************************************/ +/* GotoNextPixel() */ +/************************************************************************/ + +int GDALVirtualMem::GotoNextPixel(int& x, int& y, int& band) const +{ + if( IsBandSequential() ) + { + x++; + if( x == nBufXSize ) + { + x = 0; + y ++; + } + if( y == nBufYSize ) + { + y = 0; + band ++; + if (band == nBandCount) + return FALSE; + } + } + else + { + band ++; + if( band == nBandCount ) + { + band = 0; + x ++; + } + if( x == nBufXSize ) + { + x = 0; + y ++; + if(y == nBufYSize) + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +size_t GDALVirtualMem::GetOffset(int x, int y, int band) const +{ + return x * nPixelSpace + y * nLineSpace + band * nBandSpace; +} + +/************************************************************************/ +/* DoIOPixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::DoIOPixelInterleaved( GDALRWFlag eRWFlag, + const size_t nOffset, void* pPage, size_t nBytes ) const +{ + int x, y, band; + + GetXYBand(nOffset, x, y, band); + /*fprintf(stderr, "eRWFlag=%d, nOffset=%d, x=%d, y=%d, band=%d\n", + eRWFlag, (int)nOffset, x, y, band);*/ + + if( eRWFlag == GF_Read && !IsCompact() ) + memset(pPage, 0, nBytes); + + if( band >= nBandCount ) + { + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + else if( x >= nBufXSize ) + { + x = nBufXSize - 1; + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + + size_t nOffsetRecompute = GetOffset(x, y, band); + CPLAssert(nOffsetRecompute >= nOffset); + size_t nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + + // If we don't start at the first band for that given pixel, load/store + // the remaining bands + if( band > 0 ) + { + size_t nEndOffsetEndOfPixel = GetOffset(x, y, nBandCount); + int bandEnd; + // Check that we have enough space to load/store until last band + // Should be always OK unless the number of bands is really huge + if( nEndOffsetEndOfPixel - nOffset > nBytes ) + { + // Not enough space: find last possible band + int xEnd, yEnd; + GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd); + CPLAssert(x == xEnd); + CPLAssert(y == yEnd); + } + else + bandEnd = nBandCount; + + // Finish reading/writing the remaining bands for that pixel + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, 1, 1, + (char*)pPage + nOffsetShift, + 1, 1, eBufType, + bandEnd - band, panBandMap + band, + nPixelSpace, nLineSpace, nBandSpace ); + + if( bandEnd < nBandCount ) + return; + + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + } + + // Is there enough place to store/load up to the end of current line ? + size_t nEndOffsetEndOfLine = GetOffset(nBufXSize-1, y, nBandCount); + if( nEndOffsetEndOfLine - nOffset > nBytes ) + { + // No : read/write as many pixels on this line as possible + int xEnd, yEnd, bandEnd; + GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd); + CPLAssert(y == yEnd); + + if( x < xEnd ) + { + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, xEnd - x, 1, + (char*) pPage + nOffsetShift, + xEnd - x, 1, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + } + + // Are there partial bands to read/write for the last pixel ? + if( bandEnd > 0 ) + { + x = xEnd; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + + if( bandEnd >= nBandCount ) + bandEnd = nBandCount; + + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, 1, 1, + (char*) pPage + nOffsetShift, + 1, 1, eBufType, + bandEnd, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + } + + return; + } + + // Yes, enough place to read/write until end of line + if( x > 0 || nBytes - nOffsetShift < (size_t)nLineSpace ) + { + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, nBufXSize - x, 1, + (char*)pPage + nOffsetShift, + nBufXSize - x, 1, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + + // Go to beginning of next line + x = nBufXSize - 1; + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + } + + // How many whole lines can we store/load ? + int nLineCount = (nBytes - nOffsetShift) / nLineSpace; + if( y + nLineCount > nBufYSize ) + nLineCount = nBufYSize - y; + if( nLineCount > 0 ) + { + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + 0, nYOff + y, nBufXSize, nLineCount, + (GByte*) pPage + nOffsetShift, + nBufXSize, nLineCount, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace ); + + y += nLineCount; + if( y == nBufYSize ) + return; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + } + + if( nOffsetShift < nBytes ) + { + DoIOPixelInterleaved( eRWFlag, nOffsetRecompute, + (char*) pPage + nOffsetShift, nBytes - nOffsetShift ); + } +} + +/************************************************************************/ +/* DoIOPixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::DoIOBandSequential( GDALRWFlag eRWFlag, + const size_t nOffset, void* pPage, size_t nBytes ) const +{ + int x, y, band; + + GetXYBand(nOffset, x, y, band); + /*fprintf(stderr, "eRWFlag=%d, nOffset=%d, x=%d, y=%d, band=%d\n", + eRWFlag, (int)nOffset, x, y, band);*/ + + if( eRWFlag == GF_Read && !IsCompact() ) + memset(pPage, 0, nBytes); + + if( x >= nBufXSize ) + { + x = nBufXSize - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + else if( y >= nBufYSize ) + { + x = nBufXSize - 1; + y = nBufYSize - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + + size_t nOffsetRecompute = GetOffset(x, y, band); + CPLAssert(nOffsetRecompute >= nOffset); + size_t nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + + // Is there enough place to store/load up to the end of current line ? + size_t nEndOffsetEndOfLine = GetOffset(nBufXSize, y, band); + if( nEndOffsetEndOfLine - nOffset > nBytes ) + { + // No : read/write as many pixels on this line as possible + int xEnd, yEnd, bandEnd; + GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd); + CPLAssert(y == yEnd); + CPLAssert(band == bandEnd); + GDALRasterIO( (hBand) ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag, + nXOff + x, nYOff + y, xEnd - x, 1, + (char*)pPage + nOffsetShift, + xEnd - x, 1, eBufType, + nPixelSpace, nLineSpace ); + + return; + } + + // Yes, enough place to read/write until end of line + if( x > 0 || nBytes - nOffsetShift < (size_t)nLineSpace ) + { + GDALRasterIO( (hBand) ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag, + nXOff + x, nYOff + y, nBufXSize - x, 1, + (char*)pPage + nOffsetShift, + nBufXSize - x, 1, eBufType, + nPixelSpace, nLineSpace ); + + // Go to beginning of next line + x = nBufXSize - 1; + if( !GotoNextPixel(x, y, band) ) + return; + nOffsetRecompute = GetOffset(x, y, band); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + } + + // How many whole lines can we store/load ? + int nLineCount = (nBytes - nOffsetShift) / nLineSpace; + if( y + nLineCount > nBufYSize ) + nLineCount = nBufYSize - y; + if( nLineCount > 0 ) + { + GDALRasterIO( (hBand) ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag, + nXOff + 0, nYOff + y, nBufXSize, nLineCount, + (GByte*) pPage + nOffsetShift, + nBufXSize, nLineCount, eBufType, + nPixelSpace, nLineSpace ); + + y += nLineCount; + if( y == nBufYSize ) + { + y = 0; + band ++; + if( band == nBandCount ) + return; + } + nOffsetRecompute = GetOffset(x, y, band); + nOffsetShift = nOffsetRecompute - nOffset; + } + + if( nOffsetShift < nBytes ) + { + DoIOBandSequential( eRWFlag, nOffsetRecompute, + (char*) pPage + nOffsetShift, nBytes - nOffsetShift ); + } +} + +/************************************************************************/ +/* FillCacheBandSequential() */ +/************************************************************************/ + +void GDALVirtualMem::FillCacheBandSequential(CPLVirtualMem* ctxt, + size_t nOffset, + void* pPageToFill, + size_t nToFill, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOBandSequential(GF_Read, nOffset, pPageToFill, nToFill); +} + +/************************************************************************/ +/* SaveFromCacheBandSequential() */ +/************************************************************************/ + +void GDALVirtualMem::SaveFromCacheBandSequential(CPLVirtualMem* ctxt, + size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOBandSequential(GF_Write, nOffset, (void*)pPageToBeEvicted, nToEvicted); +} + +/************************************************************************/ +/* FillCachePixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::FillCachePixelInterleaved(CPLVirtualMem* ctxt, + size_t nOffset, + void* pPageToFill, + size_t nToFill, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOPixelInterleaved(GF_Read, nOffset, pPageToFill, nToFill); +} + +/************************************************************************/ +/* SaveFromCachePixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::SaveFromCachePixelInterleaved(CPLVirtualMem* ctxt, + size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOPixelInterleaved(GF_Write, nOffset, (void*)pPageToBeEvicted, nToEvicted); +} + +/************************************************************************/ +/* Destroy() */ +/************************************************************************/ + +void GDALVirtualMem::Destroy(void* pUserData) +{ + GDALVirtualMem* psParams = (GDALVirtualMem*) pUserData; + delete psParams; +} + +/************************************************************************/ +/* GDALCheckBandParameters() */ +/************************************************************************/ + +static int GDALCheckBandParameters( GDALDatasetH hDS, + int nBandCount, int* panBandMap ) +{ + if( nBandCount == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "nBandCount == 0"); + return FALSE; + } + + if( panBandMap != NULL ) + { + for(int i=0;i GDALGetRasterCount(hDS) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "panBandMap[%d]=%d", + i, panBandMap[i]); + return FALSE; + } + } + } + else if( nBandCount > GDALGetRasterCount(hDS) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "nBandCount > GDALGetRasterCount(hDS)"); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* GDALGetVirtualMem() */ +/************************************************************************/ + +static CPLVirtualMem* GDALGetVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ) +{ + CPLVirtualMem* view; + GDALVirtualMem* psParams; + GUIntBig nReqMem; + (void) papszOptions; + + if( nXSize != nBufXSize || nYSize != nBufYSize ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "nXSize != nBufXSize || nYSize != nBufYSize"); + return NULL; + } + + int nRasterXSize = (hDS) ? GDALGetRasterXSize(hDS) : GDALGetRasterBandXSize(hBand); + int nRasterYSize = (hDS) ? GDALGetRasterYSize(hDS) : GDALGetRasterBandYSize(hBand); + + if( nXOff < 0 || nYOff < 0 || + nXSize == 0 || nYSize == 0 || + nBufXSize < 0 || nBufYSize < 0 || + nXOff + nXSize > nRasterXSize || + nYOff + nYSize > nRasterYSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid window request"); + return NULL; + } + + if( nPixelSpace < 0 || nLineSpace < 0 || nBandSpace < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "nPixelSpace < 0 || nLineSpace < 0 || nBandSpace < 0"); + return NULL; + } + + if( hDS != NULL && !GDALCheckBandParameters(hDS, nBandCount, panBandMap ) ) + return NULL; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + if( nPixelSpace == 0 ) + nPixelSpace = nDataTypeSize; + if( nLineSpace == 0 ) + nLineSpace = (GIntBig)nBufXSize * nPixelSpace; + if( nBandSpace == 0 ) + nBandSpace = (GIntBig)nBufYSize * nLineSpace; + + // OFFSET = offset(x,y,band) = x * nPixelSpace + y * nLineSpace + band * nBandSpace + // where 0 <= x < nBufXSize and 0 <= y < nBufYSize and 0 <= band < nBandCount + // if nPixelSpace, nLineSpace and nBandSpace can have arbitrary values, there's + // no way of finding a unique(x,y,band) solution. We need to restrict the + // space of possibilities strongly. + // if nBandSpace >= nBufYSize * nLineSpace and nLineSpace >= nBufXSize * nPixelSpace, INTERLEAVE = BAND + // band = OFFSET / nBandSpace + // y = (OFFSET - band * nBandSpace) / nLineSpace + // x = (OFFSET - band * nBandSpace - y * nLineSpace) / nPixelSpace + // else if nPixelSpace >= nBandCount * nBandSpace and nLineSpace >= nBufXSize * nPixelSpace, INTERLEAVE = PIXEL + // y = OFFSET / nLineSpace + // x = (OFFSET - y * nLineSpace) / nPixelSpace + // band = (OFFSET - y * nLineSpace - x * nPixelSpace) / nBandSpace + + if( nDataTypeSize == 0 || /* to please Coverity. not needed */ + nLineSpace < (GIntBig)nBufXSize * nPixelSpace || + (nBandCount > 1 && + (nBandSpace == nPixelSpace || + (nBandSpace < nPixelSpace && + (nBandSpace < nDataTypeSize || nPixelSpace < nBandCount * nBandSpace)) || + (nBandSpace > nPixelSpace && + (nPixelSpace < nDataTypeSize || nBandSpace < nBufYSize * nLineSpace)))) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only pixel interleaving or band interleaving are supported"); + return NULL; + } + + /* Avoid odd spacings that would complicate I/O operations */ + /* Ensuring they are multiple of nDataTypeSize should be fine, because */ + /* the page size is a power of 2 that is also a multiple of nDataTypeSize */ + if( (nPixelSpace % nDataTypeSize) != 0 || + (nLineSpace % nDataTypeSize) != 0 || + (nBandSpace % nDataTypeSize) != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported spacing"); + return NULL; + } + + int bIsBandSequential = ( nBandSpace >= nBufYSize * nLineSpace ); + if( bIsBandSequential ) + nReqMem = nBandCount * nBandSpace; + else + nReqMem = nBufYSize * nLineSpace; + if( nReqMem != (GUIntBig)(size_t)nReqMem ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot reserve " CPL_FRMT_GUIB " bytes", nReqMem); + return NULL; + } + + psParams = new GDALVirtualMem(hDS, hBand, nXOff, nYOff, + nXSize, nYSize, + nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, + nLineSpace, + nBandSpace); + + view = CPLVirtualMemNew((size_t)nReqMem, + nCacheSize, + nPageSizeHint, + bSingleThreadUsage, + (eRWFlag == GF_Read) ? VIRTUALMEM_READONLY_ENFORCED : VIRTUALMEM_READWRITE, + (bIsBandSequential) ? GDALVirtualMem::FillCacheBandSequential : + GDALVirtualMem::FillCachePixelInterleaved, + (bIsBandSequential) ? GDALVirtualMem::SaveFromCacheBandSequential : + GDALVirtualMem::SaveFromCachePixelInterleaved, + GDALVirtualMem::Destroy, + psParams); + + if( view == NULL ) + { + delete psParams; + } + + return view; +} + +/************************************************************************/ +/* GDALDatasetGetVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL dataset object. + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of one + * or more GDALRasterBands from this dataset. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the dataset object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) for band b can be accessed with + * ((base_type*)p)[x + y * nBufXSize + (b-1)*nBufXSize*nBufYSize]. + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * Image decimation / replication is currently not supported, i.e. if the + * size of the region being accessed (nXSize x nYSize) is different from the + * buffer size (nBufXSize x nBufYSize). + * + * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or + * writing from various organization of buffers. Arbitrary values for the spacing + * parameters are not supported. Those values must be multiple of the size of the + * buffer data type, and must be either band sequential organization (typically + * nPixelSpace = GDALGetDataTypeSize(eBufType) / 8, nLineSpace = nPixelSpace * nBufXSize, + * nBandSpace = nLineSpace * nBufYSize), or pixel-interleaved organization + * (typically nPixelSpace = nBandSpace * nBandCount, nLineSpace = nPixelSpace * nBufXSize, + * nBandSpace = GDALGetDataTypeSize(eBufType) / 8) + * + * @param hDS Dataset object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * the buffer to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * the buffer to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nBandSpace the byte offset from the start of one bands data to the + * start of the next. If defaulted (0) the value will be + * nLineSpace * nBufYSize implying band sequential organization + * of the data buffer. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param nPageSizeHint hint for the page size. Must be a multiple of the + * system page size, returned by CPLGetPageSize(). + * Minimum value is generally 4096. Might be set to 0 to + * let the function determine a default page size. + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALDatasetGetVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetVirtualMem( hDS, NULL, eRWFlag, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + nCacheSize, nPageSizeHint, bSingleThreadUsage, + papszOptions ); +} + +/************************************************************************/ +/* GDALRasterBandGetVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL raster band object. + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of a + * GDALRasterBand. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the raster band object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) can be accessed with + * ((base_type*)p)[x + y * nBufXSize]. + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * Image decimation / replication is currently not supported, i.e. if the + * size of the region being accessed (nXSize x nYSize) is different from the + * buffer size (nBufXSize x nBufYSize). + * + * The nPixelSpace and nLineSpace parameters allow reading into or + * writing from various organization of buffers. Arbitrary values for the spacing + * parameters are not supported. Those values must be multiple of the size of the + * buffer data type and must be such that nLineSpace >= nPixelSpace * nBufXSize. + * + * @param hBand Rasterband object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * the buffer to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * the buffer to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param nPageSizeHint hint for the page size. Must be a multiple of the + * system page size, returned by CPLGetPageSize(). + * Minimum value is generally 4096. Might be set to 0 to + * let the function determine a default page size. + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALRasterBandGetVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + GIntBig nLineSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetVirtualMem( NULL, hBand, eRWFlag, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eBufType, + 1, NULL, + nPixelSpace, nLineSpace, 0, + nCacheSize, nPageSizeHint, bSingleThreadUsage, + papszOptions ); +} + +/************************************************************************/ +/* GDALTiledVirtualMem */ +/************************************************************************/ + +class GDALTiledVirtualMem +{ + GDALDatasetH hDS; + GDALRasterBandH hBand; + int nXOff; + int nYOff; + int nXSize; + int nYSize; + int nTileXSize; + int nTileYSize; + GDALDataType eBufType; + int nBandCount; + int* panBandMap; + GDALTileOrganization eTileOrganization; + + void DoIO( GDALRWFlag eRWFlag, size_t nOffset, + void* pPage, size_t nBytes ) const; + +public: + GDALTiledVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + GDALTileOrganization eTileOrganization ); + ~GDALTiledVirtualMem(); + + static void FillCache(CPLVirtualMem* ctxt, size_t nOffset, + void* pPageToFill, + size_t nPageSize, void* pUserData); + static void SaveFromCache(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData); + + static void Destroy(void* pUserData); +}; + +/************************************************************************/ +/* GDALTiledVirtualMem() */ +/************************************************************************/ + +GDALTiledVirtualMem::GDALTiledVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + GDALTileOrganization eTileOrganization ): + hDS(hDS), hBand(hBand), nXOff(nXOff), nYOff(nYOff), nXSize(nXSize), nYSize(nYSize), + nTileXSize(nTileXSize), nTileYSize(nTileYSize), eBufType(eBufType), + nBandCount(nBandCount), eTileOrganization(eTileOrganization) +{ + if( hDS != NULL ) + { + if( panBandMapIn ) + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + memcpy(panBandMap, panBandMapIn, nBandCount * sizeof(int)); + } + else + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + for(int i=0;iDoIO(GF_Read, nOffset, pPageToFill, nToFill); +} + +/************************************************************************/ +/* SaveFromCache() */ +/************************************************************************/ + +void GDALTiledVirtualMem::SaveFromCache(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData) +{ + const GDALTiledVirtualMem* psParms = (const GDALTiledVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIO(GF_Write, nOffset, (void*)pPageToBeEvicted, nToEvicted); +} + +/************************************************************************/ +/* Destroy() */ +/************************************************************************/ + +void GDALTiledVirtualMem::Destroy(void* pUserData) +{ + GDALTiledVirtualMem* psParams = (GDALTiledVirtualMem*) pUserData; + delete psParams; +} + +/************************************************************************/ +/* GDALGetTiledVirtualMem() */ +/************************************************************************/ + +static CPLVirtualMem* GDALGetTiledVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + GDALTileOrganization eTileOrganization, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ) +{ + CPLVirtualMem* view; + GDALTiledVirtualMem* psParams; + (void) papszOptions; + + int nRasterXSize = (hDS) ? GDALGetRasterXSize(hDS) : GDALGetRasterBandXSize(hBand); + int nRasterYSize = (hDS) ? GDALGetRasterYSize(hDS) : GDALGetRasterBandYSize(hBand); + + if( nXOff < 0 || nYOff < 0 || + nTileXSize <= 0 || nTileYSize <= 0 || + nXOff + nXSize > nRasterXSize || + nYOff + nYSize > nRasterYSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid window request"); + return NULL; + } + + if( hDS != NULL && !GDALCheckBandParameters(hDS, nBandCount, panBandMap ) ) + return NULL; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + int nTilesPerRow = (nXSize + nTileXSize - 1) / nTileXSize; + int nTilesPerCol = (nYSize + nTileYSize - 1) / nTileYSize; + GUIntBig nReqMem = (GUIntBig)nTilesPerRow * nTilesPerCol * + nTileXSize * nTileYSize * nBandCount * nDataTypeSize; + if( nReqMem != (GUIntBig)(size_t)nReqMem ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot reserve " CPL_FRMT_GUIB " bytes", nReqMem); + return NULL; + } + + size_t nPageSizeHint = nTileXSize * nTileYSize * nDataTypeSize; + if( eTileOrganization != GTO_BSQ ) + nPageSizeHint *= nBandCount; + if( (nPageSizeHint % CPLGetPageSize()) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Tile dimensions incompatible with page size"); + return NULL; + } + + psParams = new GDALTiledVirtualMem(hDS, hBand, nXOff, nYOff, + nXSize, nYSize, + nTileXSize, nTileYSize, + eBufType, + nBandCount, panBandMap, + eTileOrganization); + + view = CPLVirtualMemNew((size_t)nReqMem, + nCacheSize, + nPageSizeHint, + bSingleThreadUsage, + (eRWFlag == GF_Read) ? VIRTUALMEM_READONLY_ENFORCED : VIRTUALMEM_READWRITE, + GDALTiledVirtualMem::FillCache, + GDALTiledVirtualMem::SaveFromCache, + GDALTiledVirtualMem::Destroy, + psParams); + + if( view == NULL ) + { + delete psParams; + } + else if( CPLVirtualMemGetPageSize(view) != nPageSizeHint ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did not get expected page size : %d vs %d", + (int)CPLVirtualMemGetPageSize(view), (int)nPageSizeHint); + CPLVirtualMemFree(view); + return NULL; + } + + return view; +} + +/************************************************************************/ +/* GDALDatasetGetTiledVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL dataset object, with tiling + * organization + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of one + * or more GDALRasterBands from this dataset. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * Contrary to GDALDatasetGetVirtualMem(), pixels will be organized by tiles + * instead of scanlines. Different ways of organizing pixel within/accross tiles + * can be selected with the eTileOrganization parameter. + * + * If nXSize is not a multiple of nTileXSize or nYSize is not a multiple of + * nTileYSize, partial tiles will exists at the right and/or bottom of the region + * of interest. Those partial tiles will also have nTileXSize * nTileYSize dimension, + * with padding pixels. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the dataset object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) for band b can be accessed with : + * - for eTileOrganization = GTO_TIP, ((base_type*)p)[tile_number(x,y)*nBandCount*tile_size + offset_in_tile(x,y)*nBandCount + (b-1)]. + * - for eTileOrganization = GTO_BIT, ((base_type*)p)[(tile_number(x,y)*nBandCount + (b-1)) * tile_size + offset_in_tile(x,y)]. + * - for eTileOrganization = GTO_BSQ, ((base_type*)p)[(tile_number(x,y) + (b-1)*nTilesCount) * tile_size + offset_in_tile(x,y)]. + * + * where nTilesPerRow = ceil(nXSize / nTileXSize) + * nTilesPerCol = ceil(nYSize / nTileYSize) + * nTilesCount = nTilesPerRow * nTilesPerCol + * tile_number(x,y) = (y / nTileYSize) * nTilesPerRow + (x / nTileXSize) + * offset_in_tile(x,y) = (y % nTileYSize) * nTileXSize + (x % nTileXSize) + * tile_size = nTileXSize * nTileYSize + * + * Note that for a single band request, all tile organizations are equivalent. + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * @param hDS Dataset object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nTileXSize the width of the tiles. + * + * @param nTileYSize the height of the tiles. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param eTileOrganization tile organization. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALDatasetGetTiledVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + GDALTileOrganization eTileOrganization, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetTiledVirtualMem( hDS, NULL, eRWFlag, nXOff, nYOff, + nXSize, nYSize, nTileXSize, nTileYSize, + eBufType, nBandCount, panBandMap, + eTileOrganization, + nCacheSize, bSingleThreadUsage, papszOptions ); +} + +/************************************************************************/ +/* GDALRasterBandGetTiledVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL rasterband object, with tiling + * organization + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of one + * GDALRasterBand. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * Contrary to GDALDatasetGetVirtualMem(), pixels will be organized by tiles + * instead of scanlines. + * + * If nXSize is not a multiple of nTileXSize or nYSize is not a multiple of + * nTileYSize, partial tiles will exists at the right and/or bottom of the region + * of interest. Those partial tiles will also have nTileXSize * nTileYSize dimension, + * with padding pixels. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the raster band object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) can be accessed with : + * ((base_type*)p)[tile_number(x,y)*tile_size + offset_in_tile(x,y)]. + * + * where nTilesPerRow = ceil(nXSize / nTileXSize) + * nTilesCount = nTilesPerRow * nTilesPerCol + * tile_number(x,y) = (y / nTileYSize) * nTilesPerRow + (x / nTileXSize) + * offset_in_tile(x,y) = (y % nTileYSize) * nTileXSize + (x % nTileXSize) + * tile_size = nTileXSize * nTileYSize + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * @param hBand Rasterband object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nTileXSize the width of the tiles. + * + * @param nTileYSize the height of the tiles. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALRasterBandGetTiledVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetTiledVirtualMem( NULL, hBand, eRWFlag, nXOff, nYOff, + nXSize, nYSize, nTileXSize, nTileYSize, + eBufType, 1, NULL, + GTO_BSQ, + nCacheSize, bSingleThreadUsage, papszOptions ); +} diff --git a/ogr/gml2ogrgeometry.cpp b/ogr/gml2ogrgeometry.cpp index 211e040..9edabcb 100644 --- a/ogr/gml2ogrgeometry.cpp +++ b/ogr/gml2ogrgeometry.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: gml2ogrgeometry.cpp 18085 2009-11-23 19:02:18Z rouault $ + * $Id: gml2ogrgeometry.cpp 27135 2014-04-06 09:58:27Z rouault $ * * Project: GML Reader * Purpose: Code to translate between GML and OGR geometry forms. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -46,6 +47,46 @@ #include #include "ogr_p.h" +#ifndef PI +#define PI 3.14159265358979323846 +#endif + + +/************************************************************************/ +/* GMLGetCoordTokenPos() */ +/************************************************************************/ + +static const char* GMLGetCoordTokenPos(const char* pszStr, + const char** ppszNextToken) +{ + char ch; + while(TRUE) + { + ch = *pszStr; + if (ch == '\0') + { + *ppszNextToken = NULL; + return NULL; + } + else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',')) + break; + pszStr ++; + } + + const char* pszToken = pszStr; + while((ch = *pszStr) != '\0') + { + if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',') + { + *ppszNextToken = pszStr; + return pszToken; + } + pszStr ++; + } + *ppszNextToken = NULL; + return pszToken; +} + /************************************************************************/ /* BareGMLElement() */ /* */ @@ -115,6 +156,53 @@ static const char *GetElementText( const CPLXMLNode *psElement ) return NULL; } +/************************************************************************/ +/* GetChildElement() */ +/************************************************************************/ + +static const CPLXMLNode *GetChildElement( const CPLXMLNode *psElement ) + +{ + if( psElement == NULL ) + return NULL; + + const CPLXMLNode *psChild = psElement->psChild; + + while( psChild != NULL ) + { + if( psChild->eType == CXT_Element ) + return psChild; + + psChild = psChild->psNext; + } + + return NULL; +} + +/************************************************************************/ +/* GetElementOrientation() */ +/* Returns true for positive orientation. */ +/************************************************************************/ + +int GetElementOrientation( const CPLXMLNode *psElement ) +{ + if( psElement == NULL ) + return TRUE; + + const CPLXMLNode *psChild = psElement->psChild; + + while( psChild != NULL ) + { + if( psChild->eType == CXT_Attribute && + EQUAL(psChild->pszValue,"orientation") ) + return EQUAL(psChild->psChild->pszValue,"+"); + + psChild = psChild->psNext; + } + + return TRUE; +} + /************************************************************************/ /* AddPoint() */ /* */ @@ -125,12 +213,12 @@ static int AddPoint( OGRGeometry *poGeometry, double dfX, double dfY, double dfZ, int nDimension ) { - if( poGeometry->getGeometryType() == wkbPoint - || poGeometry->getGeometryType() == wkbPoint25D ) + OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType()); + if( eType == wkbPoint ) { OGRPoint *poPoint = (OGRPoint *) poGeometry; - if( poPoint->getX() != 0.0 || poPoint->getY() != 0.0 ) + if( !poPoint->IsEmpty() ) { CPLError( CE_Failure, CPLE_AppDefined, "More than one coordinate for element."); @@ -145,8 +233,7 @@ static int AddPoint( OGRGeometry *poGeometry, return TRUE; } - else if( poGeometry->getGeometryType() == wkbLineString - || poGeometry->getGeometryType() == wkbLineString25D ) + else if( eType == wkbLineString ) { if( nDimension == 3 ) ((OGRLineString *) poGeometry)->addPoint( dfX, dfY, dfZ ); @@ -175,16 +262,52 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo /* -------------------------------------------------------------------- */ /* Handle case. */ +/* Note that we don't do a strict validation, so we accept and */ +/* sometimes generate output whereas we should just reject it. */ /* -------------------------------------------------------------------- */ if( psCoordinates != NULL ) { const char *pszCoordString = GetElementText( psCoordinates ); + const char *pszDecimal = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "decimal", NULL); + char chDecimal = '.'; + if( pszDecimal != NULL ) + { + if( strlen(pszDecimal) != 1 || (pszDecimal[0] >= '0' && pszDecimal[0] <= '9') ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for decimal attribute"); + return FALSE; + } + chDecimal = pszDecimal[0]; + } + + const char *pszCS = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "cs", NULL); + char chCS = ','; + if( pszCS != NULL ) + { + if( strlen(pszCS) != 1 || (pszCS[0] >= '0' && pszCS[0] <= '9') ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for cs attribute"); + return FALSE; + } + chCS = pszCS[0]; + } + const char *pszTS = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "ts", NULL); + char chTS = ' '; + if( pszTS != NULL ) + { + if( strlen(pszTS) != 1 || (pszTS[0] >= '0' && pszTS[0] <= '9') ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for tes attribute"); + return FALSE; + } + chTS = pszTS[0]; + } + if( pszCoordString == NULL ) { - CPLError( CE_Failure, CPLE_AppDefined, - " element missing value." ); - return FALSE; + poGeometry->empty(); + return TRUE; } while( *pszCoordString != '\0' ) @@ -193,37 +316,63 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo int nDimension = 2; // parse out 2 or 3 tuple. - dfX = OGRFastAtof( pszCoordString ); + if( chDecimal == '.' ) + dfX = OGRFastAtof( pszCoordString ); + else + dfX = CPLAtofDelim( pszCoordString, chDecimal) ; while( *pszCoordString != '\0' - && *pszCoordString != ',' + && *pszCoordString != chCS && !isspace((unsigned char)*pszCoordString) ) pszCoordString++; - if( *pszCoordString == '\0' || isspace((unsigned char)*pszCoordString) ) + if( *pszCoordString == '\0' ) { - CPLError( CE_Failure, CPLE_AppDefined, - "Corrupt value." ); + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt value." ); return FALSE; } + else if( chCS == ',' && pszCS == NULL && isspace((unsigned char)*pszCoordString) ) + { + /* In theory, the coordinates inside a coordinate tuple should be */ + /* separated by a comma. However it has been found in the wild */ + /* that the coordinates are in rare cases separated by a space, and the tuples by a comma */ + /* See https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample */ + /* or http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW */ + chCS = ' '; + chTS = ','; + } pszCoordString++; - dfY = OGRFastAtof( pszCoordString ); + if( chDecimal == '.' ) + dfY = OGRFastAtof( pszCoordString ); + else + dfY = CPLAtofDelim( pszCoordString, chDecimal) ; while( *pszCoordString != '\0' - && *pszCoordString != ',' + && *pszCoordString != chCS + && *pszCoordString != chTS && !isspace((unsigned char)*pszCoordString) ) pszCoordString++; - if( *pszCoordString == ',' ) + if( *pszCoordString == chCS ) { pszCoordString++; - dfZ = OGRFastAtof( pszCoordString ); + if( chDecimal == '.' ) + dfZ = OGRFastAtof( pszCoordString ); + else + dfZ = CPLAtofDelim( pszCoordString, chDecimal) ; nDimension = 3; while( *pszCoordString != '\0' - && *pszCoordString != ',' + && *pszCoordString != chCS + && *pszCoordString != chTS && !isspace((unsigned char)*pszCoordString) ) pszCoordString++; } + if( *pszCoordString == chTS ) + { + pszCoordString++; + } + while( isspace((unsigned char)*pszCoordString) ) pszCoordString++; @@ -245,50 +394,82 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo const CPLXMLNode *psPos; int bHasFoundPosElement = FALSE; - for( psPos = psGeomNode->psChild; + for( psPos = psGeomNode->psChild; psPos != NULL; psPos = psPos->psNext ) { - if( psPos->eType != CXT_Element - || !EQUAL(BareGMLElement(psPos->pszValue),"pos") ) + if( psPos->eType != CXT_Element ) continue; - - char **papszTokens = CSLTokenizeStringComplex( - GetElementText( psPos ), " ,", FALSE, FALSE ); - int bSuccess = FALSE; - if( CSLCount( papszTokens ) > 2 ) + const char* pszSubElement = BareGMLElement(psPos->pszValue); + + if( EQUAL(pszSubElement, "pointProperty") ) { - bSuccess = AddPoint( poGeometry, - OGRFastAtof(papszTokens[0]), - OGRFastAtof(papszTokens[1]), - OGRFastAtof(papszTokens[2]), 3 ); + const CPLXMLNode *psPointPropertyIter; + for( psPointPropertyIter = psPos->psChild; + psPointPropertyIter != NULL; + psPointPropertyIter = psPointPropertyIter->psNext ) + { + if( psPointPropertyIter->eType != CXT_Element ) + continue; + + if (EQUAL(BareGMLElement(psPointPropertyIter->pszValue),"Point") ) + { + OGRPoint oPoint; + if( ParseGMLCoordinates( psPointPropertyIter, &oPoint ) ) + { + int bSuccess = AddPoint( poGeometry, oPoint.getX(), + oPoint.getY(), oPoint.getZ(), + oPoint.getCoordinateDimension() ); + if (bSuccess) + bHasFoundPosElement = TRUE; + else + return FALSE; + } + } + } + continue; } - else if( CSLCount( papszTokens ) > 1 ) + + if( !EQUAL(pszSubElement,"pos") ) + continue; + + const char* pszPos = GetElementText( psPos ); + if (pszPos == NULL) { - bSuccess = AddPoint( poGeometry, - OGRFastAtof(papszTokens[0]), - OGRFastAtof(papszTokens[1]), - 0.0, 2 ); + poGeometry->empty(); + return TRUE; } - else + + const char* pszCur = pszPos; + const char* pszX = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + const char* pszY = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + const char* pszZ = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + + if (pszY == NULL) { CPLError( CE_Failure, CPLE_AppDefined, "Did not get 2+ values in %s tuple.", - GetElementText( psPos ) ); + pszPos ? pszPos : "" ); + return FALSE; } - CSLDestroy( papszTokens ); - + double dfX = OGRFastAtof(pszX); + double dfY = OGRFastAtof(pszY); + double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0; + int bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, (pszZ != NULL) ? 3 : 2 ); + if (bSuccess) bHasFoundPosElement = TRUE; else return FALSE; } - + if (bHasFoundPosElement) return TRUE; - /* -------------------------------------------------------------------- */ /* Is this a "posList"? GML 3 construct (SF profile). */ @@ -297,30 +478,18 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo if( psPosList != NULL ) { - char **papszTokens; int bSuccess = FALSE; - int i=0, nCount=0; - const CPLXMLNode* psChild; int nDimension = 2; /* Try to detect the presence of an srsDimension attribute */ /* This attribute is only availabe for gml3.1.1 but not */ /* available for gml3.1 SF*/ - psChild = psPosList->psChild; - while (psChild != NULL) - { - if (psChild->eType == CXT_Attribute && - EQUAL(psChild->pszValue, "srsDimension")) - { - nDimension = atoi(psChild->psChild->pszValue); - break; - } - else if (psChild->eType != CXT_Attribute) - { - break; - } - psChild = psChild->psNext; - } + const char* pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psPosList, "srsDimension", NULL); + /* If not found at the posList level, try on the enclosing element */ + if (pszSRSDimension == NULL) + pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psGeomNode, "srsDimension", NULL); + if (pszSRSDimension != NULL) + nDimension = atoi(pszSRSDimension); if (nDimension != 2 && nDimension != 3) { @@ -329,31 +498,41 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo return FALSE; } - papszTokens = CSLTokenizeStringComplex( - GetElementText( psPosList ), " ,", FALSE, FALSE ); - - nCount = CSLCount( papszTokens ); - - if (nCount < nDimension || (nCount % nDimension) != 0) + const char* pszPosList = GetElementText( psPosList ); + if (pszPosList == NULL) { - CPLError( CE_Failure, CPLE_AppDefined, - "Did not get at least %d values or invalid number of \n" - "set of coordinates %s", - nDimension, GetElementText( psPosList ) ); + poGeometry->empty(); + return TRUE; } - else + + const char* pszCur = pszPosList; + while (TRUE) { - i=0; - while (i%s", + nDimension, pszPosList ? pszPosList : ""); + return FALSE; } + + double dfX = OGRFastAtof(pszX); + double dfY = OGRFastAtof(pszY); + double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0; + bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ); + + if (bSuccess == FALSE || pszCur == NULL) + break; } - CSLDestroy( papszTokens ); return bSuccess; } @@ -409,6 +588,76 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo return iCoord > 0.0; } +#ifdef HAVE_GEOS +/************************************************************************/ +/* GML2FaceExtRing() */ +/* */ +/* Identifies the "good" Polygon whithin the collection returned */ +/* by GEOSPolygonize() */ +/* short rationale: GEOSPolygonize() will possibily return a */ +/* collection of many Polygons; only one is the "good" one, */ +/* (including both exterior- and interior-rings) */ +/* any other simply represents a single "hole", and should be */ +/* consequently ignored at all. */ +/************************************************************************/ + +static OGRPolygon *GML2FaceExtRing( OGRGeometry *poGeom ) +{ + OGRPolygon *poPolygon = NULL; + int bError = FALSE; + OGRGeometryCollection *poColl = (OGRGeometryCollection *)poGeom; + int iCount = poColl->getNumGeometries(); + int iExterior = 0; + int iInterior = 0; + + for( int ig = 0; ig < iCount; ig++) + { + /* a collection of Polygons is expected to be found */ + OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig); + if( poChild == NULL) + { + bError = TRUE; + continue; + } + if( wkbFlatten( poChild->getGeometryType()) == wkbPolygon ) + { + OGRPolygon *poPg = (OGRPolygon *)poChild; + if( poPg->getNumInteriorRings() > 0 ) + iExterior++; + else + iInterior++; + } + else + bError = TRUE; + } + + if( bError == FALSE && iCount > 0 ) + { + if( iCount == 1 && iExterior == 0 && iInterior == 1) + { + /* there is a single Polygon within the collection */ + OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(0 ); + poPolygon = (OGRPolygon *)poPg->clone(); + } + else + { + if( iExterior == 1 && iInterior == iCount - 1 ) + { + /* searching the unique Polygon containing holes */ + for ( int ig = 0; ig < iCount; ig++) + { + OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(ig); + if( poPg->getNumInteriorRings() > 0 ) + poPolygon = (OGRPolygon *)poPg->clone(); + } + } + } + } + + return poPolygon; +} +#endif + /************************************************************************/ /* GML2OGRGeometry_XMLNode() */ /* */ @@ -417,15 +666,47 @@ static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeo /* collections. */ /************************************************************************/ -static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) +OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode, + int bGetSecondaryGeometryOption, + int nRecLevel, + int bIgnoreGSG, + int bOrientation, + int bFaceHoleNegative ) { + if( psNode != NULL && strcmp(psNode->pszValue, "?xml") == 0 ) + psNode = psNode->psNext; + while( psNode != NULL && psNode->eType == CXT_Comment ) + psNode = psNode->psNext; + if( psNode == NULL ) + return NULL; + const char *pszBaseGeometry = BareGMLElement( psNode->pszValue ); + if (bGetSecondaryGeometryOption < 0) + bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO")); + int bGetSecondaryGeometry = bIgnoreGSG ? FALSE : bGetSecondaryGeometryOption; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursion levels (%d) while parsing GML geometry.", + nRecLevel ); + return NULL; + } + + if( bGetSecondaryGeometry ) + if( !( EQUAL(pszBaseGeometry,"directedEdge") || + EQUAL(pszBaseGeometry,"TopoCurve") ) ) + return NULL; /* -------------------------------------------------------------------- */ -/* Polygon */ +/* Polygon / PolygonPatch / Triangle / Rectangle */ /* -------------------------------------------------------------------- */ - if( EQUAL(pszBaseGeometry,"Polygon") ) + if( EQUAL(pszBaseGeometry,"Polygon") || + EQUAL(pszBaseGeometry,"PolygonPatch") || + EQUAL(pszBaseGeometry,"Triangle") || + EQUAL(pszBaseGeometry,"Rectangle")) { const CPLXMLNode *psChild; OGRPolygon *poPolygon = new OGRPolygon(); @@ -436,19 +717,20 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) if (psChild == NULL) psChild = FindBareXMLChild( psNode, "exterior"); - if( psChild == NULL || psChild->psChild == NULL ) + psChild = GetChildElement(psChild); + if( psChild == NULL ) { - CPLError( CE_Failure, CPLE_AppDefined, - "Missing outerBoundaryIs property on Polygon." ); - delete poPolygon; - return NULL; + /* is invalid GML2, but valid GML3, so be tolerant */ + return poPolygon; } // Translate outer ring and add to polygon. poRing = (OGRLinearRing *) - GML2OGRGeometry_XMLNode( psChild->psChild ); + GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); if( poRing == NULL ) { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior ring"); delete poPolygon; return NULL; } @@ -456,8 +738,8 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) { CPLError( CE_Failure, CPLE_AppDefined, - "Got %.500s geometry as outerBoundaryIs instead of LINEARRING.", - poRing->getGeometryName() ); + "%s: Got %.500s geometry as outerBoundaryIs instead of LINEARRING.", + pszBaseGeometry, poRing->getGeometryName() ); delete poPolygon; delete poRing; return NULL; @@ -474,13 +756,24 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) && (EQUAL(BareGMLElement(psChild->pszValue),"innerBoundaryIs") || EQUAL(BareGMLElement(psChild->pszValue),"interior"))) { - poRing = (OGRLinearRing *) - GML2OGRGeometry_XMLNode( psChild->psChild ); + const CPLXMLNode* psInteriorChild = GetChildElement(psChild); + if (psInteriorChild != NULL) + poRing = (OGRLinearRing *) + GML2OGRGeometry_XMLNode( psInteriorChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + else + poRing = NULL; + if (poRing == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring"); + delete poPolygon; + return NULL; + } if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) { CPLError( CE_Failure, CPLE_AppDefined, - "Got %.500s geometry as innerBoundaryIs instead of LINEARRING.", - poRing->getGeometryName() ); + "%s: Got %.500s geometry as innerBoundaryIs instead of LINEARRING.", + pszBaseGeometry, poRing->getGeometryName() ); delete poPolygon; delete poRing; return NULL; @@ -509,10 +802,76 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) return poLinearRing; } +/* -------------------------------------------------------------------- */ +/* Ring GML3 */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Ring") ) + { + OGRLinearRing *poLinearRing = new OGRLinearRing(); + const CPLXMLNode *psChild; + + for( psChild = psNode->psChild; + psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) + { + const CPLXMLNode* psCurveChild = GetChildElement(psChild); + OGRGeometry* poGeom; + if (psCurveChild != NULL) + poGeom = + GML2OGRGeometry_XMLNode( psCurveChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + else + poGeom = NULL; + + // try to join multiline string to one linestring + if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) + { + poGeom = OGRGeometryFactory::forceToLineString( poGeom, false ); + } + + if( poGeom == NULL + || wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + { + delete poGeom; + delete poLinearRing; + return NULL; + } + + OGRLineString *poLS = (OGRLineString *) poGeom; + if( poLS->getNumPoints() < 2 ) + { + // skip it + } + else if( poLinearRing->getNumPoints() > 0 + && fabs(poLinearRing->getX(poLinearRing->getNumPoints()-1) - poLS->getX(0)) < 1e-14 + && fabs(poLinearRing->getY(poLinearRing->getNumPoints()-1) - poLS->getY(0)) < 1e-14 + && fabs(poLinearRing->getZ(poLinearRing->getNumPoints()-1) - poLS->getZ(0)) < 1e-14 ) + { + // Skip the first point of the new linestring to avoid + // invalidate duplicate points + poLinearRing->addSubLineString( poLS, 1 ); + } + else + { + // Add the whole new line string + poLinearRing->addSubLineString( poLS ); + } + + delete poLS; + } + } + + return poLinearRing; + } + /* -------------------------------------------------------------------- */ /* LineString */ /* -------------------------------------------------------------------- */ - if( EQUAL(pszBaseGeometry,"LineString") ) + if( EQUAL(pszBaseGeometry,"LineString") + || EQUAL(pszBaseGeometry,"LineStringSegment") + || EQUAL(pszBaseGeometry,"GeodesicString") ) { OGRLineString *poLine = new OGRLineString(); @@ -525,11 +884,140 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) return poLine; } +/* -------------------------------------------------------------------- */ +/* Arc/Circle : we approximate them by linear segments */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Arc") || + EQUAL(pszBaseGeometry,"Circle") ) + { + OGRLineString *poLine = new OGRLineString(); + + if( !ParseGMLCoordinates( psNode, poLine ) || + poLine->getNumPoints() != 3 ) + { + delete poLine; + return NULL; + } + + double x0 = poLine->getX(0); + double y0 = poLine->getY(0); + double x1 = poLine->getX(1); + double y1 = poLine->getY(1); + double x2 = poLine->getX(2); + double y2 = poLine->getY(2); + double dx01 = x1 - x0; + double dy01 = y1 - y0; + double dx12 = x2 - x1; + double dy12 = y2 - y1; + double c01 = dx01 * (x0 + x1) / 2 + dy01 * (y0 + y1) / 2; + double c12 = dx12 * (x1 + x2) / 2 + dy12 * (y1 + y2) / 2; + double det = dx01 * dy12 - dx12 * dy01; + if (det == 0) + { + return poLine; + } + double cx = (c01 * dy12 - c12 * dy01) / det; + double cy = (- c01 * dx12 + c12 * dx01) / det; + + double alpha0 = atan2(y0 - cy, x0 - cx); + double alpha1 = atan2(y1 - cy, x1 - cx); + double alpha2 = atan2(y2 - cy, x2 - cx); + double alpha3; + double R = sqrt((x0 - cx) * (x0 - cx) + (y0 - cy) * (y0 - cy)); + + /* if det is negative, the orientation if clockwise */ + if (det < 0) + { + if (alpha1 > alpha0) + alpha1 -= 2 * PI; + if (alpha2 > alpha1) + alpha2 -= 2 * PI; + alpha3 = alpha0 - 2 * PI; + } + else + { + if (alpha1 < alpha0) + alpha1 += 2 * PI; + if (alpha2 < alpha1) + alpha2 += 2 * PI; + alpha3 = alpha0 + 2 * PI; + } + + CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2 && alpha2 <= alpha3) || + (alpha0 >= alpha1 && alpha1 >= alpha2 && alpha2 >= alpha3)); + + int nSign = (det >= 0) ? 1 : -1; + + double alpha, dfRemainder; + double dfStep = atof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")) / 180 * PI; + + // make sure the segments are not too short + double dfMinStepLength = atof( CPLGetConfigOption("OGR_ARC_MINLENGTH","0") ); + if ( dfMinStepLength > 0.0 && dfStep * R < dfMinStepLength ) + { + CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with segment length %lf at radius %lf; min segment length is %lf)", + dfMinStepLength * 180.0 / PI / R, + dfStep * 180.0 / PI, + dfStep * R, + R, + dfMinStepLength ); + dfStep = dfMinStepLength / R; + } + + if (dfStep < 4. / 180 * PI) + { + CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with length %lf at radius %lf).", + 4. / 180 * PI, + dfStep * 180.0 / PI, + dfStep * R, + R ); + dfStep = 4. / 180 * PI; + } + + poLine->setNumPoints(0); + + dfStep *= nSign; + + dfRemainder = fmod(alpha1 - alpha0, dfStep) / 2.0; + + poLine->addPoint(x0, y0); + + for(alpha = alpha0 + dfStep + dfRemainder; (alpha + dfRemainder - alpha1) * nSign < 0; alpha += dfStep) + { + poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); + } + + poLine->addPoint(x1, y1); + + dfRemainder = fmod(alpha2 - alpha1, dfStep) / 2.0; + + for(alpha = alpha1 + dfStep + dfRemainder; (alpha + dfRemainder - alpha2) * nSign < 0; alpha += dfStep) + { + poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); + } + + if (EQUAL(pszBaseGeometry,"Circle")) + { + for(alpha = alpha2; (alpha - alpha3) * nSign < 0; alpha += dfStep) + { + poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); + } + poLine->addPoint(x0, y0); + } + else + { + poLine->addPoint(x2, y2); + } + + return poLine; + } + /* -------------------------------------------------------------------- */ /* PointType */ /* -------------------------------------------------------------------- */ if( EQUAL(pszBaseGeometry,"PointType") - || EQUAL(pszBaseGeometry,"Point") ) + || EQUAL(pszBaseGeometry,"Point") + || EQUAL(pszBaseGeometry,"ConnectionPoint") ) { OGRPoint *poPoint = new OGRPoint(); @@ -576,49 +1064,223 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) } /* -------------------------------------------------------------------- */ -/* MultiPolygon */ +/* Envelope */ /* -------------------------------------------------------------------- */ - if( EQUAL(pszBaseGeometry,"MultiPolygon") || - EQUAL(pszBaseGeometry,"MultiSurface") ) + if( EQUAL(pszBaseGeometry,"Envelope") ) { - const CPLXMLNode *psChild; - OGRMultiPolygon *poMPoly = new OGRMultiPolygon(); - - // Find all inner rings - for( psChild = psNode->psChild; - psChild != NULL; - psChild = psChild->psNext ) - { + const CPLXMLNode* psLowerCorner = FindBareXMLChild( psNode, "lowerCorner"); + const CPLXMLNode* psUpperCorner = FindBareXMLChild( psNode, "upperCorner"); + if( psLowerCorner == NULL || psUpperCorner == NULL ) + return NULL; + const char* pszLowerCorner = GetElementText(psLowerCorner); + const char* pszUpperCorner = GetElementText(psUpperCorner); + if( pszLowerCorner == NULL || pszUpperCorner == NULL ) + return NULL; + char** papszLowerCorner = CSLTokenizeString(pszLowerCorner); + char** papszUpperCorner = CSLTokenizeString(pszUpperCorner); + int nTokenCountLC = CSLCount(papszLowerCorner); + int nTokenCountUC = CSLCount(papszUpperCorner); + if( nTokenCountLC < 2 || nTokenCountUC < 2 ) + { + CSLDestroy(papszLowerCorner); + CSLDestroy(papszUpperCorner); + return NULL; + } + + double dfLLX = CPLAtof(papszLowerCorner[0]); + double dfLLY = CPLAtof(papszLowerCorner[1]); + double dfURX = CPLAtof(papszUpperCorner[0]); + double dfURY = CPLAtof(papszUpperCorner[1]); + CSLDestroy(papszLowerCorner); + CSLDestroy(papszUpperCorner); + + OGRLinearRing *poEnvelopeRing = new OGRLinearRing(); + OGRPolygon *poPoly = new OGRPolygon(); + + poEnvelopeRing->setNumPoints( 5 ); + poEnvelopeRing->setPoint(0, dfLLX, dfLLY); + poEnvelopeRing->setPoint(1, dfURX, dfLLY); + poEnvelopeRing->setPoint(2, dfURX, dfURY); + poEnvelopeRing->setPoint(3, dfLLX, dfURY); + poEnvelopeRing->setPoint(4, dfLLX, dfLLY); + poPoly->addRingDirectly(poEnvelopeRing ); + + return poPoly; + } + +/* ------------------------const CPLXMLNode *psChild;-------------------------------------------- */ +/* MultiPolygon / MultiSurface / CompositeSurface */ +/* */ +/* For CompositeSurface, this is a very rough approximation to deal with*/ +/* it as a MultiPolygon, because it can several faces of a 3D volume... */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiPolygon") || + EQUAL(pszBaseGeometry,"MultiSurface") || + EQUAL(pszBaseGeometry,"CompositeSurface") ) + { + const CPLXMLNode *psChild; + OGRMultiPolygon *poMPoly = new OGRMultiPolygon(); + int bReconstructTopology = FALSE; + + // Iterate over children + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { if( psChild->eType == CXT_Element && (EQUAL(BareGMLElement(psChild->pszValue),"polygonMember") || EQUAL(BareGMLElement(psChild->pszValue),"surfaceMember")) ) { + const CPLXMLNode* psSurfaceChild = GetChildElement(psChild); OGRPolygon *poPolygon; - poPolygon = (OGRPolygon *) - GML2OGRGeometry_XMLNode( psChild->psChild ); - - if( poPolygon == NULL ) + if (psSurfaceChild != NULL) { - delete poMPoly; - return NULL; + /* Cf #5421 where there are PolygonPatch with only inner rings */ + const CPLXMLNode* psPolygonPatch = GetChildElement(GetChildElement(psSurfaceChild)); + if( psPolygonPatch != NULL && + psPolygonPatch->eType == CXT_Element && + EQUAL(BareGMLElement(psPolygonPatch->pszValue),"PolygonPatch") && + GetChildElement(psPolygonPatch) != NULL && + EQUAL(BareGMLElement(GetChildElement(psPolygonPatch)->pszValue),"interior") ) + { + // Find all inner rings + for( const CPLXMLNode* psChild2 = psPolygonPatch->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild2->pszValue),"interior"))) + { + const CPLXMLNode* psInteriorChild = GetChildElement(psChild2); + OGRLinearRing* poRing; + if (psInteriorChild != NULL) + poRing = (OGRLinearRing *) + GML2OGRGeometry_XMLNode( psInteriorChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + else + poRing = NULL; + if (poRing == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring"); + delete poMPoly; + return NULL; + } + if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s: Got %.500s geometry as innerBoundaryIs instead of LINEARRING.", + pszBaseGeometry, poRing->getGeometryName() ); + delete poRing; + delete poMPoly; + return NULL; + } + + bReconstructTopology = TRUE; + poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly( poRing ); + poMPoly->addGeometryDirectly( poPolygon ); + } + } + } + else + { + poPolygon = (OGRPolygon *) + GML2OGRGeometry_XMLNode( psSurfaceChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + + if( poPolygon == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", + BareGMLElement(psChild->pszValue)); + delete poMPoly; + return NULL; + } + + if( !EQUAL(poPolygon->getGeometryName(),"POLYGON") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as polygonMember instead of MULTIPOLYGON.", + poPolygon->getGeometryName() ); + delete poPolygon; + delete poMPoly; + return NULL; + } + + poMPoly->addGeometryDirectly( poPolygon ); + } } - - if( !EQUAL(poPolygon->getGeometryName(),"POLYGON") ) + } + else if (psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"surfaceMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) { - CPLError( CE_Failure, CPLE_AppDefined, - "Got %.500s geometry as polygonMember instead of MULTIPOLYGON.", - poPolygon->getGeometryName() ); - delete poPolygon; - delete poMPoly; - return NULL; + if( psChild2->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild2->pszValue),"Surface") || + EQUAL(BareGMLElement(psChild2->pszValue),"Polygon") || + EQUAL(BareGMLElement(psChild2->pszValue),"PolygonPatch") || + EQUAL(BareGMLElement(psChild2->pszValue),"CompositeSurface")) ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1); + if (poGeom == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", + BareGMLElement(psChild2->pszValue)); + delete poMPoly; + return NULL; + } + + if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + { + poMPoly->addGeometryDirectly( (OGRPolygon*) poGeom ); + } + else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) + { + OGRMultiPolygon* poMPoly2 = (OGRMultiPolygon*) poGeom; + int i; + for(i=0;igetNumGeometries();i++) + { + poMPoly->addGeometry(poMPoly2->getGeometryRef(i)); + } + delete poGeom; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as polygonMember instead of POLYGON/MULTIPOLYGON.", + poGeom->getGeometryName() ); + delete poGeom; + delete poMPoly; + return NULL; + } + } } - - poMPoly->addGeometryDirectly( poPolygon ); } } - return poMPoly; + if( bReconstructTopology ) + { + int nPolygonCount = poMPoly->getNumGeometries(); + OGRGeometry** papoPolygons = new OGRGeometry*[ nPolygonCount ]; + for(int i=0;igetGeometryRef(0); + poMPoly->removeGeometry(0, FALSE); + } + delete poMPoly; + int bResultValidGeometry = FALSE; + OGRGeometry* poRet = OGRGeometryFactory::organizePolygons( + papoPolygons, nPolygonCount, &bResultValidGeometry ); + delete[] papoPolygons; + return poRet; + } + else + return poMPoly; } /* -------------------------------------------------------------------- */ @@ -637,22 +1299,64 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"pointMember") ) { + const CPLXMLNode* psPointChild = GetChildElement(psChild); OGRPoint *poPoint; - poPoint = (OGRPoint *) - GML2OGRGeometry_XMLNode( psChild->psChild ); - if( poPoint == NULL - || wkbFlatten(poPoint->getGeometryType()) != wkbPoint ) + if (psPointChild != NULL) { - CPLError( CE_Failure, CPLE_AppDefined, - "Got %.500s geometry as pointMember instead of MULTIPOINT", - poPoint ? poPoint->getGeometryName() : "NULL" ); - delete poPoint; - delete poMP; - return NULL; + poPoint = (OGRPoint *) + GML2OGRGeometry_XMLNode( psPointChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + if( poPoint == NULL + || wkbFlatten(poPoint->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MultiPoint: Got %.500s geometry as pointMember instead of POINT", + poPoint ? poPoint->getGeometryName() : "NULL" ); + delete poPoint; + delete poMP; + return NULL; + } + + poMP->addGeometryDirectly( poPoint ); + } + } + else if (psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"pointMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild2->pszValue),"Point")) ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1); + if (poGeom == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", + BareGMLElement(psChild2->pszValue)); + delete poMP; + return NULL; + } + + if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + poMP->addGeometryDirectly( (OGRPoint *)poGeom ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as pointMember instead of POINT.", + poGeom->getGeometryName() ); + delete poGeom; + delete poMP; + return NULL; + } + } } - - poMP->addGeometryDirectly( poPoint ); } } @@ -665,7 +1369,7 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) if( EQUAL(pszBaseGeometry,"MultiLineString") ) { const CPLXMLNode *psChild; - OGRMultiLineString *poMP = new OGRMultiLineString(); + OGRMultiLineString *poMLS = new OGRMultiLineString(); // collect lines for( psChild = psNode->psChild; @@ -675,31 +1379,227 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"lineStringMember") ) { + const CPLXMLNode* psLineStringChild = GetChildElement(psChild); OGRGeometry *poGeom; - poGeom = GML2OGRGeometry_XMLNode( psChild->psChild ); + if (psLineStringChild != NULL) + poGeom = GML2OGRGeometry_XMLNode( psLineStringChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + else + poGeom = NULL; if( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) { CPLError( CE_Failure, CPLE_AppDefined, - "Got %.500s geometry as Member instead of LINESTRING.", + "MultiLineString: Got %.500s geometry as Member instead of LINESTRING.", poGeom ? poGeom->getGeometryName() : "NULL" ); delete poGeom; - delete poMP; + delete poMLS; return NULL; } - poMP->addGeometryDirectly( poGeom ); + poMLS->addGeometryDirectly( poGeom ); } } - return poMP; + return poMLS; + } + + +/* -------------------------------------------------------------------- */ +/* MultiCurve / CompositeCurve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiCurve") || + EQUAL(pszBaseGeometry,"CompositeCurve") ) + { + const CPLXMLNode *psChild, *psCurve; + OGRMultiLineString *poMLS = new OGRMultiLineString(); + + // collect curveMembers + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) + { + OGRGeometry *poGeom; + + // There can be only one curve under a curveMember. + // Currently "Curve" and "LineString" are handled. + psCurve = FindBareXMLChild( psChild, "Curve" ); + if( psCurve == NULL ) + psCurve = FindBareXMLChild( psChild, "LineString" ); + if( psCurve == NULL ) + { + if( GetChildElement(psChild) != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get curve element in curveMember" ); + delete poMLS; + return NULL; + } + } + else + { + poGeom = GML2OGRGeometry_XMLNode( psCurve, bGetSecondaryGeometryOption, + nRecLevel + 1); + if( poGeom == NULL || + ( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MultiCurve: Got %.500s geometry as Member instead of LINESTRING.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + if( poGeom != NULL ) delete poGeom; + delete poMLS; + return NULL; + } + + poMLS->addGeometryDirectly( (OGRLineString *)poGeom ); + } + } + else if (psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild2->pszValue),"LineString")) ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1); + if (poGeom == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", + BareGMLElement(psChild2->pszValue)); + delete poMLS; + return NULL; + } + + if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + poMLS->addGeometryDirectly( (OGRLineString *)poGeom ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as curveMember instead of LINESTRING.", + poGeom->getGeometryName() ); + delete poGeom; + delete poMLS; + return NULL; + } + } + } + } + } + return poMLS; + } + +/* -------------------------------------------------------------------- */ +/* Curve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Curve") ) + { + const CPLXMLNode *psChild; + + psChild = FindBareXMLChild( psNode, "segments"); + if( psChild == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GML3 Curve geometry lacks segments element." ); + return NULL; + } + + OGRGeometry *poGeom; + + poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + if( poGeom == NULL || + wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Curve: Got %.500s geometry as Member instead of segments.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + if( poGeom != NULL ) delete poGeom; + return NULL; + } + + return poGeom; } /* -------------------------------------------------------------------- */ -/* GeometryCollection */ +/* segments */ /* -------------------------------------------------------------------- */ - if( EQUAL(pszBaseGeometry,"GeometryCollection") ) + if( EQUAL(pszBaseGeometry,"segments") ) + { + const CPLXMLNode *psChild; + OGRLineString *poLS = new OGRLineString(); + + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + + { + if( psChild->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild->pszValue),"LineStringSegment") || + EQUAL(BareGMLElement(psChild->pszValue),"GeodesicString") || + EQUAL(BareGMLElement(psChild->pszValue),"Arc") || + EQUAL(BareGMLElement(psChild->pszValue),"Circle")) ) + { + OGRGeometry *poGeom; + + poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1); + if( poGeom != NULL && + wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "segments: Got %.500s geometry as Member instead of LINESTRING.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + delete poGeom; + delete poLS; + return NULL; + } + OGRLineString *poAddLS = (OGRLineString *)poGeom; + if( poAddLS != NULL && poAddLS->getNumPoints() >= 2 ) + { + if( poLS->getNumPoints() > 0 + && fabs(poLS->getX(poLS->getNumPoints()-1) + - poAddLS->getX(0)) < 1e-14 + && fabs(poLS->getY(poLS->getNumPoints()-1) + - poAddLS->getY(0)) < 1e-14 + && fabs(poLS->getZ(poLS->getNumPoints()-1) + - poAddLS->getZ(0)) < 1e-14) + { + // Skip the first point of the new linestring to avoid + // invalidate duplicate points (#4451) + poLS->addSubLineString( poAddLS, 1 ); + } + else + { + // Add the whole new line string + poLS->addSubLineString( poAddLS ); + } + } + delete poGeom; + } + } + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* MultiGeometry */ +/* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is */ +/* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */ +/* tolerant with the non compliant files we produced... */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiGeometry") || + EQUAL(pszBaseGeometry,"GeometryCollection") ) { const CPLXMLNode *psChild; OGRGeometryCollection *poGC = new OGRGeometryCollection(); @@ -711,24 +1611,829 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) { if( psChild->eType == CXT_Element && EQUAL(BareGMLElement(psChild->pszValue),"geometryMember") ) + { + const CPLXMLNode* psGeometryChild = GetChildElement(psChild); + OGRGeometry *poGeom; + + if (psGeometryChild != NULL) + { + poGeom = GML2OGRGeometry_XMLNode( psGeometryChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeometryCollection: Failed to get geometry in geometryMember" ); + delete poGeom; + delete poGC; + return NULL; + } + + poGC->addGeometryDirectly( poGeom ); + } + } + } + + return poGC; + } + +/* -------------------------------------------------------------------- */ +/* Directed Edge */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"directedEdge") ) + { + const CPLXMLNode *psEdge, + *psdirectedNode, + *psNodeElement, + *pspointProperty, + *psPoint, + *psCurveProperty, + *psCurve; + int bEdgeOrientation = TRUE, + bNodeOrientation = TRUE; + OGRGeometry *poGeom; + OGRLineString *poLineString; + OGRPoint *poPositiveNode = NULL, *poNegativeNode = NULL; + OGRMultiPoint *poMP; + + bEdgeOrientation = GetElementOrientation(psNode); + + //collect edge + psEdge = FindBareXMLChild(psNode,"Edge"); + if( psEdge == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get Edge element in directedEdge" ); + return NULL; + } + + if( bGetSecondaryGeometry ) + { + psdirectedNode = FindBareXMLChild(psEdge,"directedNode"); + if( psdirectedNode == NULL ) goto nonode; + + bNodeOrientation = GetElementOrientation( psdirectedNode ); + + psNodeElement = FindBareXMLChild(psdirectedNode,"Node"); + if( psNodeElement == NULL ) goto nonode; + + pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty"); + if( pspointProperty == NULL ) + pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty"); + if( pspointProperty == NULL ) goto nonode; + + psPoint = FindBareXMLChild(pspointProperty,"Point"); + if( psPoint == NULL ) + psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint"); + if( psPoint == NULL ) goto nonode; + + poGeom = GML2OGRGeometry_XMLNode( psPoint, bGetSecondaryGeometryOption, + nRecLevel + 1, TRUE ); + if( poGeom == NULL + || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { +/* CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of POINT.", + poGeom ? poGeom->getGeometryName() : "NULL" );*/ + if( poGeom != NULL) delete poGeom; + goto nonode; + } + + if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation ) + poPositiveNode = (OGRPoint *)poGeom; + else + poNegativeNode = (OGRPoint *)poGeom; + + // look for the other node + psdirectedNode = psdirectedNode->psNext; + while( psdirectedNode != NULL && + !EQUAL( psdirectedNode->pszValue, "directedNode" ) ) + psdirectedNode = psdirectedNode->psNext; + if( psdirectedNode == NULL ) goto nonode; + + if( GetElementOrientation( psdirectedNode ) == bNodeOrientation ) + goto nonode; + + psNodeElement = FindBareXMLChild(psEdge,"Node"); + if( psNodeElement == NULL ) goto nonode; + + pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty"); + if( pspointProperty == NULL ) + pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty"); + if( pspointProperty == NULL ) goto nonode; + + psPoint = FindBareXMLChild(pspointProperty,"Point"); + if( psPoint == NULL ) + psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint"); + if( psPoint == NULL ) goto nonode; + + poGeom = GML2OGRGeometry_XMLNode( psPoint, bGetSecondaryGeometryOption, + nRecLevel + 1, TRUE ); + if( poGeom == NULL + || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { +/* CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of POINT.", + poGeom ? poGeom->getGeometryName() : "NULL" );*/ + if( poGeom != NULL) delete poGeom; + goto nonode; + } + + if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation ) + poNegativeNode = (OGRPoint *)poGeom; + else + poPositiveNode = (OGRPoint *)poGeom; + + poMP = new OGRMultiPoint(); + poMP->addGeometryDirectly( poNegativeNode ); + poMP->addGeometryDirectly( poPositiveNode ); + + return poMP; + + nonode:; + } + + // collect curveproperty + psCurveProperty = FindBareXMLChild(psEdge,"curveProperty"); + if( psCurveProperty == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "directedEdge: Failed to get curveProperty in Edge" ); + return NULL; + } + + psCurve = FindBareXMLChild(psCurveProperty,"LineString"); + if( psCurve == NULL ) + psCurve = FindBareXMLChild(psCurveProperty,"Curve"); + if( psCurve == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "directedEdge: Failed to get LineString or Curve tag in curveProperty" ); + return NULL; + } + + poLineString = (OGRLineString *)GML2OGRGeometry_XMLNode( psCurve, bGetSecondaryGeometryOption, + nRecLevel + 1, TRUE ); + if( poLineString == NULL + || wkbFlatten(poLineString->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of LINESTRING.", + poLineString ? poLineString->getGeometryName() : "NULL" ); + if( poLineString != NULL ) + delete poLineString; + return NULL; + } + + if( bGetSecondaryGeometry ) + { + // choose a point based on the orientation + poNegativeNode = new OGRPoint(); + poPositiveNode = new OGRPoint(); + if( bEdgeOrientation == bOrientation ) + { + poLineString->StartPoint( poNegativeNode ); + poLineString->EndPoint( poPositiveNode ); + } + else + { + poLineString->StartPoint( poPositiveNode ); + poLineString->EndPoint( poNegativeNode ); + } + delete poLineString; + + poMP = new OGRMultiPoint(); + poMP->addGeometryDirectly( poNegativeNode ); + poMP->addGeometryDirectly( poPositiveNode ); + + return poMP; + } + + // correct orientation of the line string + if( bEdgeOrientation != bOrientation ) + { + int iStartCoord = 0, iEndCoord = poLineString->getNumPoints() - 1; + OGRPoint *poTempStartPoint = new OGRPoint(); + OGRPoint *poTempEndPoint = new OGRPoint(); + while( iStartCoord < iEndCoord ) + { + poLineString->getPoint( iStartCoord, poTempStartPoint ); + poLineString->getPoint( iEndCoord, poTempEndPoint ); + poLineString->setPoint( iStartCoord, poTempEndPoint ); + poLineString->setPoint( iEndCoord, poTempStartPoint ); + iStartCoord++; + iEndCoord--; + } + delete poTempStartPoint; + delete poTempEndPoint; + } + return poLineString; + } + +/* -------------------------------------------------------------------- */ +/* TopoCurve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"TopoCurve") ) + { + const CPLXMLNode *psChild; + OGRMultiLineString *poMLS = NULL; + OGRMultiPoint *poMP = NULL; + + if( bGetSecondaryGeometry ) + poMP = new OGRMultiPoint(); + else + poMLS = new OGRMultiLineString(); + + // collect directedEdges + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"directedEdge")) { OGRGeometry *poGeom; - poGeom = GML2OGRGeometry_XMLNode( psChild->psChild ); + poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); if( poGeom == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, - "Failed to get geometry in geometryMember" ); + "Failed to get geometry in directedEdge" ); delete poGeom; - delete poGC; + if( bGetSecondaryGeometry ) + delete poMP; + else + delete poMLS; return NULL; } - poGC->addGeometryDirectly( poGeom ); + //Add the two points corresponding to the two nodes to poMP + if( bGetSecondaryGeometry && + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) + { + //TODO: TopoCurve geometries with more than one + // directedEdge elements were not tested. + if( poMP->getNumGeometries() <= 0 || + !(poMP->getGeometryRef( poMP->getNumGeometries() - 1 )->Equals(((OGRMultiPoint *)poGeom)->getGeometryRef( 0 ) ) )) + { + poMP->addGeometry( + ( (OGRMultiPoint *)poGeom )->getGeometryRef( 0 ) ); + } + poMP->addGeometry( + ( (OGRMultiPoint *)poGeom )->getGeometryRef( 1 ) ); + delete poGeom; + } + else if( !bGetSecondaryGeometry && + wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + poMLS->addGeometryDirectly( poGeom ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of %s.", + poGeom ? poGeom->getGeometryName() : "NULL", + bGetSecondaryGeometry?"MULTIPOINT":"LINESTRING"); + delete poGeom; + if( bGetSecondaryGeometry ) + delete poMP; + else + delete poMLS; + return NULL; + } } } - return poGC; + if( bGetSecondaryGeometry ) + return poMP; + else + return poMLS; + } + +/* -------------------------------------------------------------------- */ +/* TopoSurface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"TopoSurface") ) + { + /****************************************************************/ + /* applying the FaceHoleNegative = FALSE rules */ + /* */ + /* - each is expected to represent a MultiPolygon */ + /* - each is expected to represent a distinct Polygon, */ + /* this including any possible Interior Ring (holes); */ + /* orientation="+/-" plays no role at all to identify "holes" */ + /* - each within a may indifferently represent */ + /* an element of the Exterior or Interior Boundary; relative */ + /* order of is absolutely irrelevant. */ + /****************************************************************/ + /* Contributor: Alessandro Furieri, a.furieri@lqt.it */ + /* Developed for Faunalia (http://www.faunalia.it) */ + /* with funding from Regione Toscana - */ + /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE */ + /****************************************************************/ + if(bFaceHoleNegative != TRUE) + { + if( bGetSecondaryGeometry ) + return NULL; + +#ifndef HAVE_GEOS + static int bWarningAlreadyEmitted = FALSE; + if (!bWarningAlreadyEmitted) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Interpreating that GML TopoSurface geometry requires GDAL to be built with GEOS support.\n" + "As a workaround, you can try defining the GML_FACE_HOLE_NEGATIVE configuration option\n" + "to YES, so that the 'old' interpretation algorithm is used. But be warned that\n" + "the result might be incorrect.\n"); + bWarningAlreadyEmitted = TRUE; + } + return NULL; +#else + const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild; + OGRMultiPolygon *poTS = new OGRMultiPolygon(); + + // collect directed faces + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") ) + { + // collect next face (psChild->psChild) + psFaceChild = GetChildElement(psChild); + + while( psFaceChild != NULL && + !(psFaceChild->eType == CXT_Element && + EQUAL(BareGMLElement(psFaceChild->pszValue),"Face")) ) + psFaceChild = psFaceChild->psNext; + + if( psFaceChild == NULL ) + continue; + + OGRMultiLineString *poCollectedGeom = new OGRMultiLineString(); + + // collect directed edges of the face + for( psDirectedEdgeChild = psFaceChild->psChild; + psDirectedEdgeChild != NULL; + psDirectedEdgeChild = psDirectedEdgeChild->psNext ) + { + if( psDirectedEdgeChild->eType == CXT_Element && + EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") ) + { + OGRGeometry *poEdgeGeom; + + poEdgeGeom = GML2OGRGeometry_XMLNode( psDirectedEdgeChild, + bGetSecondaryGeometryOption, + TRUE ); + + if( poEdgeGeom == NULL || + wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get geometry in directedEdge" ); + delete poEdgeGeom; + delete poCollectedGeom; + delete poTS; + return NULL; + } + + poCollectedGeom->addGeometryDirectly( poEdgeGeom ); + } + } + + OGRGeometry *poFaceCollectionGeom = NULL; + OGRPolygon *poFaceGeom = NULL; + +//#ifdef HAVE_GEOS + poFaceCollectionGeom = poCollectedGeom->Polygonize(); + if( poFaceCollectionGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to assemble Edges in Face" ); + delete poCollectedGeom; + delete poTS; + return NULL; + } + + poFaceGeom = GML2FaceExtRing( poFaceCollectionGeom ); +//#else +// poFaceGeom = (OGRPolygon*) OGRBuildPolygonFromEdges( +// (OGRGeometryH) poCollectedGeom, +// FALSE, TRUE, 0, NULL); +//#endif + + if( poFaceGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to build Polygon for Face" ); + delete poCollectedGeom; + delete poTS; + return NULL; + } + else + { + int iCount = poTS->getNumGeometries(); + if( iCount == 0) + { + /* inserting the first Polygon */ + poTS->addGeometryDirectly( poFaceGeom ); + } + else + { + /* using Union to add the current Polygon */ + OGRGeometry *poUnion = poTS->Union( poFaceGeom ); + delete poFaceGeom; + delete poTS; + if( poUnion == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed Union for TopoSurface" ); + return NULL; + } + poTS = (OGRMultiPolygon *)poUnion; + } + } + delete poFaceCollectionGeom; + delete poCollectedGeom; + } + } + + if( wkbFlatten( poTS->getGeometryType()) == wkbPolygon ) + { + /* forcing to be a MultiPolygon */ + OGRGeometry *poOldTS = poTS; + poTS = new OGRMultiPolygon(); + poTS->addGeometryDirectly(poOldTS); + } + + return poTS; +#endif // HAVE_GEOS + } + + /****************************************************************/ + /* applying the FaceHoleNegative = TRUE rules */ + /* */ + /* - each is expected to represent a MultiPolygon */ + /* - any declaring orientation="+" is expected to */ + /* represent an Exterior Ring (no holes are allowed) */ + /* - any declaring orientation="-" is expected to */ + /* represent an Interior Ring (hole) belonging to the latest */ + /* Exterior Ring. */ + /* - within the same are expected to be */ + /* arranged in geometrically adjacent and consecutive */ + /* sequence. */ + /****************************************************************/ + if( bGetSecondaryGeometry ) + return NULL; + const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild; + int bFaceOrientation = TRUE; + OGRPolygon *poTS = new OGRPolygon(); + + // collect directed faces + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") ) + { + bFaceOrientation = GetElementOrientation(psChild); + + // collect next face (psChild->psChild) + psFaceChild = GetChildElement(psChild); + while( psFaceChild != NULL && + !EQUAL(BareGMLElement(psFaceChild->pszValue),"Face") ) + psFaceChild = psFaceChild->psNext; + + if( psFaceChild == NULL ) + continue; + + OGRLinearRing *poFaceGeom = new OGRLinearRing(); + + // collect directed edges of the face + for( psDirectedEdgeChild = psFaceChild->psChild; + psDirectedEdgeChild != NULL; + psDirectedEdgeChild = psDirectedEdgeChild->psNext ) + { + if( psDirectedEdgeChild->eType == CXT_Element && + EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") ) + { + OGRGeometry *poEdgeGeom; + + poEdgeGeom = GML2OGRGeometry_XMLNode( psDirectedEdgeChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, + TRUE, + bFaceOrientation ); + + if( poEdgeGeom == NULL || + wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get geometry in directedEdge" ); + delete poEdgeGeom; + delete poFaceGeom; + delete poTS; + return NULL; + } + + OGRLineString *poLS; + OGRLineString *poAddLS; + if( !bFaceOrientation ) + { + poLS = (OGRLineString *)poEdgeGeom; + poAddLS = (OGRLineString *)poFaceGeom; + if( poAddLS->getNumPoints() < 2 ) + { + /* skip it */ + } + else if( poLS->getNumPoints() > 0 + && fabs(poLS->getX(poLS->getNumPoints()-1) + - poAddLS->getX(0)) < 1e-14 + && fabs(poLS->getY(poLS->getNumPoints()-1) + - poAddLS->getY(0)) < 1e-14 + && fabs(poLS->getZ(poLS->getNumPoints()-1) + - poAddLS->getZ(0)) < 1e-14) + { + // Skip the first point of the new linestring to avoid + // invalidate duplicate points + poLS->addSubLineString( poAddLS, 1 ); + } + else + { + // Add the whole new line string + poLS->addSubLineString( poAddLS ); + } + poFaceGeom->empty(); + } + poLS = (OGRLineString *)poFaceGeom; + poAddLS = (OGRLineString *)poEdgeGeom; + if( poAddLS->getNumPoints() < 2 ) + { + /* skip it */ + } + else if( poLS->getNumPoints() > 0 + && fabs(poLS->getX(poLS->getNumPoints()-1) + - poAddLS->getX(0)) < 1e-14 + && fabs(poLS->getY(poLS->getNumPoints()-1) + - poAddLS->getY(0)) < 1e-14 + && fabs(poLS->getZ(poLS->getNumPoints()-1) + - poAddLS->getZ(0)) < 1e-14) + { + // Skip the first point of the new linestring to avoid + // invalidate duplicate points + poLS->addSubLineString( poAddLS, 1 ); + } + else + { + // Add the whole new line string + poLS->addSubLineString( poAddLS ); + } + delete poEdgeGeom; + } + } + +/* if( poFaceGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get Face geometry in directedFace" ); + delete poFaceGeom; + return NULL; + }*/ + + poTS->addRingDirectly( poFaceGeom ); + } + } + +/* if( poTS == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get TopoSurface geometry" ); + delete poTS; + return NULL; + }*/ + + return poTS; + } + +/* -------------------------------------------------------------------- */ +/* Surface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Surface") ) + { + const CPLXMLNode *psChild; + OGRGeometry *poResult = NULL; + + // Find outer ring. + psChild = FindBareXMLChild( psNode, "patches" ); + if( psChild == NULL ) + psChild = FindBareXMLChild( psNode, "polygonPatches" ); + if( psChild == NULL ) + psChild = FindBareXMLChild( psNode, "trianglePatches" ); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + /* and are valid GML */ + return new OGRPolygon(); + } + + for( ; psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild->pszValue),"PolygonPatch") || + EQUAL(BareGMLElement(psChild->pszValue),"Triangle") || + EQUAL(BareGMLElement(psChild->pszValue),"Rectangle"))) + { + OGRPolygon *poPolygon = (OGRPolygon *) + GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); + if( poPolygon == NULL ) + return NULL; + + if( poResult == NULL ) + poResult = poPolygon; + else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon ) + { + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + poMP->addGeometryDirectly( poResult ); + poMP->addGeometryDirectly( poPolygon ); + poResult = poMP; + } + else + { + ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon ); + } + } + } + + return poResult; + } + +/* -------------------------------------------------------------------- */ +/* TriangulatedSurface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"TriangulatedSurface") || + EQUAL(pszBaseGeometry,"Tin") ) + { + const CPLXMLNode *psChild; + OGRGeometry *poResult = NULL; + + // Find trianglePatches + psChild = FindBareXMLChild( psNode, "trianglePatches" ); + if (psChild == NULL) + psChild = FindBareXMLChild( psNode, "patches" ); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing for %s.", pszBaseGeometry ); + return NULL; + } + + for( ; psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"Triangle") ) + { + OGRPolygon *poPolygon = (OGRPolygon *) + GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); + if( poPolygon == NULL ) + return NULL; + + if( poResult == NULL ) + poResult = poPolygon; + else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon ) + { + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + poMP->addGeometryDirectly( poResult ); + poMP->addGeometryDirectly( poPolygon ); + poResult = poMP; + } + else + { + ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon ); + } + } + } + + return poResult; + } + +/* -------------------------------------------------------------------- */ +/* Solid */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Solid") ) + { + const CPLXMLNode *psChild; + OGRGeometry* poGeom; + + // Find exterior element + psChild = FindBareXMLChild( psNode, "exterior"); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + /* and are valid GML */ + return new OGRPolygon(); + } + + // Get the geometry inside + poGeom = GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior element"); + delete poGeom; + return NULL; + } + + psChild = FindBareXMLChild( psNode, "interior"); + if( psChild != NULL ) + { + static int bWarnedOnce = FALSE; + if (!bWarnedOnce) + { + CPLError( CE_Warning, CPLE_AppDefined, + " elements of are ignored"); + bWarnedOnce = TRUE; + } + } + + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* OrientableSurface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"OrientableSurface") ) + { + const CPLXMLNode *psChild; + + // Find baseSurface. + psChild = FindBareXMLChild( psNode, "baseSurface" ); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing for OrientableSurface." ); + return NULL; + } + + return GML2OGRGeometry_XMLNode( psChild, bGetSecondaryGeometryOption, + nRecLevel + 1 ); + } + +/* -------------------------------------------------------------------- */ +/* SimplePolygon, SimpleRectangle, SimpleTriangle */ +/* (GML 3.3 compact encoding) */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"SimplePolygon") || + EQUAL(pszBaseGeometry,"SimpleRectangle") || + EQUAL(pszBaseGeometry,"SimpleTriangle") ) + { + OGRLinearRing *poRing = new OGRLinearRing(); + + if( !ParseGMLCoordinates( psNode, poRing ) ) + { + delete poRing; + return NULL; + } + + poRing->closeRings(); + + OGRPolygon* poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly(poRing); + return poPolygon; + } + +/* -------------------------------------------------------------------- */ +/* SimpleMultiPoint (GML 3.3 compact encoding) */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"SimpleMultiPoint") ) + { + OGRLineString *poLS = new OGRLineString(); + + if( !ParseGMLCoordinates( psNode, poLS ) ) + { + delete poLS; + return NULL; + } + + OGRMultiPoint* poMP = new OGRMultiPoint(); + int nPoints = poLS->getNumPoints(); + for(int i = 0; i < nPoints; i++) + { + OGRPoint* poPoint = new OGRPoint(); + poLS->getPoint(i, poPoint); + poMP->addGeometryDirectly(poPoint); + } + delete poLS; + return poMP; } CPLError( CE_Failure, CPLE_AppDefined, @@ -745,20 +2450,47 @@ static OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode ) OGRGeometryH OGR_G_CreateFromGMLTree( const CPLXMLNode *psTree ) { - return (OGRGeometryH) GML2OGRGeometry_XMLNode( psTree ); + return (OGRGeometryH) GML2OGRGeometry_XMLNode( psTree, -1 ); } /************************************************************************/ /* OGR_G_CreateFromGML() */ /************************************************************************/ +/** + * \brief Create geometry from GML. + * + * This method translates a fragment of GML containing only the geometry + * portion into a corresponding OGRGeometry. There are many limitations + * on the forms of GML geometries supported by this parser, but they are + * too numerous to list here. + * + * The following GML2 elements are parsed : Point, LineString, Polygon, + * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. + * + * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface, + * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, CompositeCurve, + * LineStringSegment, Arc, Circle, CompositeSurface, OrientableSurface, Solid, + * Tin, TriangulatedSurface. + * + * Arc and Circle elements are stroked to linestring, by using a + * 4 degrees step, unless the user has overridden the value with the + * OGR_ARC_STEPSIZE configuration variable. + * + * The C++ method OGRGeometryFactory::createFromGML() is the same as this function. + * + * @param pszGML The GML fragment for the geometry. + * + * @return a geometry on succes, or NULL on error. + */ + OGRGeometryH OGR_G_CreateFromGML( const char *pszGML ) { if( pszGML == NULL || strlen(pszGML) == 0 ) { CPLError( CE_Failure, CPLE_AppDefined, - "GML Geometry is empty in GML2OGRGeometry()." ); + "GML Geometry is empty in OGR_G_CreateFromGML()." ); return NULL; } @@ -777,9 +2509,13 @@ OGRGeometryH OGR_G_CreateFromGML( const char *pszGML ) /* -------------------------------------------------------------------- */ OGRGeometry *poGeometry; - poGeometry = GML2OGRGeometry_XMLNode( psGML ); + /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */ + int bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); + poGeometry = GML2OGRGeometry_XMLNode( psGML, -1, 0, FALSE, TRUE, bFaceHoleNegative ); CPLDestroyXMLNode( psGML ); return (OGRGeometryH) poGeometry; } + + diff --git a/ogr/makefile.vc b/ogr/makefile.vc index cfb42f8..7a165b9 100755 --- a/ogr/makefile.vc +++ b/ogr/makefile.vc @@ -12,7 +12,16 @@ OBJ = ogr_srsnode.obj ogrcurve.obj ogrfeature.obj ogrfeaturedefn.obj \ ogrfeaturestyle.obj ogr_fromepsg.obj ogrfeaturequery.obj swq.obj \ ogrct.obj ogr_gensql.obj ogr_srs_xml.obj ogr_srs_esri.obj \ ogr_api.obj gml2ogrgeometry.obj ogr2gmlgeometry.obj \ - ogr_miattrind.obj ogr_attrind.obj ogr_srs_dict.obj + ogr_miattrind.obj ogr_attrind.obj ogr_srs_dict.obj \ + swq_expr_node.obj swq_op_general.obj swq_op_registrar.obj swq_parser.obj swq_select.obj \ + gdalmajorobject.obj gdaldataset.obj gdalrasterband.obj gdalmultidomainmetadata.obj \ + ogrgeomfielddefn.obj gdal_misc.obj gdaldriver.obj gdaldrivermanager.obj \ + gdalcolortable.obj rasterio.obj gdalrasterblock.obj gdaldefaultoverviews.obj \ + gdaltransformer.obj gdalopeninfo.obj gdalclientserver.obj gdalpamdataset.obj gdalpamrasterband.obj \ + gdalnodatamaskband.obj gdalnodatavaluesmaskband.obj gdalpamproxydb.obj gdal_rat.obj \ + gdalvirtualmem.obj gdalallvalidmaskband.obj gdalproxypool.obj gdalproxydataset.obj gdaldefaultasync.obj \ + ogrunionlayer.obj ogrwarpedlayer.obj ogrlayerdecorator.obj overview.obj ogr_srs_ozi.obj \ + stub.obj LIB = ogr.lib diff --git a/ogr/ogr2gmlgeometry.cpp b/ogr/ogr2gmlgeometry.cpp index 69dc2f8..cff0cdf 100644 --- a/ogr/ogr2gmlgeometry.cpp +++ b/ogr/ogr2gmlgeometry.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr2gmlgeometry.cpp 18025 2009-11-14 19:09:50Z rouault $ + * $Id: ogr2gmlgeometry.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: GML Translator * Purpose: Code to translate OGRGeometry to GML string representation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -176,8 +177,8 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, /* -------------------------------------------------------------------- */ // Buffer for srsName attribute (srsName="...") - char szSrsName[30] = { 0 }; - int nSrsNameLength = 0; + char szAttributes[30] = { 0 }; + int nAttrsLength = 0; const OGRSpatialReference* poSRS = NULL; poSRS = poGeometry->getSpatialReference(); @@ -201,10 +202,10 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, pszAuthCode = poSRS->GetAuthorityCode( pszTarget ); if( NULL != pszAuthCode && strlen(pszAuthCode) < 10 ) { - sprintf( szSrsName, " srsName=\"%s:%s\"", + sprintf( szAttributes, " srsName=\"%s:%s\"", pszAuthName, pszAuthCode ); - nSrsNameLength = strlen(szSrsName); + nAttrsLength = strlen(szAttributes); } } } @@ -221,12 +222,12 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, MakeGMLCoordinate( szCoordinate, poPoint->getX(), poPoint->getY(), 0.0, FALSE ); - _GrowBuffer( *pnLength + strlen(szCoordinate) + 60 + nSrsNameLength, + _GrowBuffer( *pnLength + strlen(szCoordinate) + 60 + nAttrsLength, ppszText, pnMaxLength ); sprintf( *ppszText + *pnLength, "%s", - szSrsName, szCoordinate ); + szAttributes, szCoordinate ); *pnLength += strlen( *ppszText + *pnLength ); } @@ -242,12 +243,12 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, poPoint->getX(), poPoint->getY(), poPoint->getZ(), TRUE ); - _GrowBuffer( *pnLength + strlen(szCoordinate) + 70 + nSrsNameLength, + _GrowBuffer( *pnLength + strlen(szCoordinate) + 70 + nAttrsLength, ppszText, pnMaxLength ); sprintf( *ppszText + *pnLength, "%s", - szSrsName, szCoordinate ); + szAttributes, szCoordinate ); *pnLength += strlen( *ppszText + *pnLength ); } @@ -263,18 +264,18 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, // Buffer for tag name + srsName attribute if set const size_t nLineTagLength = 16; char* pszLineTagName = NULL; - pszLineTagName = (char *) CPLMalloc( nLineTagLength + nSrsNameLength + 1 ); + pszLineTagName = (char *) CPLMalloc( nLineTagLength + nAttrsLength + 1 ); if( bRing ) { - sprintf( pszLineTagName, "", szSrsName ); + sprintf( pszLineTagName, "", szAttributes ); AppendString( ppszText, pnLength, pnMaxLength, pszLineTagName ); } else { - sprintf( pszLineTagName, "", szSrsName ); + sprintf( pszLineTagName, "", szAttributes ); AppendString( ppszText, pnLength, pnMaxLength, pszLineTagName ); @@ -305,10 +306,10 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, // Buffer for polygon tag name + srsName attribute if set const size_t nPolyTagLength = 13; char* pszPolyTagName = NULL; - pszPolyTagName = (char *) CPLMalloc( nPolyTagLength + nSrsNameLength + 1 ); + pszPolyTagName = (char *) CPLMalloc( nPolyTagLength + nAttrsLength + 1 ); // Compose Polygon tag with or without srsName attribute - sprintf( pszPolyTagName, "", szSrsName ); + sprintf( pszPolyTagName, "", szAttributes ); AppendString( ppszText, pnLength, pnMaxLength, pszPolyTagName ); @@ -354,7 +355,7 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, } /* -------------------------------------------------------------------- */ -/* MultiPolygon */ +/* MultiPolygon, MultiLineString, MultiPoint, MultiGeometry */ /* -------------------------------------------------------------------- */ else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString @@ -371,34 +372,34 @@ static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon ) { - pszElemOpen = (char *) CPLMalloc( 13 + nSrsNameLength + 1 ); - sprintf( pszElemOpen, "MultiPolygon%s>", szSrsName ); + pszElemOpen = (char *) CPLMalloc( 13 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiPolygon%s>", szAttributes ); pszElemClose = "MultiPolygon>"; pszMemberElem = "polygonMember>"; } else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString ) { - pszElemOpen = (char *) CPLMalloc( 16 + nSrsNameLength + 1 ); - sprintf( pszElemOpen, "MultiLineString%s>", szSrsName ); + pszElemOpen = (char *) CPLMalloc( 16 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiLineString%s>", szAttributes ); pszElemClose = "MultiLineString>"; pszMemberElem = "lineStringMember>"; } else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint ) { - pszElemOpen = (char *) CPLMalloc( 11 + nSrsNameLength + 1 ); - sprintf( pszElemOpen, "MultiPoint%s>", szSrsName ); + pszElemOpen = (char *) CPLMalloc( 11 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiPoint%s>", szAttributes ); pszElemClose = "MultiPoint>"; pszMemberElem = "pointMember>"; } else { - pszElemOpen = (char *) CPLMalloc( 19 + nSrsNameLength + 1 ); - sprintf( pszElemOpen, "GeometryCollection%s>", szSrsName ); + pszElemOpen = (char *) CPLMalloc( 19 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiGeometry%s>", szAttributes ); - pszElemClose = "GeometryCollection>"; + pszElemClose = "MultiGeometry>"; pszMemberElem = "geometryMember>"; } @@ -494,6 +495,407 @@ CPLXMLNode *OGR_G_ExportEnvelopeToGMLTree( OGRGeometryH hGeometry ) return psBox; } + +/************************************************************************/ +/* AppendGML3CoordinateList() */ +/************************************************************************/ + +static void AppendGML3CoordinateList( OGRLineString *poLine, int bCoordSwap, + char **ppszText, int *pnLength, + int *pnMaxLength ) + +{ + char szCoordinate[256]; + int b3D = (poLine->getGeometryType() & wkb25DBit); + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( *pnLength + 40, ppszText, pnMaxLength ); + + if (b3D) + strcat( *ppszText + *pnLength, "" ); + else + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); + + + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + if (bCoordSwap) + OGRMakeWktCoordinate( szCoordinate, + poLine->getY(iPoint), + poLine->getX(iPoint), + poLine->getZ(iPoint), + b3D ? 3 : 2 ); + else + OGRMakeWktCoordinate( szCoordinate, + poLine->getX(iPoint), + poLine->getY(iPoint), + poLine->getZ(iPoint), + b3D ? 3 : 2 ); + _GrowBuffer( *pnLength + strlen(szCoordinate)+1, + ppszText, pnMaxLength ); + + if( iPoint != 0 ) + strcat( *ppszText + *pnLength, " " ); + + strcat( *ppszText + *pnLength, szCoordinate ); + *pnLength += strlen(*ppszText + *pnLength); + } + + _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); +} + +/************************************************************************/ +/* OGR2GML3GeometryAppend() */ +/************************************************************************/ + +static int OGR2GML3GeometryAppend( OGRGeometry *poGeometry, + const OGRSpatialReference* poParentSRS, + char **ppszText, int *pnLength, + int *pnMaxLength, + int bIsSubGeometry, + int bLongSRS, + int bLineStringAsCurve, + const char* pszGMLId = NULL) + +{ + +/* -------------------------------------------------------------------- */ +/* Check for Spatial Reference System attached to given geometry */ +/* -------------------------------------------------------------------- */ + + // Buffer for srsName and gml:id attributes (srsName="..." gml:id="...") + char szAttributes[256]; + int nAttrsLength = 0; + + szAttributes[0] = 0; + + const OGRSpatialReference* poSRS = NULL; + if (poParentSRS) + poSRS = poParentSRS; + else + poParentSRS = poSRS = poGeometry->getSpatialReference(); + + int bCoordSwap = FALSE; + + if( NULL != poSRS ) + { + const char* pszAuthName = NULL; + const char* pszAuthCode = NULL; + const char* pszTarget = NULL; + + if (poSRS->IsProjected()) + pszTarget = "PROJCS"; + else + pszTarget = "GEOGCS"; + + pszAuthName = poSRS->GetAuthorityName( pszTarget ); + if( NULL != pszAuthName ) + { + if( EQUAL( pszAuthName, "EPSG" ) ) + { + pszAuthCode = poSRS->GetAuthorityCode( pszTarget ); + if( NULL != pszAuthCode && strlen(pszAuthCode) < 10 ) + { + if (bLongSRS && !(((OGRSpatialReference*)poSRS)->EPSGTreatsAsLatLong() || + ((OGRSpatialReference*)poSRS)->EPSGTreatsAsNorthingEasting())) + { + OGRSpatialReference oSRS; + if (oSRS.importFromEPSGA(atoi(pszAuthCode)) == OGRERR_NONE) + { + if (oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting()) + bCoordSwap = TRUE; + } + } + + if (!bIsSubGeometry) + { + if (bLongSRS) + { + snprintf( szAttributes, sizeof(szAttributes), + " srsName=\"urn:ogc:def:crs:%s::%s\"", + pszAuthName, pszAuthCode ); + } + else + { + snprintf( szAttributes, sizeof(szAttributes), + " srsName=\"%s:%s\"", + pszAuthName, pszAuthCode ); + } + + nAttrsLength = strlen(szAttributes); + } + } + } + } + } + + if (pszGMLId != NULL && nAttrsLength + 9 + strlen(pszGMLId) + 1 < sizeof(szAttributes)) + { + strcat(szAttributes, " gml:id=\""); + strcat(szAttributes, pszGMLId); + strcat(szAttributes, "\""); + nAttrsLength = strlen(szAttributes); + } + +/* -------------------------------------------------------------------- */ +/* 2D Point */ +/* -------------------------------------------------------------------- */ + if( poGeometry->getGeometryType() == wkbPoint ) + { + char szCoordinate[256]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if (bCoordSwap) + OGRMakeWktCoordinate( szCoordinate, + poPoint->getY(), poPoint->getX(), 0.0, 2 ); + else + OGRMakeWktCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), 0.0, 2 ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 60 + nAttrsLength, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szAttributes, szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } +/* -------------------------------------------------------------------- */ +/* 3D Point */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPoint25D ) + { + char szCoordinate[256]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if (bCoordSwap) + OGRMakeWktCoordinate( szCoordinate, + poPoint->getY(), poPoint->getX(), poPoint->getZ(), 3 ); + else + OGRMakeWktCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), poPoint->getZ(), 3 ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 70 + nAttrsLength, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szAttributes, szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } + +/* -------------------------------------------------------------------- */ +/* LineString and LinearRing */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbLineString + || poGeometry->getGeometryType() == wkbLineString25D ) + { + int bRing = EQUAL(poGeometry->getGeometryName(),"LINEARRING"); + if (!bRing && bLineStringAsCurve) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + AppendGML3CoordinateList( (OGRLineString *) poGeometry, bCoordSwap, + ppszText, pnLength, pnMaxLength ); + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + else + { + // Buffer for tag name + srsName attribute if set + const size_t nLineTagLength = 16; + char* pszLineTagName = NULL; + pszLineTagName = (char *) CPLMalloc( nLineTagLength + nAttrsLength + 1 ); + + if( bRing ) + { + /* LinearRing isn't supposed to have srsName attribute according to GML3 SF-0 */ + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + else + { + sprintf( pszLineTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszLineTagName ); + } + + // FREE TAG BUFFER + CPLFree( pszLineTagName ); + + AppendGML3CoordinateList( (OGRLineString *) poGeometry, bCoordSwap, + ppszText, pnLength, pnMaxLength ); + + if( bRing ) + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + else + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPolygon + || poGeometry->getGeometryType() == wkbPolygon25D ) + { + OGRPolygon *poPolygon = (OGRPolygon *) poGeometry; + + // Buffer for polygon tag name + srsName attribute if set + const size_t nPolyTagLength = 13; + char* pszPolyTagName = NULL; + pszPolyTagName = (char *) CPLMalloc( nPolyTagLength + nAttrsLength + 1 ); + + // Compose Polygon tag with or without srsName attribute + sprintf( pszPolyTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszPolyTagName ); + + // FREE TAG BUFFER + CPLFree( pszPolyTagName ); + + // Don't add srsName to polygon rings + + if( poPolygon->getExteriorRing() != NULL ) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2GML3GeometryAppend( poPolygon->getExteriorRing(), poSRS, + ppszText, pnLength, pnMaxLength, + TRUE, bLongSRS, bLineStringAsCurve ) ) + { + return FALSE; + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing); + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2GML3GeometryAppend( poRing, poSRS, ppszText, pnLength, + pnMaxLength, TRUE, bLongSRS, bLineStringAsCurve ) ) + return FALSE; + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* MultiPolygon, MultiLineString, MultiPoint, MultiGeometry */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint + || wkbFlatten(poGeometry->getGeometryType()) == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry; + int iMember; + const char *pszElemClose = NULL; + const char *pszMemberElem = NULL; + + // Buffer for opening tag + srsName attribute + char* pszElemOpen = NULL; + + if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon ) + { + pszElemOpen = (char *) CPLMalloc( 13 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiSurface%s>", szAttributes ); + + pszElemClose = "MultiSurface>"; + pszMemberElem = "surfaceMember>"; + } + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString ) + { + pszElemOpen = (char *) CPLMalloc( 16 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiCurve%s>", szAttributes ); + + pszElemClose = "MultiCurve>"; + pszMemberElem = "curveMember>"; + } + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint ) + { + pszElemOpen = (char *) CPLMalloc( 11 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiPoint%s>", szAttributes ); + + pszElemClose = "MultiPoint>"; + pszMemberElem = "pointMember>"; + } + else + { + pszElemOpen = (char *) CPLMalloc( 19 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiGeometry%s>", szAttributes ); + + pszElemClose = "MultiGeometry>"; + pszMemberElem = "geometryMember>"; + } + + AppendString( ppszText, pnLength, pnMaxLength, "getNumGeometries(); iMember++) + { + OGRGeometry *poMember = poGC->getGeometryRef( iMember ); + + AppendString( ppszText, pnLength, pnMaxLength, " + *
  • FORMAT=GML3. Otherwise it will default to GML 2.1.2 output. + *
  • GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3) To use gml:Curve element for linestrings. + * Otherwise gml:LineString will be used . + *
  • GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3) Default to YES. If YES, SRS with EPSG authority will + * be written with the "urn:ogc:def:crs:EPSG::" prefix. + * In the case, if the SRS is a geographic SRS without explicit AXIS order, but that the same SRS authority code + * imported with ImportFromEPSGA() should be treated as lat/long, then the function will take care of coordinate order swapping. + * If set to NO, SRS with EPSG authority will be written with the "EPSG:" prefix, even if they are in lat/long order. + *
  • GMLID=astring. If specified, a gml:id attribute will be written in the top-level geometry element with the provided value. + * Required for GML 3.2 compatibility. + * + * + * This method is the same as the C++ method OGRGeometry::exportToGML(). + * + * @param hGeometry handle to the geometry. + * @param papszOptions NULL-terminated list of options. + * @return A GML fragment or NULL in case of error. + * + * @since OGR 1.8.0 + */ + +char *OGR_G_ExportToGMLEx( OGRGeometryH hGeometry, char** papszOptions ) + { char *pszText; int nLength = 0, nMaxLength = 1; @@ -531,7 +986,24 @@ char *OGR_G_ExportToGML( OGRGeometryH hGeometry ) pszText = (char *) CPLMalloc(nMaxLength); pszText[0] = '\0'; - if( !OGR2GMLGeometryAppend( (OGRGeometry *) hGeometry, &pszText, + const char* pszFormat = CSLFetchNameValue(papszOptions, "FORMAT"); + if (pszFormat && EQUAL(pszFormat, "GML3")) + { + const char* pszLineStringElement = CSLFetchNameValue(papszOptions, "GML3_LINESTRING_ELEMENT"); + int bLineStringAsCurve = (pszLineStringElement && EQUAL(pszLineStringElement, "curve")); + int bLongSRS = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "GML3_LONGSRS", "YES")); + const char* pszGMLId = CSLFetchNameValue(papszOptions, "GMLID"); + if( !OGR2GML3GeometryAppend( (OGRGeometry *) hGeometry, NULL, &pszText, + &nLength, &nMaxLength, FALSE, bLongSRS, bLineStringAsCurve, pszGMLId )) + { + CPLFree( pszText ); + return NULL; + } + else + return pszText; + } + + if( !OGR2GMLGeometryAppend( (OGRGeometry *) hGeometry, &pszText, &nLength, &nMaxLength, FALSE )) { CPLFree( pszText ); diff --git a/ogr/ogr_api.cpp b/ogr/ogr_api.cpp index 97fdb67..c80c5ae 100644 --- a/ogr/ogr_api.cpp +++ b/ogr/ogr_api.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_api.cpp 16574 2009-03-14 13:09:10Z rouault $ + * $Id: ogr_api.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: C API Functions that don't correspond one-to-one with C++ @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -48,6 +49,8 @@ int OGR_G_GetPointCount( OGRGeometryH hGeom ) { + VALIDATE_POINTER1( hGeom, "OGR_G_GetPointCount", 0 ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -66,6 +69,38 @@ int OGR_G_GetPointCount( OGRGeometryH hGeom ) } } +/************************************************************************/ +/* OGR_G_SetPointCount() */ +/************************************************************************/ +/** + * \brief Set number of points in a geometry. + * + * This method primary exists to preset the number of points in a linestring + * geometry before setPoint() is used to assign them to avoid reallocating + * the array larger with each call to addPoint(). + * + * @param nNewPointCount the new number of points for geometry. + */ + +void OGR_G_SetPointCount( OGRGeometryH hGeom, int nNewPointCount ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetPointCount" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbLineString: + { + OGRLineString *poLine = (OGRLineString *) hGeom; + poLine->setNumPoints( nNewPointCount ); + break; + } + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + /************************************************************************/ /* OGR_G_GetX() */ /************************************************************************/ @@ -80,6 +115,8 @@ int OGR_G_GetPointCount( OGRGeometryH hGeom ) double OGR_G_GetX( OGRGeometryH hGeom, int i ) { + VALIDATE_POINTER1( hGeom, "OGR_G_GetX", 0 ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -94,7 +131,15 @@ double OGR_G_GetX( OGRGeometryH hGeom, int i ) } case wkbLineString: - return ((OGRLineString *) hGeom)->getX( i ); + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return 0.0; + } + return poLS->getX( i ); + } default: CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); @@ -116,6 +161,8 @@ double OGR_G_GetX( OGRGeometryH hGeom, int i ) double OGR_G_GetY( OGRGeometryH hGeom, int i ) { + VALIDATE_POINTER1( hGeom, "OGR_G_GetY", 0 ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -130,7 +177,15 @@ double OGR_G_GetY( OGRGeometryH hGeom, int i ) } case wkbLineString: - return ((OGRLineString *) hGeom)->getY( i ); + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return 0.0; + } + return poLS->getY( i ); + } default: CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); @@ -152,6 +207,8 @@ double OGR_G_GetY( OGRGeometryH hGeom, int i ) double OGR_G_GetZ( OGRGeometryH hGeom, int i ) { + VALIDATE_POINTER1( hGeom, "OGR_G_GetZ", 0 ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -166,7 +223,15 @@ double OGR_G_GetZ( OGRGeometryH hGeom, int i ) } case wkbLineString: - return ((OGRLineString *) hGeom)->getZ( i ); + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return 0.0; + } + return poLS->getZ( i ); + } default: CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); @@ -174,6 +239,65 @@ double OGR_G_GetZ( OGRGeometryH hGeom, int i ) } } +/************************************************************************/ +/* OGR_G_GetPoints() */ +/************************************************************************/ + +/** + * \brief Returns all points of line string. + * + * This method copies all points into user arrays. The user provides the + * stride between 2 consecutives elements of the array. + * + * On some CPU architectures, care must be taken so that the arrays are properly aligned. + * + * @param hGeom handle to the geometry from which to get the coordinates. + * @param pabyX a buffer of at least (sizeof(double) * nXStride * nPointCount) bytes, may be NULL. + * @param nXStride the number of bytes between 2 elements of pabyX. + * @param pabyY a buffer of at least (sizeof(double) * nYStride * nPointCount) bytes, may be NULL. + * @param nYStride the number of bytes between 2 elements of pabyY. + * @param pabyZ a buffer of at last size (sizeof(double) * nZStride * nPointCount) bytes, may be NULL. + * @param nZStride the number of bytes between 2 elements of pabyZ. + * + * @return the number of points + * + * @since OGR 1.9.0 + */ + +int OGR_G_GetPoints( OGRGeometryH hGeom, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride) +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetPoints", 0 ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if (pabyX) *((double*)pabyX) = ((OGRPoint *)hGeom)->getX(); + if (pabyY) *((double*)pabyY) = ((OGRPoint *)hGeom)->getY(); + if (pabyZ) *((double*)pabyZ) = ((OGRPoint *)hGeom)->getZ(); + return 1; + } + break; + + case wkbLineString: + { + OGRLineString* poLS = (OGRLineString *) hGeom; + poLS->getPoints(pabyX, nXStride, pabyY, nYStride, pabyZ, nZStride); + return poLS->getNumPoints(); + } + break; + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0; + break; + } +} + + /************************************************************************/ /* OGR_G_GetPoint() */ /************************************************************************/ @@ -192,6 +316,8 @@ void OGR_G_GetPoint( OGRGeometryH hGeom, int i, double *pdfX, double *pdfY, double *pdfZ ) { + VALIDATE_POINTER0( hGeom, "OGR_G_GetPoint" ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -212,10 +338,21 @@ void OGR_G_GetPoint( OGRGeometryH hGeom, int i, case wkbLineString: { - *pdfX = ((OGRLineString *) hGeom)->getX( i ); - *pdfY = ((OGRLineString *) hGeom)->getY( i ); - if( pdfZ != NULL ) - *pdfZ = ((OGRLineString *) hGeom)->getZ( i ); + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + *pdfX = *pdfY = 0; + if( pdfZ != NULL ) + *pdfZ = 0; + } + else + { + *pdfX = poLS->getX( i ); + *pdfY = poLS->getY( i ); + if( pdfZ != NULL ) + *pdfZ = poLS->getZ( i ); + } } break; @@ -225,6 +362,73 @@ void OGR_G_GetPoint( OGRGeometryH hGeom, int i, } } +/************************************************************************/ +/* OGR_G_SetPoint() */ +/************************************************************************/ +/** + * \brief Assign all points in a point or a line string geometry. + * + * This method clear any existing points assigned to this geometry, + * and assigns a whole new set. + * + * @param hGeom handle to the geometry to set the coordinates. + * @param nPointsIn number of points being passed in padfX and padfY. + * @param padfX list of X coordinates of points being assigned. + * @param nXStride the number of bytes between 2 elements of pabyX. + * @param padfY list of Y coordinates of points being assigned. + * @param nYStride the number of bytes between 2 elements of pabyY. + * @param padfZ list of Z coordinates of points being assigned (defaults to NULL for 2D objects). + * @param nZStride the number of bytes between 2 elements of pabyZ. + */ + +void CPL_DLL OGR_G_SetPoints( OGRGeometryH hGeom, int nPointsIn, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetPoints" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + ((OGRPoint *) hGeom)->setX( pabyX ? *( (double *)pabyX ) : 0.0 ); + ((OGRPoint *) hGeom)->setY( pabyY ? *( (double *)pabyY ) : 0.0 ); + ((OGRPoint *) hGeom)->setZ( pabyZ ? *( (double *)pabyZ ) : 0.0 ); + break; + } + case wkbLineString: + { + OGRLineString* poLine = (OGRLineString *) hGeom; + + if( nXStride == 0 && nYStride == 0 && nZStride == 0 ) + { + poLine->setPoints( nPointsIn, (double *)pabyX, (double *)pabyY, (double *)pabyZ ); + } + else + { + double x, y, z; + x = y = z = 0; + poLine->setNumPoints( nPointsIn ); + + for (int i = 0; i < nPointsIn; ++i) + { + if( pabyX ) x = *(double*)((char*)pabyX + i * nXStride); + if( pabyY ) y = *(double*)((char*)pabyY + i * nYStride); + if( pabyZ ) z = *(double*)((char*)pabyZ + i * nZStride); + + poLine->setPoint( i, x, y, z ); + } + } + break; + } + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + /************************************************************************/ /* OGR_G_SetPoint() */ /************************************************************************/ @@ -247,6 +451,8 @@ void OGR_G_SetPoint( OGRGeometryH hGeom, int i, double dfX, double dfY, double dfZ ) { + VALIDATE_POINTER0( hGeom, "OGR_G_SetPoint" ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -265,8 +471,15 @@ void OGR_G_SetPoint( OGRGeometryH hGeom, int i, break; case wkbLineString: - ((OGRLineString *) hGeom)->setPoint( i, dfX, dfY, dfZ ); - break; + { + if (i < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return; + } + ((OGRLineString *) hGeom)->setPoint( i, dfX, dfY, dfZ ); + break; + } default: CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); @@ -295,6 +508,8 @@ void OGR_G_SetPoint_2D( OGRGeometryH hGeom, int i, double dfX, double dfY ) { + VALIDATE_POINTER0( hGeom, "OGR_G_SetPoint_2D" ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -312,8 +527,15 @@ void OGR_G_SetPoint_2D( OGRGeometryH hGeom, int i, break; case wkbLineString: - ((OGRLineString *) hGeom)->setPoint( i, dfX, dfY ); - break; + { + if (i < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return; + } + ((OGRLineString *) hGeom)->setPoint( i, dfX, dfY ); + break; + } default: CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); @@ -340,6 +562,8 @@ void OGR_G_AddPoint( OGRGeometryH hGeom, double dfX, double dfY, double dfZ ) { + VALIDATE_POINTER0( hGeom, "OGR_G_AddPoint" ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -378,6 +602,8 @@ void OGR_G_AddPoint_2D( OGRGeometryH hGeom, double dfX, double dfY ) { + VALIDATE_POINTER0( hGeom, "OGR_G_AddPoint_2D" ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPoint: @@ -407,6 +633,8 @@ void OGR_G_AddPoint_2D( OGRGeometryH hGeom, * wkbMultiPolygon[25D] or wkbGeometryCollection[25D] may return a valid value. * Other geometry types will silently return 0. * + * For a polygon, the returned number is the number of rings (exterior ring + interior rings). + * * @param hGeom single geometry or geometry container from which to get * the number of elements. * @return the number of elements. @@ -415,6 +643,8 @@ void OGR_G_AddPoint_2D( OGRGeometryH hGeom, int OGR_G_GetGeometryCount( OGRGeometryH hGeom ) { + VALIDATE_POINTER1( hGeom, "OGR_G_GetGeometryCount", 0 ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPolygon: @@ -454,6 +684,9 @@ int OGR_G_GetGeometryCount( OGRGeometryH hGeom ) * This function is the same as the CPP method * OGRGeometryCollection::getGeometryRef(). * + * For a polygon, OGR_G_GetGeometryRef(iSubGeom) returns the exterior ring + * if iSubGeom == 0, and the interior rings for iSubGeom > 0. + * * @param hGeom handle to the geometry container from which to get a * geometry from. * @param iSubGeom the index of the geometry to fetch, between 0 and @@ -464,6 +697,8 @@ int OGR_G_GetGeometryCount( OGRGeometryH hGeom ) OGRGeometryH OGR_G_GetGeometryRef( OGRGeometryH hGeom, int iSubGeom ) { + VALIDATE_POINTER1( hGeom, "OGR_G_GetGeometryRef", NULL ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPolygon: @@ -503,6 +738,10 @@ OGRGeometryH OGR_G_GetGeometryRef( OGRGeometryH hGeom, int iSubGeom ) * This function is the same as the CPP method * OGRGeometryCollection::addGeometry. * + * For a polygon, hNewSubGeom must be a linearring. If the polygon is empty, + * the first added subgeometry will be the exterior ring. The next ones will be + * the interior rings. + * * @param hGeom existing geometry container. * @param hNewSubGeom geometry to add to the container. * @@ -513,18 +752,20 @@ OGRGeometryH OGR_G_GetGeometryRef( OGRGeometryH hGeom, int iSubGeom ) OGRErr OGR_G_AddGeometry( OGRGeometryH hGeom, OGRGeometryH hNewSubGeom ) { + VALIDATE_POINTER1( hGeom, "OGR_G_AddGeometry", OGRERR_UNSUPPORTED_OPERATION ); + VALIDATE_POINTER1( hNewSubGeom, "OGR_G_AddGeometry", OGRERR_UNSUPPORTED_OPERATION ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPolygon: { - OGRLinearRing *poRing = (OGRLinearRing *) hNewSubGeom; - - if( poRing->WkbSize() != 0 - || wkbFlatten(poRing->getGeometryType()) != wkbLineString ) + if( !EQUAL( ((OGRGeometry*) hNewSubGeom)->getGeometryName(), "LINEARRING" ) ) + { return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } else { - ((OGRPolygon *)hGeom)->addRing( poRing ); + ((OGRPolygon *)hGeom)->addRing( (OGRLinearRing *) hNewSubGeom ); return OGRERR_NONE; } } @@ -557,6 +798,10 @@ OGRErr OGR_G_AddGeometry( OGRGeometryH hGeom, OGRGeometryH hNewSubGeom ) * * There is no SFCOM analog to this method. * + * For a polygon, hNewSubGeom must be a linearring. If the polygon is empty, + * the first added subgeometry will be the exterior ring. The next ones will be + * the interior rings. + * * @param hGeom existing geometry. * @param hNewSubGeom geometry to add to the existing geometry. * @@ -568,18 +813,20 @@ OGRErr OGR_G_AddGeometryDirectly( OGRGeometryH hGeom, OGRGeometryH hNewSubGeom ) { + VALIDATE_POINTER1( hGeom, "OGR_G_AddGeometryDirectly", OGRERR_UNSUPPORTED_OPERATION ); + VALIDATE_POINTER1( hNewSubGeom, "OGR_G_AddGeometryDirectly", OGRERR_UNSUPPORTED_OPERATION ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPolygon: { - OGRLinearRing *poRing = (OGRLinearRing *) hNewSubGeom; - - if( poRing->WkbSize() != 0 - || wkbFlatten(poRing->getGeometryType()) != wkbLineString ) + if( !EQUAL( ((OGRGeometry*) hNewSubGeom)->getGeometryName(), "LINEARRING" ) ) + { return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } else { - ((OGRPolygon *)hGeom)->addRingDirectly( poRing ); + ((OGRPolygon *)hGeom)->addRingDirectly( (OGRLinearRing *) hNewSubGeom ); return OGRERR_NONE; } } @@ -628,6 +875,8 @@ OGRErr OGR_G_AddGeometryDirectly( OGRGeometryH hGeom, OGRErr OGR_G_RemoveGeometry( OGRGeometryH hGeom, int iGeom, int bDelete ) { + VALIDATE_POINTER1( hGeom, "OGR_G_RemoveGeometry", 0 ); + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) { case wkbPolygon: @@ -649,7 +898,54 @@ OGRErr OGR_G_RemoveGeometry( OGRGeometryH hGeom, int iGeom, int bDelete ) } /************************************************************************/ -/* OGR_G_GetArea() */ +/* OGR_G_Length() */ +/************************************************************************/ + +/** + * \brief Compute length of a geometry. + * + * Computes the area for OGRCurve or MultiCurve objects. + * Undefined for all other geometry types (returns zero). + * + * This function utilizes the C++ get_Length() method. + * + * @param hGeom the geometry to operate on. + * @return the lenght or 0.0 for unsupported geometry types. + * + * @since OGR 1.8.0 + */ + +double OGR_G_Length( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetLength", 0 ); + + double fLength = 0.0; + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbLinearRing: + case wkbLineString: + fLength = ((OGRCurve *) hGeom)->get_Length(); + break; + + case wkbMultiLineString: + case wkbGeometryCollection: + fLength = ((OGRGeometryCollection *) hGeom)->get_Length(); + break; + + default: + CPLError( CE_Warning, CPLE_AppDefined, + "OGR_G_Length() called against a non-curve geometry type." ); + + fLength = 0.0; + } + + return fLength; +} + +/************************************************************************/ +/* OGR_G_Area() */ /************************************************************************/ /** @@ -663,12 +959,14 @@ OGRErr OGR_G_RemoveGeometry( OGRGeometryH hGeom, int iGeom, int bDelete ) * * @param hGeom the geometry to operate on. * @return the area or 0.0 for unsupported geometry types. + * + * @since OGR 1.8.0 */ -double OGR_G_GetArea( OGRGeometryH hGeom ) +double OGR_G_Area( OGRGeometryH hGeom ) { - VALIDATE_POINTER1( hGeom, "OGR_G_GetArea", 0 ); + VALIDATE_POINTER1( hGeom, "OGR_G_Area", 0 ); double fArea = 0.0; @@ -699,7 +997,7 @@ double OGR_G_GetArea( OGRGeometryH hGeom ) default: CPLError( CE_Warning, CPLE_AppDefined, - "OGR_G_GetArea() called against non-surface geometry type." ); + "OGR_G_Area() called against non-surface geometry type." ); fArea = 0.0; } @@ -707,3 +1005,35 @@ double OGR_G_GetArea( OGRGeometryH hGeom ) return fArea; } +/** + * \brief Compute geometry area (deprecated) + * + * @deprecated + * @see OGR_G_Area() + */ +double OGR_G_GetArea( OGRGeometryH hGeom ) + +{ + return OGR_G_Area( hGeom ); +} + +#ifndef OGR_ENABLED +OGRGeometryH OGR_G_CreateGeometryFromJson( const char* ) +{ + return NULL; +} + +char* OGR_G_ExportToKML( OGRGeometryH, const char* pszAltitudeMode ) +{ + return NULL; +} + +char* OGR_G_ExportToJson( OGRGeometryH ) +{ + return NULL; +} +char* OGR_G_ExportToJsonEx( OGRGeometryH, char** papszOptions ) +{ + return NULL; +} +#endif diff --git a/ogr/ogr_api.h b/ogr/ogr_api.h index 0c12026..73a1153 100644 --- a/ogr/ogr_api.h +++ b/ogr/ogr_api.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_api.h 18226 2009-12-09 09:30:48Z chaitanya $ + * $Id: ogr_api.h 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: C API for OGR Geometry, Feature, Layers, DataSource and drivers. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -39,6 +40,8 @@ * See also: ogr_geometry.h, ogr_feature.h, ogrsf_frmts.h, ogr_featurestyle.h */ +#include "cpl_progress.h" +#include "cpl_minixml.h" #include "ogr_core.h" CPL_C_START @@ -84,11 +87,18 @@ OGR_G_ApproximateArcAngles( double dfStartAngle, double dfEndAngle, double dfMaxAngleStepSizeDegrees ); +OGRGeometryH CPL_DLL OGR_G_ForceToPolygon( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToLineString( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToMultiPolygon( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToMultiPoint( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToMultiLineString( OGRGeometryH ); + int CPL_DLL OGR_G_GetDimension( OGRGeometryH ); int CPL_DLL OGR_G_GetCoordinateDimension( OGRGeometryH ); void CPL_DLL OGR_G_SetCoordinateDimension( OGRGeometryH, int ); OGRGeometryH CPL_DLL OGR_G_Clone( OGRGeometryH ); void CPL_DLL OGR_G_GetEnvelope( OGRGeometryH, OGREnvelope * ); +void CPL_DLL OGR_G_GetEnvelope3D( OGRGeometryH, OGREnvelope3D * ); OGRErr CPL_DLL OGR_G_ImportFromWkb( OGRGeometryH, unsigned char *, int ); OGRErr CPL_DLL OGR_G_ExportToWkb( OGRGeometryH, OGRwkbByteOrder, unsigned char*); int CPL_DLL OGR_G_WkbSize( OGRGeometryH hGeom ); @@ -102,16 +112,16 @@ void CPL_DLL OGR_G_CloseRings( OGRGeometryH ); OGRGeometryH CPL_DLL OGR_G_CreateFromGML( const char * ); char CPL_DLL *OGR_G_ExportToGML( OGRGeometryH ); +char CPL_DLL *OGR_G_ExportToGMLEx( OGRGeometryH, char** papszOptions ); -#if defined(_CPL_MINIXML_H_INCLUDED) OGRGeometryH CPL_DLL OGR_G_CreateFromGMLTree( const CPLXMLNode * ); CPLXMLNode CPL_DLL *OGR_G_ExportToGMLTree( OGRGeometryH ); CPLXMLNode CPL_DLL *OGR_G_ExportEnvelopeToGMLTree( OGRGeometryH ); -#endif char CPL_DLL *OGR_G_ExportToKML( OGRGeometryH, const char* pszAltitudeMode ); char CPL_DLL *OGR_G_ExportToJson( OGRGeometryH ); +char CPL_DLL *OGR_G_ExportToJsonEx( OGRGeometryH, char** papszOptions ); OGRGeometryH CPL_DLL OGR_G_CreateGeometryFromJson( const char* ); void CPL_DLL OGR_G_AssignSpatialReference( OGRGeometryH, @@ -119,9 +129,14 @@ void CPL_DLL OGR_G_AssignSpatialReference( OGRGeometryH, OGRSpatialReferenceH CPL_DLL OGR_G_GetSpatialReference( OGRGeometryH ); OGRErr CPL_DLL OGR_G_Transform( OGRGeometryH, OGRCoordinateTransformationH ); OGRErr CPL_DLL OGR_G_TransformTo( OGRGeometryH, OGRSpatialReferenceH ); + +OGRGeometryH CPL_DLL OGR_G_Simplify( OGRGeometryH hThis, double tolerance ); +OGRGeometryH CPL_DLL OGR_G_SimplifyPreserveTopology( OGRGeometryH hThis, double tolerance ); + void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength ); int CPL_DLL OGR_G_Intersects( OGRGeometryH, OGRGeometryH ); int CPL_DLL OGR_G_Equals( OGRGeometryH, OGRGeometryH ); +/*int CPL_DLL OGR_G_EqualsExact( OGRGeometryH, OGRGeometryH, double );*/ int CPL_DLL OGR_G_Disjoint( OGRGeometryH, OGRGeometryH ); int CPL_DLL OGR_G_Touches( OGRGeometryH, OGRGeometryH ); int CPL_DLL OGR_G_Crosses( OGRGeometryH, OGRGeometryH ); @@ -129,41 +144,62 @@ int CPL_DLL OGR_G_Within( OGRGeometryH, OGRGeometryH ); int CPL_DLL OGR_G_Contains( OGRGeometryH, OGRGeometryH ); int CPL_DLL OGR_G_Overlaps( OGRGeometryH, OGRGeometryH ); -OGRGeometryH CPL_DLL OGR_G_GetBoundary( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_Boundary( OGRGeometryH ); OGRGeometryH CPL_DLL OGR_G_ConvexHull( OGRGeometryH ); OGRGeometryH CPL_DLL OGR_G_Buffer( OGRGeometryH, double, int ); OGRGeometryH CPL_DLL OGR_G_Intersection( OGRGeometryH, OGRGeometryH ); OGRGeometryH CPL_DLL OGR_G_Union( OGRGeometryH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_UnionCascaded( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_PointOnSurface( OGRGeometryH ); +/*OGRGeometryH CPL_DLL OGR_G_Polygonize( OGRGeometryH *, int);*/ +/*OGRGeometryH CPL_DLL OGR_G_Polygonizer_getCutEdges( OGRGeometryH *, int);*/ +/*OGRGeometryH CPL_DLL OGR_G_LineMerge( OGRGeometryH );*/ + OGRGeometryH CPL_DLL OGR_G_Difference( OGRGeometryH, OGRGeometryH ); -OGRGeometryH CPL_DLL OGR_G_SymmetricDifference( OGRGeometryH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_SymDifference( OGRGeometryH, OGRGeometryH ); double CPL_DLL OGR_G_Distance( OGRGeometryH, OGRGeometryH ); - -double CPL_DLL OGR_G_GetArea( OGRGeometryH ); +double CPL_DLL OGR_G_Length( OGRGeometryH ); +double CPL_DLL OGR_G_Area( OGRGeometryH ); int CPL_DLL OGR_G_Centroid( OGRGeometryH, OGRGeometryH ); void CPL_DLL OGR_G_Empty( OGRGeometryH ); -int CPL_DLL OGR_G_IsEmpty (OGRGeometryH ); -int CPL_DLL OGR_G_IsValid (OGRGeometryH ); -int CPL_DLL OGR_G_IsSimple (OGRGeometryH ); -int CPL_DLL OGR_G_IsRing (OGRGeometryH ); - -/* backward compatibility */ -int CPL_DLL OGR_G_Intersect( OGRGeometryH, OGRGeometryH ); -int CPL_DLL OGR_G_Equal( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_IsEmpty( OGRGeometryH ); +int CPL_DLL OGR_G_IsValid( OGRGeometryH ); +/*char CPL_DLL *OGR_G_IsValidReason( OGRGeometryH );*/ +int CPL_DLL OGR_G_IsSimple( OGRGeometryH ); +int CPL_DLL OGR_G_IsRing( OGRGeometryH ); + +OGRGeometryH CPL_DLL OGR_G_Polygonize( OGRGeometryH ); + +/* backward compatibility (non-standard methods) */ +int CPL_DLL OGR_G_Intersect( OGRGeometryH, OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Intersects() instead"); +int CPL_DLL OGR_G_Equal( OGRGeometryH, OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Equals() instead"); +OGRGeometryH CPL_DLL OGR_G_SymmetricDifference( OGRGeometryH, OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_SymDifference() instead"); +double CPL_DLL OGR_G_GetArea( OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Area() instead"); +OGRGeometryH CPL_DLL OGR_G_GetBoundary( OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Boundary() instead"); /* Methods for getting/setting vertices in points, line strings and rings */ int CPL_DLL OGR_G_GetPointCount( OGRGeometryH ); +int CPL_DLL OGR_G_GetPoints( OGRGeometryH hGeom, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride); double CPL_DLL OGR_G_GetX( OGRGeometryH, int ); double CPL_DLL OGR_G_GetY( OGRGeometryH, int ); double CPL_DLL OGR_G_GetZ( OGRGeometryH, int ); void CPL_DLL OGR_G_GetPoint( OGRGeometryH, int iPoint, double *, double *, double * ); +void CPL_DLL OGR_G_SetPointCount( OGRGeometryH hGeom, int nNewPointCount ); void CPL_DLL OGR_G_SetPoint( OGRGeometryH, int iPoint, double, double, double ); void CPL_DLL OGR_G_SetPoint_2D( OGRGeometryH, int iPoint, double, double ); void CPL_DLL OGR_G_AddPoint( OGRGeometryH, double, double, double ); void CPL_DLL OGR_G_AddPoint_2D( OGRGeometryH, double, double ); +void CPL_DLL OGR_G_SetPoints( OGRGeometryH hGeom, int nPointsIn, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride ); /* Methods for getting/setting rings and members collections */ @@ -199,10 +235,11 @@ typedef void *OGRFeatureDefnH; typedef void *OGRFeatureH; typedef void *OGRStyleTableH; #endif +typedef struct OGRGeomFieldDefnHS *OGRGeomFieldDefnH; /* OGRFieldDefn */ -OGRFieldDefnH CPL_DLL OGR_Fld_Create( const char *, OGRFieldType ); +OGRFieldDefnH CPL_DLL OGR_Fld_Create( const char *, OGRFieldType ) CPL_WARN_UNUSED_RESULT; void CPL_DLL OGR_Fld_Destroy( OGRFieldDefnH ); void CPL_DLL OGR_Fld_SetName( OGRFieldDefnH, const char * ); @@ -217,12 +254,32 @@ int CPL_DLL OGR_Fld_GetPrecision( OGRFieldDefnH ); void CPL_DLL OGR_Fld_SetPrecision( OGRFieldDefnH, int ); void CPL_DLL OGR_Fld_Set( OGRFieldDefnH, const char *, OGRFieldType, int, int, OGRJustification ); +int CPL_DLL OGR_Fld_IsIgnored( OGRFieldDefnH hDefn ); +void CPL_DLL OGR_Fld_SetIgnored( OGRFieldDefnH hDefn, int ); const char CPL_DLL *OGR_GetFieldTypeName( OGRFieldType ); +/* OGRGeomFieldDefnH */ + +OGRGeomFieldDefnH CPL_DLL OGR_GFld_Create( const char *, OGRwkbGeometryType ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_GFld_Destroy( OGRGeomFieldDefnH ); + +void CPL_DLL OGR_GFld_SetName( OGRGeomFieldDefnH, const char * ); +const char CPL_DLL *OGR_GFld_GetNameRef( OGRGeomFieldDefnH ); + +OGRwkbGeometryType CPL_DLL OGR_GFld_GetType( OGRGeomFieldDefnH ); +void CPL_DLL OGR_GFld_SetType( OGRGeomFieldDefnH, OGRwkbGeometryType ); + +OGRSpatialReferenceH CPL_DLL OGR_GFld_GetSpatialRef( OGRGeomFieldDefnH ); +void CPL_DLL OGR_GFld_SetSpatialRef( OGRGeomFieldDefnH, + OGRSpatialReferenceH hSRS ); + +int CPL_DLL OGR_GFld_IsIgnored( OGRGeomFieldDefnH hDefn ); +void CPL_DLL OGR_GFld_SetIgnored( OGRGeomFieldDefnH hDefn, int ); + /* OGRFeatureDefn */ -OGRFeatureDefnH CPL_DLL OGR_FD_Create( const char * ); +OGRFeatureDefnH CPL_DLL OGR_FD_Create( const char * ) CPL_WARN_UNUSED_RESULT; void CPL_DLL OGR_FD_Destroy( OGRFeatureDefnH ); void CPL_DLL OGR_FD_Release( OGRFeatureDefnH ); const char CPL_DLL *OGR_FD_GetName( OGRFeatureDefnH ); @@ -230,21 +287,40 @@ int CPL_DLL OGR_FD_GetFieldCount( OGRFeatureDefnH ); OGRFieldDefnH CPL_DLL OGR_FD_GetFieldDefn( OGRFeatureDefnH, int ); int CPL_DLL OGR_FD_GetFieldIndex( OGRFeatureDefnH, const char * ); void CPL_DLL OGR_FD_AddFieldDefn( OGRFeatureDefnH, OGRFieldDefnH ); +OGRErr CPL_DLL OGR_FD_DeleteFieldDefn( OGRFeatureDefnH hDefn, int iField ); +OGRErr CPL_DLL OGR_FD_ReorderFieldDefns( OGRFeatureDefnH hDefn, int* panMap ); OGRwkbGeometryType CPL_DLL OGR_FD_GetGeomType( OGRFeatureDefnH ); void CPL_DLL OGR_FD_SetGeomType( OGRFeatureDefnH, OGRwkbGeometryType ); +int CPL_DLL OGR_FD_IsGeometryIgnored( OGRFeatureDefnH ); +void CPL_DLL OGR_FD_SetGeometryIgnored( OGRFeatureDefnH, int ); +int CPL_DLL OGR_FD_IsStyleIgnored( OGRFeatureDefnH ); +void CPL_DLL OGR_FD_SetStyleIgnored( OGRFeatureDefnH, int ); int CPL_DLL OGR_FD_Reference( OGRFeatureDefnH ); int CPL_DLL OGR_FD_Dereference( OGRFeatureDefnH ); int CPL_DLL OGR_FD_GetReferenceCount( OGRFeatureDefnH ); +int CPL_DLL OGR_FD_GetGeomFieldCount( OGRFeatureDefnH hFDefn ); +OGRGeomFieldDefnH CPL_DLL OGR_FD_GetGeomFieldDefn( OGRFeatureDefnH hFDefn, + int i ); +int CPL_DLL OGR_FD_GetGeomFieldIndex( OGRFeatureDefnH hFDefn, + const char *pszName); + +void CPL_DLL OGR_FD_AddGeomFieldDefn( OGRFeatureDefnH hFDefn, + OGRGeomFieldDefnH hGFldDefn); +OGRErr CPL_DLL OGR_FD_DeleteGeomFieldDefn( OGRFeatureDefnH hFDefn, + int iGeomField ); +int CPL_DLL OGR_FD_IsSame( OGRFeatureDefnH hFDefn, + OGRFeatureDefnH hOtherFDefn ); /* OGRFeature */ -OGRFeatureH CPL_DLL OGR_F_Create( OGRFeatureDefnH ); +OGRFeatureH CPL_DLL OGR_F_Create( OGRFeatureDefnH ) CPL_WARN_UNUSED_RESULT; void CPL_DLL OGR_F_Destroy( OGRFeatureH ); OGRFeatureDefnH CPL_DLL OGR_F_GetDefnRef( OGRFeatureH ); OGRErr CPL_DLL OGR_F_SetGeometryDirectly( OGRFeatureH, OGRGeometryH ); OGRErr CPL_DLL OGR_F_SetGeometry( OGRFeatureH, OGRGeometryH ); OGRGeometryH CPL_DLL OGR_F_GetGeometryRef( OGRFeatureH ); +OGRGeometryH CPL_DLL OGR_F_StealGeometry( OGRFeatureH ); OGRFeatureH CPL_DLL OGR_F_Clone( OGRFeatureH ); int CPL_DLL OGR_F_Equal( OGRFeatureH, OGRFeatureH ); @@ -277,6 +353,20 @@ void CPL_DLL OGR_F_SetFieldBinary( OGRFeatureH, int, int, GByte * ); void CPL_DLL OGR_F_SetFieldDateTime( OGRFeatureH, int, int, int, int, int, int, int, int ); +int CPL_DLL OGR_F_GetGeomFieldCount( OGRFeatureH hFeat ); +OGRGeomFieldDefnH CPL_DLL OGR_F_GetGeomFieldDefnRef( OGRFeatureH hFeat, + int iField ); +int CPL_DLL OGR_F_GetGeomFieldIndex( OGRFeatureH hFeat, + const char *pszName); + +OGRGeometryH CPL_DLL OGR_F_GetGeomFieldRef( OGRFeatureH hFeat, + int iField ); +OGRErr CPL_DLL OGR_F_SetGeomFieldDirectly( OGRFeatureH hFeat, + int iField, + OGRGeometryH hGeom ); +OGRErr CPL_DLL OGR_F_SetGeomField( OGRFeatureH hFeat, + int iField, OGRGeometryH hGeom ); + long CPL_DLL OGR_F_GetFID( OGRFeatureH ); OGRErr CPL_DLL OGR_F_SetFID( OGRFeatureH, long ); void CPL_DLL OGR_F_DumpReadable( OGRFeatureH, FILE * ); @@ -306,10 +396,17 @@ typedef void *OGRSFDriverH; /* OGRLayer */ +const char CPL_DLL* OGR_L_GetName( OGRLayerH ); +OGRwkbGeometryType CPL_DLL OGR_L_GetGeomType( OGRLayerH ); OGRGeometryH CPL_DLL OGR_L_GetSpatialFilter( OGRLayerH ); void CPL_DLL OGR_L_SetSpatialFilter( OGRLayerH, OGRGeometryH ); void CPL_DLL OGR_L_SetSpatialFilterRect( OGRLayerH, double, double, double, double ); +void CPL_DLL OGR_L_SetSpatialFilterEx( OGRLayerH, int iGeomField, + OGRGeometryH hGeom ); +void CPL_DLL OGR_L_SetSpatialFilterRectEx( OGRLayerH, int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); OGRErr CPL_DLL OGR_L_SetAttributeFilter( OGRLayerH, const char * ); void CPL_DLL OGR_L_ResetReading( OGRLayerH ); OGRFeatureH CPL_DLL OGR_L_GetNextFeature( OGRLayerH ); @@ -320,10 +417,19 @@ OGRErr CPL_DLL OGR_L_CreateFeature( OGRLayerH, OGRFeatureH ); OGRErr CPL_DLL OGR_L_DeleteFeature( OGRLayerH, long ); OGRFeatureDefnH CPL_DLL OGR_L_GetLayerDefn( OGRLayerH ); OGRSpatialReferenceH CPL_DLL OGR_L_GetSpatialRef( OGRLayerH ); +int CPL_DLL OGR_L_FindFieldIndex( OGRLayerH, const char *, int bExactMatch ); int CPL_DLL OGR_L_GetFeatureCount( OGRLayerH, int ); OGRErr CPL_DLL OGR_L_GetExtent( OGRLayerH, OGREnvelope *, int ); +OGRErr CPL_DLL OGR_L_GetExtentEx( OGRLayerH, int iGeomField, + OGREnvelope *psExtent, int bForce ); int CPL_DLL OGR_L_TestCapability( OGRLayerH, const char * ); OGRErr CPL_DLL OGR_L_CreateField( OGRLayerH, OGRFieldDefnH, int ); +OGRErr CPL_DLL OGR_L_CreateGeomField( OGRLayerH hLayer, + OGRGeomFieldDefnH hFieldDefn, int bForce ); +OGRErr CPL_DLL OGR_L_DeleteField( OGRLayerH, int iField ); +OGRErr CPL_DLL OGR_L_ReorderFields( OGRLayerH, int* panMap ); +OGRErr CPL_DLL OGR_L_ReorderField( OGRLayerH, int iOldFieldPos, int iNewFieldPos ); +OGRErr CPL_DLL OGR_L_AlterFieldDefn( OGRLayerH, int iField, OGRFieldDefnH hNewFieldDefn, int nFlags ); OGRErr CPL_DLL OGR_L_StartTransaction( OGRLayerH ); OGRErr CPL_DLL OGR_L_CommitTransaction( OGRLayerH ); OGRErr CPL_DLL OGR_L_RollbackTransaction( OGRLayerH ); @@ -337,6 +443,14 @@ const char CPL_DLL *OGR_L_GetGeometryColumn( OGRLayerH ); OGRStyleTableH CPL_DLL OGR_L_GetStyleTable( OGRLayerH ); void CPL_DLL OGR_L_SetStyleTableDirectly( OGRLayerH, OGRStyleTableH ); void CPL_DLL OGR_L_SetStyleTable( OGRLayerH, OGRStyleTableH ); +OGRErr CPL_DLL OGR_L_SetIgnoredFields( OGRLayerH, const char** ); +OGRErr CPL_DLL OGR_L_Intersection( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Union( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_SymDifference( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Identity( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Update( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Clip( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Erase( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); /* OGRDataSource */ @@ -368,20 +482,21 @@ void CPL_DLL OGR_DS_SetStyleTable( OGRDataSourceH, OGRStyleTableH ); /* OGRSFDriver */ const char CPL_DLL *OGR_Dr_GetName( OGRSFDriverH ); -OGRDataSourceH CPL_DLL OGR_Dr_Open( OGRSFDriverH, const char *, int ); +OGRDataSourceH CPL_DLL OGR_Dr_Open( OGRSFDriverH, const char *, int ) CPL_WARN_UNUSED_RESULT; int CPL_DLL OGR_Dr_TestCapability( OGRSFDriverH, const char * ); OGRDataSourceH CPL_DLL OGR_Dr_CreateDataSource( OGRSFDriverH, const char *, - char ** ); + char ** ) CPL_WARN_UNUSED_RESULT; OGRDataSourceH CPL_DLL OGR_Dr_CopyDataSource( OGRSFDriverH, OGRDataSourceH, - const char *, char ** ); + const char *, char ** ) CPL_WARN_UNUSED_RESULT; OGRErr CPL_DLL OGR_Dr_DeleteDataSource( OGRSFDriverH, const char * ); /* OGRSFDriverRegistrar */ -OGRDataSourceH CPL_DLL OGROpen( const char *, int, OGRSFDriverH * ); -OGRDataSourceH CPL_DLL OGROpenShared( const char *, int, OGRSFDriverH * ); +OGRDataSourceH CPL_DLL OGROpen( const char *, int, OGRSFDriverH * ) CPL_WARN_UNUSED_RESULT; +OGRDataSourceH CPL_DLL OGROpenShared( const char *, int, OGRSFDriverH * ) CPL_WARN_UNUSED_RESULT; OGRErr CPL_DLL OGRReleaseDataSource( OGRDataSourceH ); void CPL_DLL OGRRegisterDriver( OGRSFDriverH ); +void CPL_DLL OGRDeregisterDriver( OGRSFDriverH ); int CPL_DLL OGRGetDriverCount(void); OGRSFDriverH CPL_DLL OGRGetDriver( int ); OGRSFDriverH CPL_DLL OGRGetDriverByName( const char * ); @@ -407,7 +522,7 @@ typedef void *OGRStyleToolH; /* OGRStyleMgr */ -OGRStyleMgrH CPL_DLL OGR_SM_Create(OGRStyleTableH hStyleTable); +OGRStyleMgrH CPL_DLL OGR_SM_Create(OGRStyleTableH hStyleTable) CPL_WARN_UNUSED_RESULT; void CPL_DLL OGR_SM_Destroy(OGRStyleMgrH hSM); const char CPL_DLL *OGR_SM_InitFromFeature(OGRStyleMgrH hSM, @@ -424,7 +539,7 @@ int CPL_DLL OGR_SM_AddStyle(OGRStyleMgrH hSM, const char *pszStyleName, /* OGRStyleTool */ -OGRStyleToolH CPL_DLL OGR_ST_Create(OGRSTClassId eClassId); +OGRStyleToolH CPL_DLL OGR_ST_Create(OGRSTClassId eClassId) CPL_WARN_UNUSED_RESULT; void CPL_DLL OGR_ST_Destroy(OGRStyleToolH hST); OGRSTClassId CPL_DLL OGR_ST_GetType(OGRStyleToolH hST); @@ -447,8 +562,11 @@ int CPL_DLL OGR_ST_GetRGBFromString(OGRStyleToolH hST, const char *pszColor, /* OGRStyleTable */ -OGRStyleTableH CPL_DLL OGR_STBL_Create( void ); +OGRStyleTableH CPL_DLL OGR_STBL_Create( void ) CPL_WARN_UNUSED_RESULT; void CPL_DLL OGR_STBL_Destroy( OGRStyleTableH hSTBL ); +int CPL_DLL OGR_STBL_AddStyle( OGRStyleTableH hStyleTable, + const char *pszName, + const char *pszStyleString); int CPL_DLL OGR_STBL_SaveStyleTable( OGRStyleTableH hStyleTable, const char *pszFilename ); int CPL_DLL OGR_STBL_LoadStyleTable( OGRStyleTableH hStyleTable, diff --git a/ogr/ogr_attrind.h b/ogr/ogr_attrind.h index 1ef6f28..f509ae2 100644 --- a/ogr/ogr_attrind.h +++ b/ogr/ogr_attrind.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_attrind.h 10645 2007-01-18 02:22:39Z warmerdam $ + * $Id: ogr_attrind.h 20101 2010-07-18 15:20:35Z tamas $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Classes related to generic implementation of attribute indexing. @@ -48,6 +48,7 @@ class CPL_DLL OGRAttrIndex virtual long GetFirstMatch( OGRField *psKey ) = 0; virtual long *GetAllMatches( OGRField *psKey ) = 0; + virtual long *GetAllMatches( OGRField *psKey, long* panFIDList, int* nFIDCount, int* nLength ) = 0; virtual OGRErr AddEntry( OGRField *psKey, long nFID ) = 0; virtual OGRErr RemoveEntry( OGRField *psKey, long nFID ) = 0; diff --git a/ogr/ogr_core.h b/ogr/ogr_core.h index 7709e1c..a0c98fe 100644 --- a/ogr/ogr_core.h +++ b/ogr/ogr_core.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_core.h 17722 2009-10-01 16:40:26Z warmerdam $ + * $Id: ogr_core.h 27058 2014-03-19 22:19:21Z kyle $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Define some core portability services for cross-platform OGR code. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,6 +32,7 @@ #define OGR_CORE_H_INCLUDED #include "cpl_port.h" +#include "gdal_version.h" /** * \file @@ -46,16 +48,31 @@ class CPL_DLL OGREnvelope { public: - OGREnvelope() + OGREnvelope() : MinX(0.0), MaxX(0.0), MinY(0.0), MaxY(0.0) { - MinX = MaxX = MinY = MaxY = 0; } + + OGREnvelope(const OGREnvelope& oOther) : + MinX(oOther.MinX),MaxX(oOther.MaxX), MinY(oOther.MinY), MaxY(oOther.MaxY) + { + } + double MinX; double MaxX; double MinY; double MaxY; +/* See http://trac.osgeo.org/gdal/ticket/5299 for details on this pragma */ +#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && !defined(_MSC_VER)) +#pragma GCC diagnostic ignored "-Wfloat-equal" +#pragma GCC diagnostic push +#endif int IsInit() const { return MinX != 0 || MinY != 0 || MaxX != 0 || MaxY != 0; } + +#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && !defined(_MSC_VER)) +#pragma GCC diagnostic pop +#endif + void Merge( OGREnvelope const& sOther ) { if( IsInit() ) { @@ -136,6 +153,127 @@ typedef struct } OGREnvelope; #endif + +/** + * Simple container for a bounding region in 3D. + */ + +#if defined(__cplusplus) && !defined(CPL_SUPRESS_CPLUSPLUS) +class CPL_DLL OGREnvelope3D : public OGREnvelope +{ + public: + OGREnvelope3D() : OGREnvelope(), MinZ(0.0), MaxZ(0.0) + { + } + + OGREnvelope3D(const OGREnvelope3D& oOther) : + OGREnvelope(oOther), + MinZ(oOther.MinZ), MaxZ(oOther.MaxZ) + { + } + + double MinZ; + double MaxZ; + + int IsInit() const { return MinX != 0 || MinY != 0 || MaxX != 0 || MaxY != 0 || MinZ != 0 || MaxZ != 0; } + void Merge( OGREnvelope3D const& sOther ) { + if( IsInit() ) + { + MinX = MIN(MinX,sOther.MinX); + MaxX = MAX(MaxX,sOther.MaxX); + MinY = MIN(MinY,sOther.MinY); + MaxY = MAX(MaxY,sOther.MaxY); + MinZ = MIN(MinZ,sOther.MinZ); + MaxZ = MAX(MaxZ,sOther.MaxZ); + } + else + { + MinX = sOther.MinX; + MaxX = sOther.MaxX; + MinY = sOther.MinY; + MaxY = sOther.MaxY; + MinZ = sOther.MinZ; + MaxZ = sOther.MaxZ; + } + } + void Merge( double dfX, double dfY, double dfZ ) { + if( IsInit() ) + { + MinX = MIN(MinX,dfX); + MaxX = MAX(MaxX,dfX); + MinY = MIN(MinY,dfY); + MaxY = MAX(MaxY,dfY); + MinZ = MIN(MinZ,dfZ); + MaxZ = MAX(MaxZ,dfZ); + } + else + { + MinX = MaxX = dfX; + MinY = MaxY = dfY; + MinZ = MaxZ = dfZ; + } + } + + void Intersect( OGREnvelope3D const& sOther ) { + if(Intersects(sOther)) + { + if( IsInit() ) + { + MinX = MAX(MinX,sOther.MinX); + MaxX = MIN(MaxX,sOther.MaxX); + MinY = MAX(MinY,sOther.MinY); + MaxY = MIN(MaxY,sOther.MaxY); + MinZ = MAX(MinZ,sOther.MinZ); + MaxZ = MIN(MaxZ,sOther.MaxZ); + } + else + { + MinX = sOther.MinX; + MaxX = sOther.MaxX; + MinY = sOther.MinY; + MaxY = sOther.MaxY; + MinZ = sOther.MinZ; + MaxZ = sOther.MaxZ; + } + } + else + { + MinX = 0; + MaxX = 0; + MinY = 0; + MaxY = 0; + MinZ = 0; + MaxZ = 0; + } + } + + int Intersects(OGREnvelope3D const& other) const + { + return MinX <= other.MaxX && MaxX >= other.MinX && + MinY <= other.MaxY && MaxY >= other.MinY && + MinZ <= other.MaxZ && MaxZ >= other.MinZ; + } + + int Contains(OGREnvelope3D const& other) const + { + return MinX <= other.MinX && MinY <= other.MinY && + MaxX >= other.MaxX && MaxY >= other.MaxY && + MinZ <= other.MinZ && MaxZ >= other.MaxZ; + } +}; +#else +typedef struct +{ + double MinX; + double MaxX; + double MinY; + double MaxY; + double MinZ; + double MaxZ; +} OGREnvelope3D; +#endif + + CPL_C_START void CPL_DLL *OGRMalloc( size_t ); @@ -161,12 +299,12 @@ typedef int OGRBoolean; /* -------------------------------------------------------------------- */ /* ogr_geometry.h related definitions. */ /* -------------------------------------------------------------------- */ + /** * List of well known binary geometry types. These are used within the BLOBs * but are also returned from OGRGeometry::getGeometryType() to identify the * type of a geometry object. */ - typedef enum { wkbUnknown = 0, /**< unknown type, non-standard */ @@ -192,6 +330,19 @@ typedef enum wkbGeometryCollection25D = 0x80000007 /**< 2.5D extension as per 99-402 */ } OGRwkbGeometryType; +/** + * Output variants of WKB we support. + * 99-402 was a short-lived extension to SFSQL 1.1 that used a high-bit flag + * to indicate the presence of Z coordiantes in a WKB geometry. + * SQL/MM Part 3 and SFSQL 1.2 use offsets of 1000 (Z), 2000 (M) and 3000 (ZM) + * to indicate the present of higher dimensional coordinates in a WKB geometry. + */ +typedef enum +{ + wkbVariantOgc, /**< Old-style 99-402 extended dimension (Z) WKB types */ + wkbVariantIso /**< SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB types */ +} OGRwkbVariant; + #define wkb25DBit 0x80000000 #define wkbFlatten(x) ((OGRwkbGeometryType) ((x) & (~wkb25DBit))) @@ -219,6 +370,11 @@ typedef enum # define DB2_V72_UNFIX_BYTE_ORDER(x) (x) #endif +#define ALTER_NAME_FLAG 0x1 +#define ALTER_TYPE_FLAG 0x2 +#define ALTER_WIDTH_PRECISION_FLAG 0x4 +#define ALTER_ALL_FLAG (ALTER_NAME_FLAG | ALTER_TYPE_FLAG | ALTER_WIDTH_PRECISION_FLAG) + /************************************************************************/ /* ogr_feature.h related definitions. */ /************************************************************************/ @@ -323,13 +479,19 @@ int CPL_DLL OGRParseDate( const char *pszInput, OGRField *psOutput, #define OLCFastFeatureCount "FastFeatureCount" #define OLCFastGetExtent "FastGetExtent" #define OLCCreateField "CreateField" +#define OLCDeleteField "DeleteField" +#define OLCReorderFields "ReorderFields" +#define OLCAlterFieldDefn "AlterFieldDefn" #define OLCTransactions "Transactions" #define OLCDeleteFeature "DeleteFeature" #define OLCFastSetNextByIndex "FastSetNextByIndex" #define OLCStringsAsUTF8 "StringsAsUTF8" +#define OLCIgnoreFields "IgnoreFields" +#define OLCCreateGeomField "CreateGeomField" #define ODsCCreateLayer "CreateLayer" #define ODsCDeleteLayer "DeleteLayer" +#define ODsCCreateGeomFieldAfterCreateLayer "CreateGeomFieldAfterCreateLayer" #define ODrCCreateDataSource "CreateDataSource" #define ODrCDeleteDataSource "DeleteDataSource" diff --git a/ogr/ogr_feature.h b/ogr/ogr_feature.h index 2a56180..4c591c7 100644 --- a/ogr/ogr_feature.h +++ b/ogr/ogr_feature.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_feature.h 18226 2009-12-09 09:30:48Z chaitanya $ + * $Id: ogr_feature.h 27390 2014-05-24 13:51:47Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Class for representing a whole feature, and layer schemas. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -57,6 +58,8 @@ class CPL_DLL OGRFieldDefn int nWidth; /* zero is variable */ int nPrecision; OGRField uDefault; + + int bIgnore; void Initialize( const char *, OGRFieldType ); @@ -88,6 +91,54 @@ class CPL_DLL OGRFieldDefn void SetDefault( const OGRField * ); const OGRField *GetDefaultRef() { return &uDefault; } + + int IsIgnored() { return bIgnore; } + void SetIgnored( int bIgnore ) { this->bIgnore = bIgnore; } + + int IsSame( const OGRFieldDefn * ) const; +}; + +/************************************************************************/ +/* OGRGeomFieldDefn */ +/************************************************************************/ + +/** + * Definition of a geometry field of an OGRFeatureDefn. A geometry field + * is described by a name, a geometry type and a spatial reference system. + * + * @since OGR 2.0 + */ + +class CPL_DLL OGRGeomFieldDefn +{ +protected: + char *pszName; + OGRwkbGeometryType eGeomType; /* all values possible except wkbNone */ + OGRSpatialReference* poSRS; + + int bIgnore; + + void Initialize( const char *, OGRwkbGeometryType ); + +public: + OGRGeomFieldDefn(const char *pszNameIn, + OGRwkbGeometryType eGeomTypeIn); + OGRGeomFieldDefn( OGRGeomFieldDefn * ); + virtual ~OGRGeomFieldDefn(); + + void SetName( const char * ); + const char *GetNameRef() { return pszName; } + + OGRwkbGeometryType GetType() { return eGeomType; } + void SetType( OGRwkbGeometryType eTypeIn ); + + virtual OGRSpatialReference* GetSpatialRef(); + void SetSpatialRef(OGRSpatialReference* poSRS); + + int IsIgnored() { return bIgnore; } + void SetIgnored( int bIgnore ) { this->bIgnore = bIgnore; } + + int IsSame( OGRGeomFieldDefn * ); }; /************************************************************************/ @@ -106,44 +157,66 @@ class CPL_DLL OGRFieldDefn * This object also can contain some other information such as a name, the * base geometry type and potentially other metadata. * + * Starting with GDAL 1.11, in addition to attribute fields, it can also + * contain multiple geometry fields. + * * It is reasonable for different translators to derive classes from * OGRFeatureDefn with additional translator specific information. */ class CPL_DLL OGRFeatureDefn { - private: + protected: volatile int nRefCount; int nFieldCount; OGRFieldDefn **papoFieldDefn; - OGRwkbGeometryType eGeomType; + int nGeomFieldCount; + OGRGeomFieldDefn **papoGeomFieldDefn; char *pszFeatureClassName; + + int bIgnoreStyle; public: OGRFeatureDefn( const char * pszName = NULL ); virtual ~OGRFeatureDefn(); - const char *GetName() { return pszFeatureClassName; } + virtual const char *GetName(); + + virtual int GetFieldCount(); + virtual OGRFieldDefn *GetFieldDefn( int i ); + virtual int GetFieldIndex( const char * ); + + virtual void AddFieldDefn( OGRFieldDefn * ); + virtual OGRErr DeleteFieldDefn( int iField ); + virtual OGRErr ReorderFieldDefns( int* panMap ); - int GetFieldCount() { return nFieldCount; } - OGRFieldDefn *GetFieldDefn( int i ); - int GetFieldIndex( const char * ); + virtual int GetGeomFieldCount(); + virtual OGRGeomFieldDefn *GetGeomFieldDefn( int i ); + virtual int GetGeomFieldIndex( const char * ); - void AddFieldDefn( OGRFieldDefn * ); + virtual void AddGeomFieldDefn( OGRGeomFieldDefn *, int bCopy = TRUE ); + virtual OGRErr DeleteGeomFieldDefn( int iGeomField ); - OGRwkbGeometryType GetGeomType() { return eGeomType; } - void SetGeomType( OGRwkbGeometryType ); + virtual OGRwkbGeometryType GetGeomType(); + virtual void SetGeomType( OGRwkbGeometryType ); - OGRFeatureDefn *Clone(); + virtual OGRFeatureDefn *Clone(); int Reference() { return CPLAtomicInc(&nRefCount); } int Dereference() { return CPLAtomicDec(&nRefCount); } int GetReferenceCount() { return nRefCount; } void Release(); + virtual int IsGeometryIgnored(); + virtual void SetGeometryIgnored( int bIgnore ); + virtual int IsStyleIgnored() { return bIgnoreStyle; } + virtual void SetStyleIgnored( int bIgnore ) { bIgnoreStyle = bIgnore; } + + virtual int IsSame( OGRFeatureDefn * poOtherFeatureDefn ); + static OGRFeatureDefn *CreateFeatureDefn( const char *pszName = NULL ); static void DestroyFeatureDefn( OGRFeatureDefn * ); }; @@ -162,7 +235,7 @@ class CPL_DLL OGRFeature long nFID; OGRFeatureDefn *poDefn; - OGRGeometry *poGeometry; + OGRGeometry **papoGeometries; OGRField *pauFields; protected: @@ -178,9 +251,22 @@ class CPL_DLL OGRFeature OGRErr SetGeometryDirectly( OGRGeometry * ); OGRErr SetGeometry( OGRGeometry * ); - OGRGeometry *GetGeometryRef() { return poGeometry; } + OGRGeometry *GetGeometryRef(); OGRGeometry *StealGeometry(); + int GetGeomFieldCount() + { return poDefn->GetGeomFieldCount(); } + OGRGeomFieldDefn *GetGeomFieldDefnRef( int iField ) + { return poDefn->GetGeomFieldDefn(iField); } + int GetGeomFieldIndex( const char * pszName) + { return poDefn->GetGeomFieldIndex(pszName); } + + OGRGeometry* GetGeomFieldRef(int iField); + OGRGeometry* StealGeometry(int iField); + OGRGeometry* GetGeomFieldRef(const char* pszFName); + OGRErr SetGeomFieldDirectly( int iField, OGRGeometry * ); + OGRErr SetGeomField( int iField, OGRGeometry * ); + OGRFeature *Clone(); virtual OGRBoolean Equal( OGRFeature * poFeature ); @@ -190,11 +276,7 @@ class CPL_DLL OGRFeature int GetFieldIndex( const char * pszName) { return poDefn->GetFieldIndex(pszName);} - int IsFieldSet( int iField ) const - { return - pauFields[iField].Set.nMarker1 != OGRUnsetMarker - || pauFields[iField].Set.nMarker2 != OGRUnsetMarker; - } + int IsFieldSet( int iField ); void UnsetField( int iField ); @@ -205,7 +287,7 @@ class CPL_DLL OGRFeature const char *GetFieldAsString( int i ); const int *GetFieldAsIntegerList( int i, int *pnCount ); const double *GetFieldAsDoubleList( int i, int *pnCount ); - char **GetFieldAsStringList( int i ) const; + char **GetFieldAsStringList( int i ); GByte *GetFieldAsBinary( int i, int *pnCount ); int GetFieldAsDateTime( int i, int *pnYear, int *pnMonth, int *pnDay, @@ -272,18 +354,19 @@ class CPL_DLL OGRFeature OGRErr SetFrom( OGRFeature *, int = TRUE); OGRErr SetFrom( OGRFeature *, int *, int = TRUE ); + OGRErr SetFieldsFrom( OGRFeature *, int *, int = TRUE ); OGRErr RemapFields( OGRFeatureDefn *poNewDefn, int *panRemapSource ); + OGRErr RemapGeomFields( OGRFeatureDefn *poNewDefn, + int *panRemapSource ); virtual const char *GetStyleString(); virtual void SetStyleString( const char * ); virtual void SetStyleStringDirectly( char * ); virtual OGRStyleTable *GetStyleTable() { return m_poStyleTable; } virtual void SetStyleTable(OGRStyleTable *poStyleTable); - virtual void SetStyleTableDirectly(OGRStyleTable *poStyleTable) - { if ( m_poStyleTable ) delete m_poStyleTable; - m_poStyleTable = poStyleTable; } + virtual void SetStyleTableDirectly(OGRStyleTable *poStyleTable); static OGRFeature *CreateFeature( OGRFeatureDefn * ); static void DestroyFeature( OGRFeature * ); @@ -294,6 +377,7 @@ class CPL_DLL OGRFeature /************************************************************************/ class OGRLayer; +class swq_expr_node; class CPL_DLL OGRFeatureQuery { @@ -302,6 +386,10 @@ class CPL_DLL OGRFeatureQuery void *pSWQExpr; char **FieldCollector( void *, char ** ); + + long *EvaluateAgainstIndices( swq_expr_node*, OGRLayer *, int& nFIDCount); + + int CanUseIndex( swq_expr_node*, OGRLayer * ); public: OGRFeatureQuery(); @@ -311,6 +399,8 @@ class CPL_DLL OGRFeatureQuery int Evaluate( OGRFeature * ); long *EvaluateAgainstIndices( OGRLayer *, OGRErr * ); + + int CanUseIndex( OGRLayer * ); char **GetUsedFields(); diff --git a/ogr/ogr_featurestyle.h b/ogr/ogr_featurestyle.h index 8ba1a52..8bdb04c 100644 --- a/ogr/ogr_featurestyle.h +++ b/ogr/ogr_featurestyle.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_featurestyle.h 16763 2009-04-14 09:57:15Z chaitanya $ + * $Id: ogr_featurestyle.h 19442 2010-04-18 00:02:37Z mloskot $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Define of Feature Representation @@ -260,7 +260,7 @@ class CPL_DLL OGRStylePen : public OGRStyleTool void SetColor(const char *pszColor){SetParamStr(OGRSTPenColor,pszColor);} double Width(GBool &bDefault){return GetParamDbl(OGRSTPenWidth,bDefault);} void SetWidth(double dfWidth){SetParamDbl(OGRSTPenWidth,dfWidth);} - const char *Pattern(GBool &bDefault){return (char *)GetParamStr(OGRSTPenPattern,bDefault);} + const char *Pattern(GBool &bDefault){return (const char *)GetParamStr(OGRSTPenPattern,bDefault);} void SetPattern(const char *pszPattern){SetParamStr(OGRSTPenPattern,pszPattern);} const char *Id(GBool &bDefault){return GetParamStr(OGRSTPenId,bDefault);} void SetId(const char *pszId){SetParamStr(OGRSTPenId,pszId);} diff --git a/ogr/ogr_fromepsg.cpp b/ogr/ogr_fromepsg.cpp index e74d077..0ce5a6b 100644 --- a/ogr/ogr_fromepsg.cpp +++ b/ogr/ogr_fromepsg.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_fromepsg.cpp 18571 2010-01-17 13:56:32Z rouault $ + * $Id: ogr_fromepsg.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Generate an OGRSpatialReference object based on an EPSG @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -32,12 +33,14 @@ #include "ogr_p.h" #include "cpl_csv.h" -CPL_CVSID("$Id: ogr_fromepsg.cpp 18571 2010-01-17 13:56:32Z rouault $"); +CPL_CVSID("$Id: ogr_fromepsg.cpp 27044 2014-03-16 23:41:27Z rouault $"); #ifndef PI # define PI 3.14159265358979323846 #endif +void OGRPrintDouble( char * pszStrBuf, double dfValue ); + static const char *papszDatumEquiv[] = { "Militar_Geographische_Institut", @@ -74,7 +77,8 @@ void OGREPSGDatumNameMassage( char ** ppszDatum ) /* -------------------------------------------------------------------- */ for( i = 0; pszDatum[i] != '\0'; i++ ) { - if( !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') + if( pszDatum[i] != '+' + && !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') ) { @@ -203,9 +207,25 @@ int EPSGGetUOMAngleInfo( int nUOMAngleCode, { const char *pszUOMName = NULL; double dfInDegrees = 1.0; - const char *pszFilename = CSVFilename( "unit_of_measure.csv" ); + const char *pszFilename; char szSearchKey[24]; + /* We do a special override of some of the DMS formats name */ + /* This will also solve accuracy problems when computing */ + /* the dfInDegree value from the CSV values (#3643) */ + if( nUOMAngleCode == 9102 || nUOMAngleCode == 9107 + || nUOMAngleCode == 9108 || nUOMAngleCode == 9110 + || nUOMAngleCode == 9122 ) + { + if( ppszUOMName != NULL ) + *ppszUOMName = CPLStrdup("degree"); + if( pdfInDegrees != NULL ) + *pdfInDegrees = 1.0; + return TRUE; + } + + pszFilename = CSVFilename( "unit_of_measure.csv" ); + sprintf( szSearchKey, "%d", nUOMAngleCode ); pszUOMName = CSVGetField( pszFilename, "UOM_CODE", szSearchKey, CC_Integer, @@ -234,12 +254,6 @@ int EPSGGetUOMAngleInfo( int nUOMAngleCode, if( dfFactorC != 0.0 ) dfInDegrees = (dfFactorB / dfFactorC) * (180.0 / PI); - /* We do a special override of some of the DMS formats name */ - if( nUOMAngleCode == 9102 || nUOMAngleCode == 9107 - || nUOMAngleCode == 9108 || nUOMAngleCode == 9110 - || nUOMAngleCode == 9122 ) - pszUOMName = "degree"; - // For some reason, (FactorB) is not very precise in EPSG, use // a more exact form for grads. if( nUOMAngleCode == 9105 ) @@ -257,7 +271,7 @@ int EPSGGetUOMAngleInfo( int nUOMAngleCode, pszUOMName = "radian"; dfInDegrees = 180.0 / PI; break; - + case 9102: case 9107: case 9108: @@ -442,6 +456,8 @@ int EPSGGetWGS84Transform( int nGeogCS, double *padfTransform ) /* Fetch the transformation parameters. */ /* -------------------------------------------------------------------- */ iDXField = CSVGetFileFieldId(pszFilename, "DX"); + if (iDXField < 0 || CSLCount(papszLine) < iDXField + 7) + return FALSE; for( iField = 0; iField < 7; iField++ ) padfTransform[iField] = CPLAtof(papszLine[iDXField+iField]); @@ -480,7 +496,9 @@ EPSGGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset ) /* -------------------------------------------------------------------- */ /* Use a special short cut for Greenwich, since it is so common. */ /* -------------------------------------------------------------------- */ - if( nPMCode == 7022 /* PM_Greenwich */ ) + /* FIXME? Where does 7022 come from ? Let's keep it just in case */ + /* 8901 is the official current code for Greenwich */ + if( nPMCode == 7022 /* PM_Greenwich */ || nPMCode == 8901 ) { if( pdfOffset != NULL ) *pdfOffset = 0.0; @@ -744,6 +762,7 @@ OSRGetEllipsoidInfo( int nCode, char ** ppszName, return OGRERR_NONE; } +#define CoLatConeAxis 1036 /* see #4223 */ #define NatOriginLat 8801 #define NatOriginLong 8802 #define NatOriginScaleFactor 8805 @@ -863,7 +882,9 @@ EPSGGetProjTRFInfo( int nPCS, int * pnProjMethod, else /* really we should consider looking up other scaling factors */ { if( nUOM != 9201 ) - CPLDebug( "OGR", "Non-unity scale factor units!" ); + CPLDebug( "OGR", + "Non-unity scale factor units! (UOM=%d, PCS=%d)", + nUOM, nPCS ); adfProjParms[i] = CPLAtof(pszValue); } @@ -1091,6 +1112,7 @@ static OGRErr SetEPSGAxisInfo( OGRSpatialReference *poSRS, /* are which. */ /* -------------------------------------------------------------------- */ int iAxisOrientationField, iAxisAbbrevField, iAxisOrderField; + int iAxisNameCodeField; iAxisOrientationField = CSVGetFileFieldId( pszFilename, "coord_axis_orientation" ); @@ -1098,6 +1120,25 @@ static OGRErr SetEPSGAxisInfo( OGRSpatialReference *poSRS, CSVGetFileFieldId( pszFilename, "coord_axis_abbreviation" ); iAxisOrderField = CSVGetFileFieldId( pszFilename, "coord_axis_order" ); + iAxisNameCodeField = + CSVGetFileFieldId( pszFilename, "coord_axis_name_code" ); + + /* Check that all fields are available and that the axis_order field */ + /* is the one with highest index */ + if ( !( iAxisOrientationField >= 0 && + iAxisOrientationField < iAxisOrderField && + iAxisAbbrevField >= 0 && + iAxisAbbrevField < iAxisOrderField && + iAxisOrderField >= 0 && + iAxisNameCodeField >= 0 && + iAxisNameCodeField < iAxisOrderField ) ) + { + CSLDestroy( papszAxis1 ); + CSLDestroy( papszAxis2 ); + CPLError( CE_Failure, CPLE_AppDefined, + "coordinate_axis.csv corrupted" ); + return OGRERR_FAILURE; + } if( CSLCount(papszAxis1) < iAxisOrderField+1 || CSLCount(papszAxis2) < iAxisOrderField+1 ) @@ -1125,8 +1166,9 @@ static OGRErr SetEPSGAxisInfo( OGRSpatialReference *poSRS, /* -------------------------------------------------------------------- */ OGRAxisOrientation eOAxis1 = OAO_Other, eOAxis2 = OAO_Other; int iAO; + static int anCodes[7] = { -1, 9907, 9909, 9906, 9908, -1, -1 }; - for( iAO = 0; iAO <= 6; iAO++ ) + for( iAO = 0; iAO < 7; iAO++ ) { if( EQUAL(papszAxis1[iAxisOrientationField], OSRAxisEnumToName((OGRAxisOrientation) iAO)) ) @@ -1134,6 +1176,13 @@ static OGRErr SetEPSGAxisInfo( OGRSpatialReference *poSRS, if( EQUAL(papszAxis2[iAxisOrientationField], OSRAxisEnumToName((OGRAxisOrientation) iAO)) ) eOAxis2 = (OGRAxisOrientation) iAO; + + if( eOAxis1 == OAO_Other + && anCodes[iAO] == atoi(papszAxis1[iAxisNameCodeField]) ) + eOAxis1 = (OGRAxisOrientation) iAO; + if( eOAxis2 == OAO_Other + && anCodes[iAO] == atoi(papszAxis2[iAxisNameCodeField]) ) + eOAxis2 = (OGRAxisOrientation) iAO; } /* -------------------------------------------------------------------- */ @@ -1192,13 +1241,22 @@ static OGRErr SetEPSGGeogCS( OGRSpatialReference * poSRS, int nGeogCS ) return OGRERR_UNSUPPORTED_SRS; if( !EPSGGetPMInfo( nPMCode, &pszPMName, &dfPMOffset ) ) + { + CPLFree( pszDatumName ); + CPLFree( pszGeogCSName ); return OGRERR_UNSUPPORTED_SRS; + } OGREPSGDatumNameMassage( &pszDatumName ); if( OSRGetEllipsoidInfo( nEllipsoidCode, &pszEllipsoidName, &dfSemiMajor, &dfInvFlattening ) != OGRERR_NONE ) + { + CPLFree( pszDatumName ); + CPLFree( pszGeogCSName ); + CPLFree( pszPMName ); return OGRERR_UNSUPPORTED_SRS; + } if( !EPSGGetUOMAngleInfo( nUOMAngle, &pszAngleName, &dfAngleInDegrees ) ) { @@ -1353,7 +1411,10 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) if( !EPSGGetPCSInfo( nPCSCode, &pszPCSName, &nUOMLength, &nUOMAngleCode, &nGCSCode, &nTRFCode, &nCSC ) ) + { + CPLFree(pszPCSName); return OGRERR_UNSUPPORTED_SRS; + } poSRS->SetNode( "PROJCS", pszPCSName ); @@ -1362,7 +1423,10 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) /* -------------------------------------------------------------------- */ nErr = SetEPSGGeogCS( poSRS, nGCSCode ); if( nErr != OGRERR_NONE ) + { + CPLFree(pszPCSName); return nErr; + } dfFromGreenwich = poSRS->GetPrimeMeridian(); @@ -1370,7 +1434,10 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) /* Set linear units. */ /* -------------------------------------------------------------------- */ if( !EPSGGetUOMLengthInfo( nUOMLength, &pszUOMLengthName, &dfInMeters ) ) + { + CPLFree(pszPCSName); return OGRERR_UNSUPPORTED_SRS; + } poSRS->SetLinearUnits( pszUOMLengthName, dfInMeters ); poSRS->SetAuthority( "PROJCS|UNIT", "EPSG", nUOMLength ); @@ -1407,8 +1474,14 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) OGR_FP( FalseOriginNorthing )); break; + case 9805: + poSRS->SetMercator2SP( OGR_FP( StdParallel1Lat ), + OGR_FP( NatOriginLat ), OGR_FP(NatOriginLong), + OGR_FP( FalseEasting ), OGR_FP(FalseNorthing) ); + + break; + case 9804: - case 9805: /* NOTE: treats 1SP and 2SP cases the same */ case 9841: /* Mercator 1SP (Spherical) */ case 1024: /* Google Mercator */ poSRS->SetMercator( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), @@ -1478,12 +1551,12 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) break; case 9815: - poSRS->SetHOM( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ), - OGR_FP( Azimuth ), - OGR_FP( AngleRectifiedToSkewedGrid ), - OGR_FP( InitialLineScaleFactor ), - OGR_FP( ProjCenterEasting ), - OGR_FP( ProjCenterNorthing ) ); + poSRS->SetHOMAC( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ), + OGR_FP( Azimuth ), + OGR_FP( AngleRectifiedToSkewedGrid ), + OGR_FP( InitialLineScaleFactor ), + OGR_FP( ProjCenterEasting ), + OGR_FP( ProjCenterNorthing ) ); break; case 9816: @@ -1497,6 +1570,7 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); break; + case 1041: /* used by EPSG:5514 */ case 9819: { double dfCenterLong = OGR_FP( ProjCenterLong ); @@ -1504,8 +1578,12 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) if( dfCenterLong == 0.0 ) // See ticket #2559 dfCenterLong = OGR_FP( PolarLongOrigin ); + double dfAzimuth = OGR_FP( CoLatConeAxis ); // See ticket #4223 + if( dfAzimuth == 0.0 ) + dfAzimuth = OGR_FP( Azimuth ); + poSRS->SetKrovak( OGR_FP( ProjCenterLat ), dfCenterLong, - OGR_FP( Azimuth ), + dfAzimuth, OGR_FP( PseudoStdParallelLat ), OGR_FP( PseudoStdParallelScaleFactor ), OGR_FP( ProjCenterEasting ), @@ -1514,14 +1592,15 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) break; case 9820: + case 1027: /* used by EPSG:2163, 3408, 3409, 3973 and 3974 */ poSRS->SetLAEA( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); break; - case 9821: /* this is the spherical form, and really needs different + case 9821: /* DEPREACTED : this is the spherical form, and really needs different equations which give different results but PROJ.4 doesn't seem to support the spherical form. */ - poSRS->SetLAEA( OGR_FP( SphericalOriginLat ), + poSRS->SetLAEA( OGR_FP( SphericalOriginLat ), OGR_FP( SphericalOriginLong ), OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); break; @@ -1536,6 +1615,8 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) break; case 9823: /* Equidistant Cylindrical / Plate Carre / Equirectangular */ + case 1028: + case 1029: poSRS->SetEquirectangular( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), 0.0, 0.0 ); @@ -1575,6 +1656,404 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) return OGRERR_NONE; } +/************************************************************************/ +/* SetEPSGVertCS() */ +/************************************************************************/ + +static OGRErr SetEPSGVertCS( OGRSpatialReference * poSRS, int nVertCSCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch record from the vertcs.csv or override file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord; + char szSearchKey[24]; + const char *pszFilename; + + pszFilename = CSVFilename( "vertcs.override.csv" ); + sprintf( szSearchKey, "%d", nVertCSCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "vertcs.csv" ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return OGRERR_UNSUPPORTED_SRS; + + +/* -------------------------------------------------------------------- */ +/* Setup the basic VERT_CS. */ +/* -------------------------------------------------------------------- */ + poSRS->SetVertCS( + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")), + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DATUM_NAME")) ); +/* -------------------------------------------------------------------- */ +/* Setup the VERT_DATUM node. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "VERT_CS|VERT_DATUM", "EPSG", + atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DATUM_CODE"))) ); + +/* -------------------------------------------------------------------- */ +/* Should we add a geoidgrids extension node? */ +/* -------------------------------------------------------------------- */ + const char *pszMethod = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"COORD_OP_METHOD_CODE_1")); + if( pszMethod && EQUAL(pszMethod,"9665") ) + { + const char *pszParm11 = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"PARM_1_1")); + + poSRS->SetExtension( "VERT_CS|VERT_DATUM", "PROJ4_GRIDS", pszParm11 ); + } + +/* -------------------------------------------------------------------- */ +/* Set linear units. */ +/* -------------------------------------------------------------------- */ + char *pszUOMLengthName = NULL; + double dfInMeters; + int nUOM_CODE = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "UOM_CODE"))); + + if( !EPSGGetUOMLengthInfo( nUOM_CODE, &pszUOMLengthName, &dfInMeters ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to lookup UOM CODE %d", nUOM_CODE ); + } + else + { + poSRS->SetTargetLinearUnits( "VERT_CS", pszUOMLengthName, dfInMeters ); + poSRS->SetAuthority( "VERT_CS|UNIT", "EPSG", nUOM_CODE ); + + CPLFree( pszUOMLengthName ); + } + +/* -------------------------------------------------------------------- */ +/* Set overall authority code. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "VERT_CS", "EPSG", nVertCSCode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetEPSGCompdCS() */ +/************************************************************************/ + +static OGRErr SetEPSGCompdCS( OGRSpatialReference * poSRS, int nCCSCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch record from the compdcs.csv or override file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord = NULL; + char szSearchKey[24]; + const char *pszFilename; + + sprintf( szSearchKey, "%d", nCCSCode ); + +// So far no override file needed. +// pszFilename = CSVFilename( "compdcs.override.csv" ); +// papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", +// szSearchKey, CC_Integer ); + + //if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "compdcs.csv" ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Fetch subinformation now before anything messes with the */ +/* last loaded record. */ +/* -------------------------------------------------------------------- */ + int nPCSCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "CMPD_HORIZCRS_CODE"))); + int nVertCSCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "CMPD_VERTCRS_CODE"))); + +/* -------------------------------------------------------------------- */ +/* Set the COMPD_CS node with a name. */ +/* -------------------------------------------------------------------- */ + poSRS->SetNode( "COMPD_CS", + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")) ); + +/* -------------------------------------------------------------------- */ +/* Lookup the the projected coordinate system. Can the */ +/* horizontal CRS be a GCS? */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oPCS; + OGRErr eErr; + + eErr = SetEPSGProjCS( &oPCS, nPCSCode ); + if( eErr != OGRERR_NONE ) + { + // perhaps it is a GCS? + eErr = SetEPSGGeogCS( &oPCS, nPCSCode ); + } + + if( eErr != OGRERR_NONE ) + { + return eErr; + } + + poSRS->GetRoot()->AddChild( + oPCS.GetRoot()->Clone() ); + +/* -------------------------------------------------------------------- */ +/* Lookup the VertCS. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oVertCS; + eErr = SetEPSGVertCS( &oVertCS, nVertCSCode ); + if( eErr != OGRERR_NONE ) + return eErr; + + poSRS->GetRoot()->AddChild( + oVertCS.GetRoot()->Clone() ); + +/* -------------------------------------------------------------------- */ +/* Set overall authority code. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "COMPD_CS", "EPSG", nCCSCode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetEPSGGeocCS() */ +/************************************************************************/ + +static OGRErr SetEPSGGeocCS( OGRSpatialReference * poSRS, int nGCSCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch record from the geoccs.csv or override file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord = NULL; + char szSearchKey[24]; + const char *pszFilename; + + sprintf( szSearchKey, "%d", nGCSCode ); + +// So far no override file needed. +// pszFilename = CSVFilename( "compdcs.override.csv" ); +// papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", +// szSearchKey, CC_Integer ); + + //if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "geoccs.csv" ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Set the GEOCCS node with a name. */ +/* -------------------------------------------------------------------- */ + poSRS->Clear(); + poSRS->SetGeocCS( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")) ); + +/* -------------------------------------------------------------------- */ +/* Get datum related information. */ +/* -------------------------------------------------------------------- */ + int nDatumCode, nEllipsoidCode, nPMCode; + char *pszDatumName; + + nDatumCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DATUM_CODE"))); + + pszDatumName = + CPLStrdup( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"DATUM_NAME") ) ); + OGREPSGDatumNameMassage( &pszDatumName ); + + + nEllipsoidCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "ELLIPSOID_CODE"))); + + nPMCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "PRIME_MERIDIAN_CODE"))); + +/* -------------------------------------------------------------------- */ +/* Get prime meridian information. */ +/* -------------------------------------------------------------------- */ + char *pszPMName = NULL; + double dfPMOffset = 0.0; + + if( !EPSGGetPMInfo( nPMCode, &pszPMName, &dfPMOffset ) ) + { + CPLFree( pszDatumName ); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Get the ellipsoid information. */ +/* -------------------------------------------------------------------- */ + char *pszEllipsoidName = NULL; + double dfSemiMajor, dfInvFlattening; + + if( OSRGetEllipsoidInfo( nEllipsoidCode, &pszEllipsoidName, + &dfSemiMajor, &dfInvFlattening ) != OGRERR_NONE ) + { + CPLFree( pszDatumName ); + CPLFree( pszPMName ); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Setup the spheroid. */ +/* -------------------------------------------------------------------- */ + char szValue[128]; + + OGR_SRSNode *poSpheroid = new OGR_SRSNode( "SPHEROID" ); + poSpheroid->AddChild( new OGR_SRSNode( pszEllipsoidName ) ); + + OGRPrintDouble( szValue, dfSemiMajor ); + poSpheroid->AddChild( new OGR_SRSNode(szValue) ); + + OGRPrintDouble( szValue, dfInvFlattening ); + poSpheroid->AddChild( new OGR_SRSNode(szValue) ); + + CPLFree( pszEllipsoidName ); + +/* -------------------------------------------------------------------- */ +/* Setup the Datum. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poDatum = new OGR_SRSNode( "DATUM" ); + poDatum->AddChild( new OGR_SRSNode(pszDatumName) ); + poDatum->AddChild( poSpheroid ); + + poSRS->GetRoot()->AddChild( poDatum ); + + CPLFree( pszDatumName ); + +/* -------------------------------------------------------------------- */ +/* Setup the prime meridian. */ +/* -------------------------------------------------------------------- */ + if( dfPMOffset == 0.0 ) + strcpy( szValue, "0" ); + else + OGRPrintDouble( szValue, dfPMOffset ); + + OGR_SRSNode *poPM = new OGR_SRSNode( "PRIMEM" ); + poPM->AddChild( new OGR_SRSNode( pszPMName ) ); + poPM->AddChild( new OGR_SRSNode( szValue ) ); + + poSRS->GetRoot()->AddChild( poPM ); + + CPLFree( pszPMName ); + +/* -------------------------------------------------------------------- */ +/* Should we try to lookup a datum transform? */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if( EPSGGetWGS84Transform( nGeogCS, adfBursaTransform ) ) + { + OGR_SRSNode *poWGS84; + char szValue[100]; + + poWGS84 = new OGR_SRSNode( "TOWGS84" ); + + for( int iCoeff = 0; iCoeff < 7; iCoeff++ ) + { + sprintf( szValue, "%g", adfBursaTransform[iCoeff] ); + poWGS84->AddChild( new OGR_SRSNode( szValue ) ); + } + + poSRS->GetAttrNode( "DATUM" )->AddChild( poWGS84 ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Set linear units. */ +/* -------------------------------------------------------------------- */ + char *pszUOMLengthName = NULL; + double dfInMeters = 1.0; + int nUOMLength = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "UOM_CODE"))); + + if( !EPSGGetUOMLengthInfo( nUOMLength, &pszUOMLengthName, &dfInMeters ) ) + { + return OGRERR_UNSUPPORTED_SRS; + } + + poSRS->SetLinearUnits( pszUOMLengthName, dfInMeters ); + poSRS->SetAuthority( "GEOCCS|UNIT", "EPSG", nUOMLength ); + + CPLFree( pszUOMLengthName ); + +/* -------------------------------------------------------------------- */ +/* Set axes */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Geocentric X" ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_Other) ) ); + + poSRS->GetRoot()->AddChild( poAxis ); + + poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Geocentric Y" ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_Other) ) ); + + poSRS->GetRoot()->AddChild( poAxis ); + + poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Geocentric Z" ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_North) ) ); + + poSRS->GetRoot()->AddChild( poAxis ); + +/* -------------------------------------------------------------------- */ +/* Set the authority codes. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "DATUM", "EPSG", nDatumCode ); + poSRS->SetAuthority( "SPHEROID", "EPSG", nEllipsoidCode ); + poSRS->SetAuthority( "PRIMEM", "EPSG", nPMCode ); + +// if( nUOMAngle > 0 ) +// poSRS->SetAuthority( "GEOGCS|UNIT", "EPSG", nUOMAngle ); + + poSRS->SetAuthority( "GEOCCS", "EPSG", nGCSCode ); + + return OGRERR_NONE; +} + /************************************************************************/ /* importFromEPSG() */ /************************************************************************/ @@ -1599,7 +2078,9 @@ static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) * This method is similar to importFromEPSGA() except that EPSG preferred * axis ordering will *not* be applied for geographic coordinate systems. * EPSG normally defines geographic coordinate systems to use lat/long - * contrary to typical GIS use). + * contrary to typical GIS use). Since OGR 1.10.0, EPSG preferred + * axis ordering will also *not* be applied for projected coordinate systems + * that use northing/easting order. * * This method is the same as the C function OSRImportFromEPSG(). * @@ -1620,6 +2101,10 @@ OGRErr OGRSpatialReference::importFromEPSG( int nCode ) if( poGEOGCS != NULL ) poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); } return eErr; @@ -1654,9 +2139,10 @@ OGRErr CPL_STDCALL OSRImportFromEPSG( OGRSpatialReferenceH hSRS, int nCode ) * passed in EPSG GCS or PCS code. * * This method is similar to importFromEPSG() except that EPSG preferred - * axis ordering *will* be applied for geographic coordinate systems. - * EPSG normally defines geographic coordinate systems to use lat/long - * contrary to typical GIS use). See OGRSpatialReference::importFromEPSG() + * axis ordering *will* be applied for geographic and projected coordinate systems. + * EPSG normally defines geographic coordinate systems to use lat/long, and + * also there are also a few projected coordinate systems that use northing/easting + * order contrary to typical GIS use). See OGRSpatialReference::importFromEPSG() * for more details on operation of this method. * * This method is the same as the C function OSRImportFromEPSGA(). @@ -1670,6 +2156,7 @@ OGRErr OGRSpatialReference::importFromEPSGA( int nCode ) { OGRErr eErr; + CPLLocaleC oLocaleForcer; bNormInfoSet = FALSE; @@ -1698,12 +2185,17 @@ OGRErr OGRSpatialReference::importFromEPSGA( int nCode ) } /* -------------------------------------------------------------------- */ -/* Is this a GeogCS code? this is inadequate as a criteria */ +/* Try this as various sorts of objects till one works. */ /* -------------------------------------------------------------------- */ - if( EPSGGetGCSInfo( nCode, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) ) - eErr = SetEPSGGeogCS( this, nCode ); - else + eErr = SetEPSGGeogCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) eErr = SetEPSGProjCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGVertCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGCompdCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGGeocCS( this, nCode ); /* -------------------------------------------------------------------- */ /* If we get it as an unsupported code, try looking it up in */ @@ -1753,8 +2245,6 @@ OGRErr OGRSpatialReference::importFromEPSGA( int nCode ) SetAuthority( "PROJCS", "EPSG", nCode ); else if( IsGeographic() ) SetAuthority( "GEOGCS", "EPSG", nCode ); - - eErr = FixupOrdering(); } /* -------------------------------------------------------------------- */ @@ -1767,6 +2257,16 @@ OGRErr OGRSpatialReference::importFromEPSGA( int nCode ) nCode ); } +/* -------------------------------------------------------------------- */ +/* To the extent possible, we want to return the results in as */ +/* close to standard OGC format as possible, so we fixup the */ +/* ordering. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_NONE ) + { + eErr = FixupOrdering(); + } + return eErr; } @@ -1916,6 +2416,12 @@ OGRErr OGRSpatialReference::SetStatePlane( int nZone, int bNAD83, /* OSRSetStatePlane() */ /************************************************************************/ +/** + * \brief Set State Plane projection definition. + * + * This function is the same as OGRSpatialReference::SetStatePlane(). + */ + OGRErr OSRSetStatePlane( OGRSpatialReferenceH hSRS, int nZone, int bNAD83 ) { @@ -1928,6 +2434,12 @@ OGRErr OSRSetStatePlane( OGRSpatialReferenceH hSRS, int nZone, int bNAD83 ) /* OSRSetStatePlaneWithUnits() */ /************************************************************************/ +/** + * \brief Set State Plane projection definition. + * + * This function is the same as OGRSpatialReference::SetStatePlane(). + */ + OGRErr OSRSetStatePlaneWithUnits( OGRSpatialReferenceH hSRS, int nZone, int bNAD83, const char *pszOverrideUnitName, @@ -2115,6 +2627,12 @@ OGRErr OGRSpatialReference::AutoIdentifyEPSG() /* OSRAutoIdentifyEPSG() */ /************************************************************************/ +/** + * \brief Set EPSG authority info if possible. + * + * This function is the same as OGRSpatialReference::AutoIdentifyEPSG(). + */ + OGRErr OSRAutoIdentifyEPSG( OGRSpatialReferenceH hSRS ) { @@ -2139,6 +2657,8 @@ OGRErr OSRAutoIdentifyEPSG( OGRSpatialReferenceH hSRS ) * FALSE will be returned for all coordinate systems that are not geographic, * or that do not have an EPSG code set. * + * This method is the same as the C function OSREPSGTreatsAsLatLong(). + * * @return TRUE or FALSE. */ @@ -2156,7 +2676,7 @@ int OGRSpatialReference::EPSGTreatsAsLatLong() OGR_SRSNode *poFirstAxis = GetAttrNode( "GEOGCS|AXIS" ); if( poFirstAxis == NULL ) - return TRUE; + return FALSE; if( poFirstAxis->GetChildCount() >= 2 && EQUAL(poFirstAxis->GetChild(1)->GetValue(),"NORTH") ) @@ -2169,6 +2689,13 @@ int OGRSpatialReference::EPSGTreatsAsLatLong() /* OSREPSGTreatsAsLatLong() */ /************************************************************************/ +/** + * \brief This function returns TRUE if EPSG feels this geographic coordinate + * system should be treated as having lat/long coordinate ordering. + * + * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong(). + */ + int OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS ) { @@ -2177,3 +2704,68 @@ int OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS ) return ((OGRSpatialReference *) hSRS)->EPSGTreatsAsLatLong(); } +/************************************************************************/ +/* EPSGTreatsAsNorthingEasting() */ +/************************************************************************/ + +/** + * \brief This method returns TRUE if EPSG feels this projected coordinate + * system should be treated as having northing/easting coordinate ordering. + * + * Currently this returns TRUE for all projected coordinate systems + * with an EPSG code set, and AXIS values set defining it as northing, easting. + * + * FALSE will be returned for all coordinate systems that are not projected, + * or that do not have an EPSG code set. + * + * This method is the same as the C function EPSGTreatsAsNorthingEasting(). + * + * @return TRUE or FALSE. + * + * @since OGR 1.10.0 + */ + +int OGRSpatialReference::EPSGTreatsAsNorthingEasting() + +{ + if( !IsProjected() ) + return FALSE; + + const char *pszAuth = GetAuthorityName( "PROJCS" ); + + if( pszAuth == NULL || !EQUAL(pszAuth,"EPSG") ) + return FALSE; + + OGR_SRSNode *poFirstAxis = GetAttrNode( "PROJCS|AXIS" ); + + if( poFirstAxis == NULL ) + return FALSE; + + if( poFirstAxis->GetChildCount() >= 2 + && EQUAL(poFirstAxis->GetChild(1)->GetValue(),"NORTH") ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* OSREPSGTreatsAsNorthingEasting() */ +/************************************************************************/ + +/** + * \brief This function returns TRUE if EPSG feels this geographic coordinate + * system should be treated as having northing/easting coordinate ordering. + * + * This function is the same as OGRSpatialReference::EPSGTreatsAsNorthingEasting(). + * + * @since OGR 1.10.0 + */ + +int OSREPSGTreatsAsNorthingEasting( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsNorthingEasting", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->EPSGTreatsAsNorthingEasting(); +} + diff --git a/ogr/ogr_gensql.cpp b/ogr/ogr_gensql.cpp index 5932cf5..109016a 100644 --- a/ogr/ogr_gensql.cpp +++ b/ogr/ogr_gensql.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_gensql.cpp 17648 2009-09-17 15:11:18Z warmerdam $ + * $Id: ogr_gensql.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements OGRGenSQLResultsLayer. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -27,19 +28,67 @@ * DEALINGS IN THE SOFTWARE. ****************************************************************************/ +#include "swq.h" #include "ogr_p.h" #include "ogr_gensql.h" #include "cpl_string.h" +#include "ogr_api.h" +#include "cpl_time.h" +#include -CPL_CVSID("$Id: ogr_gensql.cpp 17648 2009-09-17 15:11:18Z warmerdam $"); +CPL_CVSID("$Id: ogr_gensql.cpp 27384 2014-05-24 12:28:12Z rouault $"); + + +class OGRGenSQLGeomFieldDefn: public OGRGeomFieldDefn +{ + public: + OGRGenSQLGeomFieldDefn(OGRGeomFieldDefn* poGeomFieldDefn) : + OGRGeomFieldDefn(poGeomFieldDefn->GetNameRef(), + poGeomFieldDefn->GetType()), bForceGeomType(FALSE) + { + SetSpatialRef(poGeomFieldDefn->GetSpatialRef()); + } + + int bForceGeomType; +}; + +/************************************************************************/ +/* OGRGenSQLResultsLayerHasSpecialField() */ +/************************************************************************/ + +static +int OGRGenSQLResultsLayerHasSpecialField(swq_expr_node* expr, + int nMinIndexForSpecialField) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if (expr->table_index == 0) + { + return expr->field_index >= nMinIndexForSpecialField && + expr->field_index < nMinIndexForSpecialField + SPECIAL_FIELD_COUNT; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (OGRGenSQLResultsLayerHasSpecialField(expr->papoSubExpr[i], + nMinIndexForSpecialField)) + return TRUE; + } + } + return FALSE; +} /************************************************************************/ /* OGRGenSQLResultsLayer() */ /************************************************************************/ -OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, +OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( GDALDataset *poSrcDS, void *pSelectInfo, - OGRGeometry *poSpatFilter ) + OGRGeometry *poSpatFilter, + const char *pszWHERE, + const char *pszDialect ) { swq_select *psSelectInfo = (swq_select *) pSelectInfo; @@ -49,10 +98,12 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, poDefn = NULL; poSummaryFeature = NULL; panFIDIndex = NULL; + bOrderByValid = FALSE; nIndexSize = 0; nNextIndexFID = 0; nExtraDSCount = 0; papoExtraDS = NULL; + panGeomFieldToSrcGeomField = NULL; /* -------------------------------------------------------------------- */ /* Identify all the layers involved in the SELECT. */ @@ -65,14 +116,12 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) { swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; - OGRDataSource *poTableDS = poSrcDS; + GDALDataset *poTableDS = poSrcDS; if( psTableDef->data_source != NULL ) { - OGRSFDriverRegistrar *poReg=OGRSFDriverRegistrar::GetRegistrar(); - - poTableDS = - poReg->OpenShared( psTableDef->data_source, FALSE, NULL ); + poTableDS = (GDALDataset*) GDALOpenEx( psTableDef->data_source, + GDAL_OF_VECTOR | GDAL_OF_SHARED, NULL, NULL, NULL ); if( poTableDS == NULL ) { if( strlen(CPLGetLastErrorMsg()) == 0 ) @@ -83,7 +132,7 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, return; } - papoExtraDS = (OGRDataSource **) + papoExtraDS = (GDALDataset **) CPLRealloc( papoExtraDS, sizeof(void*) * ++nExtraDSCount ); papoExtraDS[nExtraDSCount-1] = poTableDS; @@ -101,11 +150,27 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, poSrcLayer = papoTableLayers[0]; /* -------------------------------------------------------------------- */ -/* Now that we have poSrcLayer, we can install a spatial filter */ -/* if there is one. */ +/* If the user has explicitely requested a OGRSQL dialect, then */ +/* we should avoid to forward the where clause to the source layer */ +/* when there is a risk it cannot understand it (#4022) */ /* -------------------------------------------------------------------- */ - if( poSpatFilter != NULL ) - SetSpatialFilter( poSpatFilter ); + int bForwardWhereToSourceLayer = TRUE; + if( pszWHERE ) + { + if( psSelectInfo->where_expr && pszDialect != NULL && + EQUAL(pszDialect, "OGRSQL") ) + { + int nMinIndexForSpecialField = poSrcLayer->GetLayerDefn()->GetFieldCount(); + bForwardWhereToSourceLayer = !OGRGenSQLResultsLayerHasSpecialField + (psSelectInfo->where_expr, nMinIndexForSpecialField); + } + if (bForwardWhereToSourceLayer) + this->pszWHERE = CPLStrdup(pszWHERE); + else + this->pszWHERE = NULL; + } + else + this->pszWHERE = NULL; /* -------------------------------------------------------------------- */ /* Prepare a feature definition based on the query. */ @@ -113,39 +178,105 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, OGRFeatureDefn *poSrcDefn = poSrcLayer->GetLayerDefn(); poDefn = new OGRFeatureDefn( psSelectInfo->table_defs[0].table_alias ); + SetDescription( poDefn->GetName() ); + poDefn->SetGeomType(wkbNone); poDefn->Reference(); iFIDFieldIndex = poSrcDefn->GetFieldCount(); + /* + 1 since we can add an implicit geometry field */ + panGeomFieldToSrcGeomField = (int*) CPLMalloc(sizeof(int) * (1 + psSelectInfo->result_columns)); + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) { swq_col_def *psColDef = psSelectInfo->column_defs + iField; - OGRFieldDefn oFDefn( psColDef->field_name, OFTInteger ); + OGRFieldDefn oFDefn( "", OFTInteger ); + OGRGeomFieldDefn oGFDefn( "", wkbUnknown ); OGRFieldDefn *poSrcFDefn = NULL; - OGRFeatureDefn *poLayerDefn = - papoTableLayers[psColDef->table_index]->GetLayerDefn(); + OGRGeomFieldDefn *poSrcGFDefn = NULL; + int bIsGeometry = FALSE; + OGRFeatureDefn *poLayerDefn = NULL; + int iSrcGeomField = -1; + + if( psColDef->table_index != -1 ) + poLayerDefn = + papoTableLayers[psColDef->table_index]->GetLayerDefn(); if( psColDef->field_index > -1 + && poLayerDefn != NULL && psColDef->field_index < poLayerDefn->GetFieldCount() ) + { poSrcFDefn = poLayerDefn->GetFieldDefn(psColDef->field_index); + } + + if( poLayerDefn != NULL && + IS_GEOM_FIELD_INDEX(poLayerDefn, psColDef->field_index) ) + { + bIsGeometry = TRUE; + iSrcGeomField = + ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX(poLayerDefn, psColDef->field_index); + poSrcGFDefn = poLayerDefn->GetGeomFieldDefn(iSrcGeomField); + } + + if( psColDef->target_type == SWQ_GEOMETRY ) + bIsGeometry = TRUE; + + if( psColDef->col_func == SWQCF_COUNT ) + bIsGeometry = FALSE; + + if( strlen(psColDef->field_name) == 0 && !bIsGeometry ) + { + CPLFree( psColDef->field_name ); + psColDef->field_name = (char *) CPLMalloc(40); + sprintf( psColDef->field_name, "FIELD_%d", poDefn->GetFieldCount()+1 ); + } if( psColDef->field_alias != NULL ) { - oFDefn.SetName(psColDef->field_alias); + if( bIsGeometry ) + oGFDefn.SetName(psColDef->field_alias); + else + oFDefn.SetName(psColDef->field_alias); } - else if( psColDef->col_func_name != NULL ) + else if( psColDef->col_func != SWQCF_NONE ) { - oFDefn.SetName( CPLSPrintf( "%s_%s",psColDef->col_func_name, - psColDef->field_name) ); + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) psColDef->col_func ); + + oFDefn.SetName( CPLSPrintf( "%s_%s", + op->pszName, + psColDef->field_name ) ); + } + else + { + if( bIsGeometry ) + oGFDefn.SetName(psColDef->field_name); + else + oFDefn.SetName( psColDef->field_name ); } if( psColDef->col_func == SWQCF_COUNT ) oFDefn.SetType( OFTInteger ); else if( poSrcFDefn != NULL ) { - oFDefn.SetType( poSrcFDefn->GetType() ); - oFDefn.SetWidth( poSrcFDefn->GetWidth() ); - oFDefn.SetPrecision( poSrcFDefn->GetPrecision() ); + if( psColDef->col_func != SWQCF_AVG || + psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP ) + oFDefn.SetType( poSrcFDefn->GetType() ); + else + oFDefn.SetType( OFTReal ); + if( psColDef->col_func != SWQCF_AVG && + psColDef->col_func != SWQCF_SUM ) + { + oFDefn.SetWidth( poSrcFDefn->GetWidth() ); + oFDefn.SetPrecision( poSrcFDefn->GetPrecision() ); + } + } + else if( poSrcGFDefn != NULL ) + { + oGFDefn.SetType( poSrcGFDefn->GetType() ); + oGFDefn.SetSpatialRef( poSrcGFDefn->GetSpatialRef() ); } else if ( psColDef->field_index >= iFIDFieldIndex ) { @@ -162,31 +293,56 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, break; } } + else + { + switch( psColDef->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + oFDefn.SetType( OFTInteger ); + break; + + case SWQ_FLOAT: + oFDefn.SetType( OFTReal ); + break; + + default: + oFDefn.SetType( OFTString ); + break; + } + } /* setting up the target_type */ switch (psColDef->target_type) { - case SWQ_OTHER: - break; - case SWQ_INTEGER: - case SWQ_BOOLEAN: - oFDefn.SetType( OFTInteger ); - break; - case SWQ_FLOAT: - oFDefn.SetType( OFTReal ); - break; - case SWQ_STRING: - oFDefn.SetType( OFTString ); - break; - case SWQ_TIMESTAMP: - oFDefn.SetType( OFTDateTime ); - break; - case SWQ_DATE: - oFDefn.SetType( OFTDate ); - break; - case SWQ_TIME: - oFDefn.SetType( OFTTime ); - break; + case SWQ_OTHER: + break; + case SWQ_INTEGER: + case SWQ_BOOLEAN: + oFDefn.SetType( OFTInteger ); + break; + case SWQ_FLOAT: + oFDefn.SetType( OFTReal ); + break; + case SWQ_STRING: + oFDefn.SetType( OFTString ); + break; + case SWQ_TIMESTAMP: + oFDefn.SetType( OFTDateTime ); + break; + case SWQ_DATE: + oFDefn.SetType( OFTDate ); + break; + case SWQ_TIME: + oFDefn.SetType( OFTTime ); + break; + case SWQ_GEOMETRY: + break; + + default: + CPLAssert( FALSE ); + oFDefn.SetType( OFTString ); + break; } if (psColDef->field_length > 0) @@ -199,19 +355,95 @@ OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, oFDefn.SetPrecision( psColDef->field_precision ); } - poDefn->AddFieldDefn( &oFDefn ); + if( bIsGeometry ) + { + panGeomFieldToSrcGeomField[poDefn->GetGeomFieldCount()] = iSrcGeomField; + /* Hack while drivers haven't been updated so that */ + /* poSrcDefn->GetGeomFieldDefn(0)->GetSpatialRef() == poSrcLayer->GetSpatialRef() */ + if( iSrcGeomField == 0 && + poSrcDefn->GetGeomFieldCount() == 1 && + oGFDefn.GetSpatialRef() == NULL ) + { + oGFDefn.SetSpatialRef(poSrcLayer->GetSpatialRef()); + } + int bForceGeomType = FALSE; + if( psColDef->eGeomType != wkbUnknown ) + { + oGFDefn.SetType( psColDef->eGeomType ); + bForceGeomType = TRUE; + } + if( psColDef->nSRID > 0 ) + { + OGRSpatialReference* poSRS = new OGRSpatialReference(); + if( poSRS->importFromEPSG( psColDef->nSRID ) == OGRERR_NONE ) + { + oGFDefn.SetSpatialRef( poSRS ); + } + poSRS->Release(); + } + + OGRGenSQLGeomFieldDefn* poMyGeomFieldDefn = + new OGRGenSQLGeomFieldDefn(&oGFDefn); + poMyGeomFieldDefn->bForceGeomType = bForceGeomType; + poDefn->AddGeomFieldDefn( poMyGeomFieldDefn, FALSE ); + } + else + poDefn->AddFieldDefn( &oFDefn ); } - poDefn->SetGeomType( poSrcLayer->GetLayerDefn()->GetGeomType() ); +/* -------------------------------------------------------------------- */ +/* Add implicit geometry field. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->query_mode == SWQM_RECORDSET && + poDefn->GetGeomFieldCount() == 0 && + poSrcDefn->GetGeomFieldCount() == 1 ) + { + psSelectInfo->result_columns++; + + psSelectInfo->column_defs = (swq_col_def *) + CPLRealloc( psSelectInfo->column_defs, sizeof(swq_col_def) * psSelectInfo->result_columns ); + + swq_col_def *col_def = psSelectInfo->column_defs + psSelectInfo->result_columns - 1; + + memset( col_def, 0, sizeof(swq_col_def) ); + const char* pszName = poSrcDefn->GetGeomFieldDefn(0)->GetNameRef(); + if( *pszName != '\0' ) + col_def->field_name = CPLStrdup( pszName ); + else + col_def->field_name = CPLStrdup( OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME ); + col_def->field_alias = NULL; + col_def->table_index = 0; + col_def->field_index = GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poSrcDefn, 0); + col_def->field_type = SWQ_GEOMETRY; + col_def->target_type = SWQ_GEOMETRY; + + panGeomFieldToSrcGeomField[poDefn->GetGeomFieldCount()] = 0; + + OGRGenSQLGeomFieldDefn* poMyGeomFieldDefn = + new OGRGenSQLGeomFieldDefn(poSrcDefn->GetGeomFieldDefn(0)); + poDefn->AddGeomFieldDefn( poMyGeomFieldDefn, FALSE ); + + /* Hack while drivers haven't been updated so that */ + /* poSrcDefn->GetGeomFieldDefn(0)->GetSpatialRef() == poSrcLayer->GetSpatialRef() */ + if( poSrcDefn->GetGeomFieldDefn(0)->GetSpatialRef() == NULL ) + { + poDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSrcLayer->GetSpatialRef()); + } + } /* -------------------------------------------------------------------- */ -/* If an ORDER BY is in effect, apply it now. */ +/* Now that we have poSrcLayer, we can install a spatial filter */ +/* if there is one. */ /* -------------------------------------------------------------------- */ - if( psSelectInfo->order_specs > 0 - && psSelectInfo->query_mode == SWQM_RECORDSET ) - CreateOrderByIndex(); + if( poSpatFilter != NULL ) + SetSpatialFilter( 0, poSpatFilter ); ResetReading(); + + FindAndSetIgnoredFields(); + + if( !bForwardWhereToSourceLayer ) + SetAttributeFilter( pszWHERE ); } /************************************************************************/ @@ -236,14 +468,11 @@ OGRGenSQLResultsLayer::~OGRGenSQLResultsLayer() CPLFree( papoTableLayers ); papoTableLayers = NULL; - if( panFIDIndex != NULL ) - CPLFree( panFIDIndex ); + CPLFree( panFIDIndex ); + CPLFree( panGeomFieldToSrcGeomField ); - if( poSummaryFeature ) - delete poSummaryFeature; - - if( pSelectInfo != NULL ) - swq_select_free( (swq_select *) pSelectInfo ); + delete poSummaryFeature; + delete (swq_select *) pSelectInfo; if( poDefn != NULL ) { @@ -253,12 +482,11 @@ OGRGenSQLResultsLayer::~OGRGenSQLResultsLayer() /* -------------------------------------------------------------------- */ /* Release any additional datasources being used in joins. */ /* -------------------------------------------------------------------- */ - OGRSFDriverRegistrar *poReg=OGRSFDriverRegistrar::GetRegistrar(); - for( int iEDS = 0; iEDS < nExtraDSCount; iEDS++ ) - poReg->ReleaseDataSource( papoExtraDS[iEDS] ); + GDALClose( (GDALDatasetH)papoExtraDS[iEDS] ); CPLFree( papoExtraDS ); + CPLFree( pszWHERE ); } /************************************************************************/ @@ -298,6 +526,54 @@ void OGRGenSQLResultsLayer::ClearFilters() poJoinLayer->SetAttributeFilter( "" ); } } + +/* -------------------------------------------------------------------- */ +/* Clear any ignored field lists installed on source layers */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo != NULL ) + { + for( int iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + OGRLayer* poLayer = papoTableLayers[iTable]; + poLayer->SetIgnoredFields(NULL); + } + } +} + +/************************************************************************/ +/* MustEvaluateSpatialFilterOnGenSQL() */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::MustEvaluateSpatialFilterOnGenSQL() +{ + int bEvaluateSpatialFilter = FALSE; + if( m_poFilterGeom != NULL && + m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount() ) + { + int iSrcGeomField = panGeomFieldToSrcGeomField[m_iGeomFieldFilter]; + if( iSrcGeomField < 0 ) + bEvaluateSpatialFilter = TRUE; + } + return bEvaluateSpatialFilter; +} + +/************************************************************************/ +/* ApplyFiltersToSource() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::ApplyFiltersToSource() +{ + poSrcLayer->SetAttributeFilter( pszWHERE ); + if( m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount() ) + { + int iSrcGeomField = panGeomFieldToSrcGeomField[m_iGeomFieldFilter]; + if( iSrcGeomField >= 0 ) + poSrcLayer->SetSpatialFilter( iSrcGeomField, m_poFilterGeom ); + } + + poSrcLayer->ResetReading(); } /************************************************************************/ @@ -311,11 +587,7 @@ void OGRGenSQLResultsLayer::ResetReading() if( psSelectInfo->query_mode == SWQM_RECORDSET ) { - poSrcLayer->SetAttributeFilter( psSelectInfo->whole_where_clause ); - - poSrcLayer->SetSpatialFilter( m_poFilterGeom ); - - poSrcLayer->ResetReading(); + ApplyFiltersToSource(); } nNextIndexFID = 0; @@ -333,6 +605,8 @@ OGRErr OGRGenSQLResultsLayer::SetNextByIndex( long nIndex ) { swq_select *psSelectInfo = (swq_select *) pSelectInfo; + CreateOrderByIndex(); + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD || psSelectInfo->query_mode == SWQM_DISTINCT_LIST || panFIDIndex != NULL ) @@ -350,31 +624,36 @@ OGRErr OGRGenSQLResultsLayer::SetNextByIndex( long nIndex ) /* GetExtent() */ /************************************************************************/ -OGRErr OGRGenSQLResultsLayer::GetExtent( OGREnvelope *psExtent, +OGRErr OGRGenSQLResultsLayer::GetExtent( int iGeomField, + OGREnvelope *psExtent, int bForce ) { swq_select *psSelectInfo = (swq_select *) pSelectInfo; - if( psSelectInfo->query_mode == SWQM_RECORDSET ) - return poSrcLayer->GetExtent( psExtent, bForce ); - else + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } return OGRERR_FAILURE; -} - -/************************************************************************/ -/* GetSpatialRef() */ -/************************************************************************/ - -OGRSpatialReference *OGRGenSQLResultsLayer::GetSpatialRef() - -{ - swq_select *psSelectInfo = (swq_select *) pSelectInfo; + } - if( psSelectInfo->query_mode != SWQM_RECORDSET ) - return NULL; + if( psSelectInfo->query_mode == SWQM_RECORDSET ) + { + int iSrcGeomField = panGeomFieldToSrcGeomField[iGeomField]; + if( iSrcGeomField >= 0 ) + return poSrcLayer->GetExtent( iSrcGeomField, psExtent, bForce ); + else if( iGeomField == 0 ) + return OGRLayer::GetExtent( psExtent, bForce ); + else + return OGRLayer::GetExtent( iGeomField, psExtent, bForce ); + } else - return poSrcLayer->GetSpatialRef(); + return OGRERR_FAILURE; } /************************************************************************/ @@ -386,6 +665,8 @@ int OGRGenSQLResultsLayer::GetFeatureCount( int bForce ) { swq_select *psSelectInfo = (swq_select *) pSelectInfo; + CreateOrderByIndex(); + if( psSelectInfo->query_mode == SWQM_DISTINCT_LIST ) { if( !PrepareSummary() ) @@ -400,7 +681,7 @@ int OGRGenSQLResultsLayer::GetFeatureCount( int bForce ) } else if( psSelectInfo->query_mode != SWQM_RECORDSET ) return 1; - else if( m_poAttrQuery == NULL ) + else if( m_poAttrQuery == NULL && !MustEvaluateSpatialFilterOnGenSQL() ) return poSrcLayer->GetFeatureCount( bForce ); else return OGRLayer::GetFeatureCount( bForce ); @@ -436,7 +717,40 @@ int OGRGenSQLResultsLayer::TestCapability( const char *pszCap ) if( EQUAL(pszCap,OLCFastFeatureCount) ) return TRUE; } + return FALSE; +} + +/************************************************************************/ +/* ContainGeomSpecialField() */ +/************************************************************************/ +int OGRGenSQLResultsLayer::ContainGeomSpecialField(swq_expr_node* expr) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if( expr->table_index == 0 && expr->field_index != -1 ) + { + OGRLayer* poLayer = papoTableLayers[expr->table_index]; + int nSpecialFieldIdx = expr->field_index - + poLayer->GetLayerDefn()->GetFieldCount(); + if( nSpecialFieldIdx == SPF_OGR_GEOMETRY || + nSpecialFieldIdx == SPF_OGR_GEOM_WKT || + nSpecialFieldIdx == SPF_OGR_GEOM_AREA ) + return TRUE; + if( expr->field_index == + GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poLayer->GetLayerDefn(), 0) ) + return TRUE; + return FALSE; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (ContainGeomSpecialField(expr->papoSubExpr[i])) + return TRUE; + } + } return FALSE; } @@ -459,22 +773,61 @@ int OGRGenSQLResultsLayer::PrepareSummary() /* Ensure our query parameters are in place on the source */ /* layer. And initialize reading. */ /* -------------------------------------------------------------------- */ - poSrcLayer->SetAttributeFilter( psSelectInfo->whole_where_clause ); - - poSrcLayer->SetSpatialFilter( m_poFilterGeom ); - - poSrcLayer->ResetReading(); + ApplyFiltersToSource(); /* -------------------------------------------------------------------- */ -/* We treat COUNT(*) (or COUNT of anything without distinct) as */ -/* a special case, and fill with GetFeatureCount(). */ +/* Ignore geometry reading if no spatial filter in place and that */ +/* the where clause and no column references OGR_GEOMETRY, */ +/* OGR_GEOM_WKT or OGR_GEOM_AREA special fields. */ +/* -------------------------------------------------------------------- */ + int bSaveIsGeomIgnored = poSrcLayer->GetLayerDefn()->IsGeometryIgnored(); + if ( m_poFilterGeom == NULL && ( psSelectInfo->where_expr == NULL || + !ContainGeomSpecialField(psSelectInfo->where_expr) ) ) + { + int bFoundGeomExpr = FALSE; + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + if (psColDef->table_index == 0 && psColDef->field_index != -1) + { + OGRLayer* poLayer = papoTableLayers[psColDef->table_index]; + int nSpecialFieldIdx = psColDef->field_index - + poLayer->GetLayerDefn()->GetFieldCount(); + if (nSpecialFieldIdx == SPF_OGR_GEOMETRY || + nSpecialFieldIdx == SPF_OGR_GEOM_WKT || + nSpecialFieldIdx == SPF_OGR_GEOM_AREA) + { + bFoundGeomExpr = TRUE; + break; + } + if( psColDef->field_index == + GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poLayer->GetLayerDefn(), 0) ) + { + bFoundGeomExpr = TRUE; + break; + } + } + if (psColDef->expr != NULL && ContainGeomSpecialField(psColDef->expr)) + { + bFoundGeomExpr = TRUE; + break; + } + } + if (!bFoundGeomExpr) + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(TRUE); + } + +/* -------------------------------------------------------------------- */ +/* We treat COUNT(*) as a special case, and fill with */ +/* GetFeatureCount(). */ /* -------------------------------------------------------------------- */ if( psSelectInfo->result_columns == 1 && psSelectInfo->column_defs[0].col_func == SWQCF_COUNT - && !psSelectInfo->column_defs[0].distinct_flag ) + && psSelectInfo->column_defs[0].field_index < 0 ) { poSummaryFeature->SetField( 0, poSrcLayer->GetFeatureCount( TRUE ) ); + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(bSaveIsGeomIgnored); return TRUE; } @@ -484,22 +837,52 @@ int OGRGenSQLResultsLayer::PrepareSummary() /* -------------------------------------------------------------------- */ const char *pszError; OGRFeature *poSrcFeature; + int iField; while( (poSrcFeature = poSrcLayer->GetNextFeature()) != NULL ) { - for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + for( iField = 0; iField < psSelectInfo->result_columns; iField++ ) { swq_col_def *psColDef = psSelectInfo->column_defs + iField; - pszError = swq_select_summarize( psSelectInfo, iField, - poSrcFeature->GetFieldAsString( - psColDef->field_index ) ); + if (psColDef->col_func == SWQCF_COUNT) + { + /* psColDef->field_index can be -1 in the case of a COUNT(*) */ + if (psColDef->field_index < 0) + pszError = swq_select_summarize( psSelectInfo, iField, "" ); + else if (IS_GEOM_FIELD_INDEX(poSrcLayer->GetLayerDefn(), psColDef->field_index) ) + { + int iSrcGeomField = ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX( + poSrcLayer->GetLayerDefn(), psColDef->field_index); + OGRGeometry* poGeom = poSrcFeature->GetGeomFieldRef(iSrcGeomField); + if( poGeom != NULL ) + pszError = swq_select_summarize( psSelectInfo, iField, "" ); + else + pszError = NULL; + } + else if (poSrcFeature->IsFieldSet(psColDef->field_index)) + pszError = swq_select_summarize( psSelectInfo, iField, poSrcFeature->GetFieldAsString( + psColDef->field_index ) ); + else + pszError = NULL; + } + else + { + const char* pszVal = NULL; + if (poSrcFeature->IsFieldSet(psColDef->field_index)) + pszVal = poSrcFeature->GetFieldAsString( + psColDef->field_index ); + pszError = swq_select_summarize( psSelectInfo, iField, pszVal ); + } if( pszError != NULL ) { + delete poSrcFeature; delete poSummaryFeature; poSummaryFeature = NULL; + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(bSaveIsGeomIgnored); + CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); return FALSE; } @@ -508,6 +891,8 @@ int OGRGenSQLResultsLayer::PrepareSummary() delete poSrcFeature; } + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(bSaveIsGeomIgnored); + pszError = swq_select_finish_summarize( psSelectInfo ); if( pszError != NULL ) { @@ -530,45 +915,255 @@ int OGRGenSQLResultsLayer::PrepareSummary() /* Now apply the values to the summary feature. If we are in */ /* DISTINCT_LIST mode we don't do this step. */ /* -------------------------------------------------------------------- */ - if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD - && psSelectInfo->column_summary != NULL ) + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD ) { - for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + for( iField = 0; iField < psSelectInfo->result_columns; iField++ ) { swq_col_def *psColDef = psSelectInfo->column_defs + iField; - swq_summary *psSummary = psSelectInfo->column_summary + iField; - - if( psColDef->col_func == SWQCF_AVG ) - poSummaryFeature->SetField( iField, - psSummary->sum / psSummary->count ); - else if( psColDef->col_func == SWQCF_MIN ) - poSummaryFeature->SetField( iField, psSummary->min ); - else if( psColDef->col_func == SWQCF_MAX ) - poSummaryFeature->SetField( iField, psSummary->max ); - else if( psColDef->col_func == SWQCF_COUNT ) - poSummaryFeature->SetField( iField, psSummary->count ); - else if( psColDef->col_func == SWQCF_SUM ) - poSummaryFeature->SetField( iField, psSummary->sum ); + if (psSelectInfo->column_summary != NULL) + { + swq_summary *psSummary = psSelectInfo->column_summary + iField; + + if( psColDef->col_func == SWQCF_AVG ) + { + if( psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP) + { + struct tm brokendowntime; + CPLUnixTimeToYMDHMS((GIntBig)(psSummary->sum / psSummary->count), &brokendowntime); + poSummaryFeature->SetField( iField, + brokendowntime.tm_year + 1900, + brokendowntime.tm_mon + 1, + brokendowntime.tm_mday, + brokendowntime.tm_hour, + brokendowntime.tm_min, + brokendowntime.tm_sec, 0); + } + else + poSummaryFeature->SetField( iField, + psSummary->sum / psSummary->count ); + } + else if( psColDef->col_func == SWQCF_MIN ) + { + if( psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP) + poSummaryFeature->SetField( iField, psSummary->szMin ); + else + poSummaryFeature->SetField( iField, psSummary->min ); + } + else if( psColDef->col_func == SWQCF_MAX ) + { + if( psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP) + poSummaryFeature->SetField( iField, psSummary->szMax ); + else + poSummaryFeature->SetField( iField, psSummary->max ); + } + else if( psColDef->col_func == SWQCF_COUNT ) + poSummaryFeature->SetField( iField, psSummary->count ); + else if( psColDef->col_func == SWQCF_SUM ) + poSummaryFeature->SetField( iField, psSummary->sum ); + } + else if ( psColDef->col_func == SWQCF_COUNT ) + poSummaryFeature->SetField( iField, 0 ); } } return TRUE; } -/************************************************************************/ -/* TranslateFeature() */ -/************************************************************************/ +/************************************************************************/ +/* OGRMultiFeatureFetcher() */ +/************************************************************************/ + +static swq_expr_node *OGRMultiFeatureFetcher( swq_expr_node *op, + void *pFeatureList ) + +{ + std::vector *papoFeatures = + (std::vector *) pFeatureList; + OGRFeature *poFeature; + swq_expr_node *poRetNode = NULL; + + CPLAssert( op->eNodeType == SNT_COLUMN ); + +/* -------------------------------------------------------------------- */ +/* What feature are we using? The primary or one of the joined ones?*/ +/* -------------------------------------------------------------------- */ + if( op->table_index < 0 || op->table_index >= (int)papoFeatures->size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Request for unexpected table_index (%d) in field fetcher.", + op->table_index ); + return NULL; + } + + poFeature = (*papoFeatures)[op->table_index]; + +/* -------------------------------------------------------------------- */ +/* Fetch the value. */ +/* -------------------------------------------------------------------- */ + switch( op->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node(0); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsInteger(op->field_index) ); + break; + + case SWQ_FLOAT: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node( 0.0 ); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsDouble(op->field_index) ); + break; + + case SWQ_GEOMETRY: + if( poFeature == NULL ) + { + poRetNode = new swq_expr_node( (OGRGeometry*) NULL ); + } + else + { + int iSrcGeomField = ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX( + poFeature->GetDefnRef(), op->field_index); + poRetNode = new swq_expr_node( + poFeature->GetGeomFieldRef(iSrcGeomField) ); + } + break; + + default: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node(""); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsString(op->field_index) ); + break; + } + + return poRetNode; +} + +/************************************************************************/ +/* TranslateFeature() */ +/************************************************************************/ + +OGRFeature *OGRGenSQLResultsLayer::TranslateFeature( OGRFeature *poSrcFeat ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + OGRFeature *poDstFeat; + std::vector apoFeatures; + + if( poSrcFeat == NULL ) + return NULL; + + m_nFeaturesRead++; + + apoFeatures.push_back( poSrcFeat ); + +/* -------------------------------------------------------------------- */ +/* Fetch the corresponding features from any jointed tables. */ +/* -------------------------------------------------------------------- */ + int iJoin; + + for( iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) + { + CPLString osFilter; + + swq_join_def *psJoinInfo = psSelectInfo->join_defs + iJoin; + + /* OGRMultiFeatureFetcher assumes that the features are pushed in */ + /* apoFeatures with increasing secondary_table, so make sure */ + /* we have taken care of this */ + CPLAssert(psJoinInfo->secondary_table == iJoin + 1); + + OGRLayer *poJoinLayer = papoTableLayers[psJoinInfo->secondary_table]; + + // if source key is null, we can't do join. + if( !poSrcFeat->IsFieldSet( psJoinInfo->primary_field ) ) + { + apoFeatures.push_back( NULL ); + continue; + } + + OGRFieldDefn* poSecondaryFieldDefn = + poJoinLayer->GetLayerDefn()->GetFieldDefn( + psJoinInfo->secondary_field ); + OGRFieldType ePrimaryFieldType = poSrcLayer->GetLayerDefn()-> + GetFieldDefn(psJoinInfo->primary_field )->GetType(); + OGRFieldType eSecondaryFieldType = poSecondaryFieldDefn->GetType(); + + // Prepare attribute query to express fetching on the joined variable + + // If joining a (primary) numeric column with a (secondary) string column + // then add implicit casting of the secondary column to numeric. This behaviour + // worked in GDAL < 1.8, and it is consistant with how sqlite behaves too. See #4321 + // For the reverse case, joining a string column with a numeric column, the + // string constant will be cast to float by SWQAutoConvertStringToNumeric (#4259) + if( eSecondaryFieldType == OFTString && + (ePrimaryFieldType == OFTInteger || ePrimaryFieldType == OFTReal) ) + osFilter.Printf("CAST(%s AS FLOAT) = ", poSecondaryFieldDefn->GetNameRef() ); + else + osFilter.Printf("%s = ", poSecondaryFieldDefn->GetNameRef() ); + + OGRField *psSrcField = + poSrcFeat->GetRawFieldRef(psJoinInfo->primary_field); + + switch( ePrimaryFieldType ) + { + case OFTInteger: + osFilter += CPLString().Printf("%d", psSrcField->Integer ); + break; + + case OFTReal: + osFilter += CPLString().Printf("%.16g", psSrcField->Real ); + break; -OGRFeature *OGRGenSQLResultsLayer::TranslateFeature( OGRFeature *poSrcFeat ) + case OFTString: + { + char *pszEscaped = CPLEscapeString( psSrcField->String, + strlen(psSrcField->String), + CPLES_SQL ); + osFilter += "'"; + osFilter += pszEscaped; + osFilter += "'"; + CPLFree( pszEscaped ); + } + break; -{ - swq_select *psSelectInfo = (swq_select *) pSelectInfo; - OGRFeature *poDstFeat; + default: + CPLAssert( FALSE ); + continue; + } - if( poSrcFeat == NULL ) - return NULL; + OGRFeature *poJoinFeature = NULL; - m_nFeaturesRead++; + poJoinLayer->ResetReading(); + if( poJoinLayer->SetAttributeFilter( osFilter.c_str() ) == OGRERR_NONE ) + poJoinFeature = poJoinLayer->GetNextFeature(); + + apoFeatures.push_back( poJoinFeature ); + } /* -------------------------------------------------------------------- */ /* Create destination feature. */ @@ -577,139 +1172,205 @@ OGRFeature *OGRGenSQLResultsLayer::TranslateFeature( OGRFeature *poSrcFeat ) poDstFeat->SetFID( poSrcFeat->GetFID() ); - poDstFeat->SetGeometry( poSrcFeat->GetGeometryRef() ); - poDstFeat->SetStyleString( poSrcFeat->GetStyleString() ); + +/* -------------------------------------------------------------------- */ +/* Evaluate fields that are complex expressions. */ +/* -------------------------------------------------------------------- */ + int iRegularField = 0; + int iGeomField = 0; + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + swq_expr_node *poResult; + + if( psColDef->field_index != -1 ) + { + if( psColDef->field_type == SWQ_GEOMETRY || + psColDef->target_type == SWQ_GEOMETRY ) + iGeomField++; + else + iRegularField++; + continue; + } + + poResult = psColDef->expr->Evaluate( OGRMultiFeatureFetcher, + (void *) &apoFeatures ); + + if( poResult == NULL ) + { + delete poDstFeat; + return NULL; + } + + if( poResult->is_null ) + { + if( poResult->field_type == SWQ_GEOMETRY ) + iGeomField++; + else + iRegularField++; + delete poResult; + continue; + } + + switch( poResult->field_type ) + { + case SWQ_INTEGER: + poDstFeat->SetField( iRegularField++, poResult->int_value ); + break; + + case SWQ_FLOAT: + poDstFeat->SetField( iRegularField++, poResult->float_value ); + break; + + case SWQ_GEOMETRY: + { + OGRGenSQLGeomFieldDefn* poGeomFieldDefn = + (OGRGenSQLGeomFieldDefn*) poDstFeat->GetGeomFieldDefnRef(iGeomField); + if( poGeomFieldDefn->bForceGeomType && + poResult->geometry_value != NULL ) + { + OGRwkbGeometryType eCurType = + wkbFlatten(poResult->geometry_value->getGeometryType()); + OGRwkbGeometryType eReqType = + wkbFlatten(poGeomFieldDefn->GetType()); + if( eCurType == wkbPolygon && eReqType == wkbMultiPolygon ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToMultiPolygon( (OGRGeometryH) poResult->geometry_value ); + } + else if( (eCurType == wkbMultiPolygon || eCurType == wkbGeometryCollection) && + eReqType == wkbPolygon ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToPolygon( (OGRGeometryH) poResult->geometry_value ); + } + else if( eCurType == wkbLineString && eReqType == wkbMultiLineString ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToMultiLineString( (OGRGeometryH) poResult->geometry_value ); + } + else if( (eCurType == wkbMultiLineString || eCurType == wkbGeometryCollection) && + eReqType == wkbLineString ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToLineString( (OGRGeometryH) poResult->geometry_value ); + } + } + poDstFeat->SetGeomField( iGeomField++, poResult->geometry_value ); + break; + } + + default: + poDstFeat->SetField( iRegularField++, poResult->string_value ); + break; + } + + delete poResult; + } /* -------------------------------------------------------------------- */ /* Copy fields from primary record to the destination feature. */ /* -------------------------------------------------------------------- */ + iRegularField = 0; + iGeomField = 0; for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) { swq_col_def *psColDef = psSelectInfo->column_defs + iField; if( psColDef->table_index != 0 ) + { + if( psColDef->field_type == SWQ_GEOMETRY || + psColDef->target_type == SWQ_GEOMETRY ) + iGeomField++; + else + iRegularField++; continue; + } - if ( psColDef->field_index >= iFIDFieldIndex && - psColDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT ) + if( IS_GEOM_FIELD_INDEX(poSrcFeat->GetDefnRef(), psColDef->field_index) ) + { + int iSrcGeomField = ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX( + poSrcFeat->GetDefnRef(), psColDef->field_index); + poDstFeat->SetGeomField( iGeomField ++, + poSrcFeat->GetGeomFieldRef(iSrcGeomField) ); + } + else if( psColDef->field_index >= iFIDFieldIndex && + psColDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT ) { switch (SpecialFieldTypes[psColDef->field_index - iFIDFieldIndex]) { case SWQ_INTEGER: - poDstFeat->SetField( iField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) ); + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) ); break; case SWQ_FLOAT: - poDstFeat->SetField( iField, poSrcFeat->GetFieldAsDouble(psColDef->field_index) ); + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsDouble(psColDef->field_index) ); break; default: - poDstFeat->SetField( iField, poSrcFeat->GetFieldAsString(psColDef->field_index) ); + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsString(psColDef->field_index) ); } + iRegularField ++; } else { switch (psColDef->target_type) { case SWQ_INTEGER: - poDstFeat->SetField( iField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) ); + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) ); break; case SWQ_FLOAT: - poDstFeat->SetField( iField, poSrcFeat->GetFieldAsDouble(psColDef->field_index) ); + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsDouble(psColDef->field_index) ); break; case SWQ_STRING: case SWQ_TIMESTAMP: case SWQ_DATE: case SWQ_TIME: - poDstFeat->SetField( iField, poSrcFeat->GetFieldAsString(psColDef->field_index) ); + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsString(psColDef->field_index) ); break; + + case SWQ_GEOMETRY: + CPLAssert(0); + break; default: - poDstFeat->SetField( iField, + poDstFeat->SetField( iRegularField, poSrcFeat->GetRawFieldRef( psColDef->field_index ) ); } + iRegularField ++; } } /* -------------------------------------------------------------------- */ /* Copy values from any joined tables. */ /* -------------------------------------------------------------------- */ - int iJoin; - for( iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) { - char szFilter[512]; + CPLString osFilter; swq_join_def *psJoinInfo = psSelectInfo->join_defs + iJoin; - OGRLayer *poJoinLayer = papoTableLayers[psJoinInfo->secondary_table]; - - // if source key is null, we can't do join. - if( !poSrcFeat->IsFieldSet( psJoinInfo->primary_field ) ) - continue; - - // Prepare attribute query to express fetching on the joined variable - sprintf( szFilter, "%s = ", - poJoinLayer->GetLayerDefn()->GetFieldDefn( - psJoinInfo->secondary_field )->GetNameRef() ); - - OGRField *psSrcField = - poSrcFeat->GetRawFieldRef(psJoinInfo->primary_field); - - switch( poSrcLayer->GetLayerDefn()->GetFieldDefn( - psJoinInfo->primary_field )->GetType() ) - { - case OFTInteger: - sprintf( szFilter+strlen(szFilter), "%d", psSrcField->Integer ); - break; - - case OFTReal: - sprintf( szFilter+strlen(szFilter), "%.16g", psSrcField->Real ); - break; - - case OFTString: - { - char *pszEscaped = CPLEscapeString( psSrcField->String, - strlen(psSrcField->String), - CPLES_SQL ); - if( strlen(pszEscaped) + strlen(szFilter) < sizeof(szFilter)-3 ) - sprintf( szFilter+strlen(szFilter), "\'%s\'", - pszEscaped ); - else - { - strcat( szFilter, "' '" ); - CPLDebug( "GenSQL", "Skip long join field value." ); - } - CPLFree( pszEscaped ); - } - break; - - default: - CPLAssert( FALSE ); - continue; - } - - poJoinLayer->ResetReading(); - if( poJoinLayer->SetAttributeFilter( szFilter ) != OGRERR_NONE ) - continue; - - // Fetch first joined feature. - OGRFeature *poJoinFeature; - - poJoinFeature = poJoinLayer->GetNextFeature(); + OGRFeature *poJoinFeature = apoFeatures[iJoin+1]; if( poJoinFeature == NULL ) continue; // Copy over selected field values. + iRegularField = 0; for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) { swq_col_def *psColDef = psSelectInfo->column_defs + iField; - + + if( psColDef->field_type == SWQ_GEOMETRY || + psColDef->target_type == SWQ_GEOMETRY ) + continue; + if( psColDef->table_index == psJoinInfo->secondary_table ) - poDstFeat->SetField( iField, + poDstFeat->SetField( iRegularField, poJoinFeature->GetRawFieldRef( psColDef->field_index ) ); + + iRegularField ++; } delete poJoinFeature; @@ -727,6 +1388,8 @@ OGRFeature *OGRGenSQLResultsLayer::GetNextFeature() { swq_select *psSelectInfo = (swq_select *) pSelectInfo; + CreateOrderByIndex(); + /* -------------------------------------------------------------------- */ /* Handle summary sets. */ /* -------------------------------------------------------------------- */ @@ -734,6 +1397,8 @@ OGRFeature *OGRGenSQLResultsLayer::GetNextFeature() || psSelectInfo->query_mode == SWQM_DISTINCT_LIST ) return GetFeature( nNextIndexFID++ ); + int bEvaluateSpatialFilter = MustEvaluateSpatialFilterOnGenSQL(); + /* -------------------------------------------------------------------- */ /* Handle ordered sets. */ /* -------------------------------------------------------------------- */ @@ -757,8 +1422,10 @@ OGRFeature *OGRGenSQLResultsLayer::GetNextFeature() if( poFeature == NULL ) return NULL; - if( m_poAttrQuery == NULL - || m_poAttrQuery->Evaluate( poFeature ) ) + if( (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) && + (!bEvaluateSpatialFilter || + FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) )) ) return poFeature; delete poFeature; @@ -776,6 +1443,8 @@ OGRFeature *OGRGenSQLResultsLayer::GetFeature( long nFID ) { swq_select *psSelectInfo = (swq_select *) pSelectInfo; + CreateOrderByIndex(); + /* -------------------------------------------------------------------- */ /* Handle request for summary record. */ /* -------------------------------------------------------------------- */ @@ -803,7 +1472,10 @@ OGRFeature *OGRGenSQLResultsLayer::GetFeature( long nFID ) if( nFID < 0 || nFID >= psSummary->count ) return NULL; - poSummaryFeature->SetField( 0, psSummary->distinct_list[nFID] ); + if( psSummary->distinct_list[nFID] != NULL ) + poSummaryFeature->SetField( 0, psSummary->distinct_list[nFID] ); + else + poSummaryFeature->UnsetField( 0 ); poSummaryFeature->SetFID( nFID ); return poSummaryFeature->Clone(); @@ -883,41 +1555,77 @@ void OGRGenSQLResultsLayer::CreateOrderByIndex() int i, nOrderItems = psSelectInfo->order_specs; long *panFIDList; - if( nOrderItems == 0 ) + if( ! (psSelectInfo->order_specs > 0 + && psSelectInfo->query_mode == SWQM_RECORDSET + && nOrderItems != 0 ) ) return; + if( bOrderByValid ) + return; + + bOrderByValid = TRUE; + ResetReading(); /* -------------------------------------------------------------------- */ /* Allocate set of key values, and the output index. */ /* -------------------------------------------------------------------- */ - nIndexSize = poSrcLayer->GetFeatureCount(); + int nFeaturesAlloc = 100; + panFIDIndex = NULL; pasIndexFields = (OGRField *) - CPLCalloc(sizeof(OGRField), nOrderItems * nIndexSize); - panFIDIndex = (long *) CPLCalloc(sizeof(long),nIndexSize); - panFIDList = (long *) CPLCalloc(sizeof(long),nIndexSize); - - for( i = 0; i < nIndexSize; i++ ) - panFIDIndex[i] = i; + CPLCalloc(sizeof(OGRField), nOrderItems * nFeaturesAlloc); + panFIDList = (long *) CPLMalloc(sizeof(long) * nFeaturesAlloc); /* -------------------------------------------------------------------- */ /* Read in all the key values. */ /* -------------------------------------------------------------------- */ OGRFeature *poSrcFeat; - int iFeature = 0; + nIndexSize = 0; while( (poSrcFeat = poSrcLayer->GetNextFeature()) != NULL ) { int iKey; + if (nIndexSize == nFeaturesAlloc) + { + int nNewFeaturesAlloc = (nFeaturesAlloc * 4) / 3; + OGRField* pasNewIndexFields = (OGRField *) + VSIRealloc(pasIndexFields, + sizeof(OGRField) * nOrderItems * nNewFeaturesAlloc); + if (pasNewIndexFields == NULL) + { + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + return; + } + pasIndexFields = pasNewIndexFields; + + long* panNewFIDList = (long *) + VSIRealloc(panFIDList, sizeof(long) * nNewFeaturesAlloc); + if (panNewFIDList == NULL) + { + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + return; + } + panFIDList = panNewFIDList; + + memset(pasIndexFields + nFeaturesAlloc, 0, + sizeof(OGRField) * nOrderItems * (nNewFeaturesAlloc - nFeaturesAlloc)); + + nFeaturesAlloc = nNewFeaturesAlloc; + } + for( iKey = 0; iKey < nOrderItems; iKey++ ) { swq_order_def *psKeyDef = psSelectInfo->order_defs + iKey; OGRFieldDefn *poFDefn; OGRField *psSrcField, *psDstField; - psDstField = pasIndexFields + iFeature * nOrderItems + iKey; + psDstField = pasIndexFields + nIndexSize * nOrderItems + iKey; if ( psKeyDef->field_index >= iFIDFieldIndex) { @@ -961,13 +1669,20 @@ void OGRGenSQLResultsLayer::CreateOrderByIndex() } } - panFIDList[iFeature] = poSrcFeat->GetFID(); + panFIDList[nIndexSize] = poSrcFeat->GetFID(); delete poSrcFeat; - iFeature++; + nIndexSize++; } - CPLAssert( nIndexSize == iFeature ); + //CPLDebug("GenSQL", "CreateOrderByIndex() = %d features", nIndexSize); + +/* -------------------------------------------------------------------- */ +/* Initialize panFIDIndex */ +/* -------------------------------------------------------------------- */ + panFIDIndex = (long *) CPLMalloc(sizeof(long) * nIndexSize); + for( i = 0; i < nIndexSize; i++ ) + panFIDIndex[i] = i; /* -------------------------------------------------------------------- */ /* Quick sort the records. */ @@ -977,8 +1692,13 @@ void OGRGenSQLResultsLayer::CreateOrderByIndex() /* -------------------------------------------------------------------- */ /* Rework the FID map to map to real FIDs. */ /* -------------------------------------------------------------------- */ + int bAlreadySorted = TRUE; for( i = 0; i < nIndexSize; i++ ) + { + if (panFIDIndex[i] != i) + bAlreadySorted = FALSE; panFIDIndex[i] = panFIDList[panFIDIndex[i]]; + } CPLFree( panFIDList ); @@ -1022,6 +1742,21 @@ void OGRGenSQLResultsLayer::CreateOrderByIndex() } CPLFree( pasIndexFields ); + + /* If it is already sorted, then free than panFIDIndex array */ + /* so that GetNextFeature() can call a sequential GetNextFeature() */ + /* on the source array. Very usefull for layers where random access */ + /* is slow. */ + /* Use case: the GML result of a WFS GetFeature with a SORTBY */ + if (bAlreadySorted) + { + CPLFree( panFIDIndex ); + panFIDIndex = NULL; + + nIndexSize = 0; + } + + ResetReading(); } /************************************************************************/ @@ -1084,49 +1819,6 @@ void OGRGenSQLResultsLayer::SortIndexSection( OGRField *pasIndexFields, CPLFree( panMerged ); } - -/************************************************************************/ -/* OGRGenSQLCompareDate() */ -/************************************************************************/ - -static int OGRGenSQLCompareDate( OGRField *psFirstTuple, - OGRField *psSecondTuple ) -{ - /* FIXME? : We ignore TZFlag */ - - if (psFirstTuple->Date.Year < psSecondTuple->Date.Year) - return -1; - else if (psFirstTuple->Date.Year > psSecondTuple->Date.Year) - return 1; - - if (psFirstTuple->Date.Month < psSecondTuple->Date.Month) - return -1; - else if (psFirstTuple->Date.Month > psSecondTuple->Date.Month) - return 1; - - if (psFirstTuple->Date.Day < psSecondTuple->Date.Day) - return -1; - else if (psFirstTuple->Date.Day > psSecondTuple->Date.Day) - return 1; - - if (psFirstTuple->Date.Hour < psSecondTuple->Date.Hour) - return -1; - else if (psFirstTuple->Date.Hour > psSecondTuple->Date.Hour) - return 1; - - if (psFirstTuple->Date.Minute < psSecondTuple->Date.Minute) - return -1; - else if (psFirstTuple->Date.Minute > psSecondTuple->Date.Minute) - return 1; - - if (psFirstTuple->Date.Second < psSecondTuple->Date.Second) - return -1; - else if (psFirstTuple->Date.Second > psSecondTuple->Date.Second) - return 1; - - return 0; -} - /************************************************************************/ /* Compare() */ /************************************************************************/ @@ -1143,7 +1835,12 @@ int OGRGenSQLResultsLayer::Compare( OGRField *pasFirstTuple, swq_order_def *psKeyDef = psSelectInfo->order_defs + iKey; OGRFieldDefn *poFDefn; - if( psKeyDef->field_index >= iFIDFieldIndex ) + if( psKeyDef->field_index >= iFIDFieldIndex + SPECIAL_FIELD_COUNT ) + { + CPLAssert( FALSE ); + return 0; + } + else if( psKeyDef->field_index >= iFIDFieldIndex ) poFDefn = NULL; else poFDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( @@ -1202,8 +1899,8 @@ int OGRGenSQLResultsLayer::Compare( OGRField *pasFirstTuple, poFDefn->GetType() == OFTTime || poFDefn->GetType() == OFTDateTime) { - nResult = OGRGenSQLCompareDate(&pasFirstTuple[iKey], - &pasSecondTuple[iKey]); + nResult = OGRCompareDate(&pasFirstTuple[iKey], + &pasSecondTuple[iKey]); } if( psKeyDef->ascending_flag ) @@ -1212,3 +1909,142 @@ int OGRGenSQLResultsLayer::Compare( OGRField *pasFirstTuple, return nResult; } + + +/************************************************************************/ +/* AddFieldDefnToSet() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::AddFieldDefnToSet(int iTable, int iColumn, + CPLHashSet* hSet) +{ + if (iTable != -1 && iColumn != -1) + { + OGRLayer* poLayer = papoTableLayers[iTable]; + if (iColumn < poLayer->GetLayerDefn()->GetFieldCount()) + { + OGRFieldDefn* poFDefn = + poLayer->GetLayerDefn()->GetFieldDefn(iColumn); + CPLHashSetInsert(hSet, poFDefn); + } + } +} + +/************************************************************************/ +/* ExploreExprForIgnoredFields() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::ExploreExprForIgnoredFields(swq_expr_node* expr, + CPLHashSet* hSet) +{ + if (expr->eNodeType == SNT_COLUMN) + { + AddFieldDefnToSet(expr->table_index, expr->field_index, hSet); + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + ExploreExprForIgnoredFields(expr->papoSubExpr[i], hSet); + } +} + +/************************************************************************/ +/* FindAndSetIgnoredFields() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::FindAndSetIgnoredFields() +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + CPLHashSet* hSet = CPLHashSetNew(CPLHashSetHashPointer, + CPLHashSetEqualPointer, + NULL); + +/* -------------------------------------------------------------------- */ +/* 1st phase : explore the whole select infos to determine which */ +/* source fields are used */ +/* -------------------------------------------------------------------- */ + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + AddFieldDefnToSet(psColDef->table_index, psColDef->field_index, hSet); + if (psColDef->expr) + ExploreExprForIgnoredFields(psColDef->expr, hSet); + } + + if (psSelectInfo->where_expr) + ExploreExprForIgnoredFields(psSelectInfo->where_expr, hSet); + + for( int iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) + { + swq_join_def *psJoinDef = psSelectInfo->join_defs + iJoin; + AddFieldDefnToSet(0, psJoinDef->primary_field, hSet); + AddFieldDefnToSet(psJoinDef->secondary_table, psJoinDef->secondary_field, hSet); + } + + for( int iOrder = 0; iOrder < psSelectInfo->order_specs; iOrder++ ) + { + swq_order_def *psOrderDef = psSelectInfo->order_defs + iOrder; + AddFieldDefnToSet(psOrderDef->table_index, psOrderDef->field_index, hSet); + } + +/* -------------------------------------------------------------------- */ +/* 2nd phase : now, we can exclude the unused fields */ +/* -------------------------------------------------------------------- */ + for( int iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + OGRLayer* poLayer = papoTableLayers[iTable]; + OGRFeatureDefn *poSrcFDefn = poLayer->GetLayerDefn(); + int iSrcField; + char** papszIgnoredFields = NULL; + for(iSrcField=0;iSrcFieldGetFieldCount();iSrcField++) + { + OGRFieldDefn* poFDefn = poSrcFDefn->GetFieldDefn(iSrcField); + if (CPLHashSetLookup(hSet,poFDefn) == NULL) + { + papszIgnoredFields = CSLAddString(papszIgnoredFields, poFDefn->GetNameRef()); + //CPLDebug("OGR", "Adding %s to the list of ignored fields of layer %s", + // poFDefn->GetNameRef(), poLayer->GetName()); + } + } + poLayer->SetIgnoredFields((const char**)papszIgnoredFields); + CSLDestroy(papszIgnoredFields); + } + + CPLHashSetDestroy(hSet); +} + +/************************************************************************/ +/* InvalidateOrderByIndex() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::InvalidateOrderByIndex() +{ + CPLFree( panFIDIndex ); + panFIDIndex = NULL; + + nIndexSize = 0; + bOrderByValid = FALSE; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRGenSQLResultsLayer::SetAttributeFilter( const char* pszAttributeFilter ) +{ + InvalidateOrderByIndex(); + return OGRLayer::SetAttributeFilter(pszAttributeFilter); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + InvalidateOrderByIndex(); + if( iGeomField == 0 ) + OGRLayer::SetSpatialFilter(poGeom); + else + OGRLayer::SetSpatialFilter(iGeomField, poGeom); +} diff --git a/ogr/ogr_gensql.h b/ogr/ogr_gensql.h index 058e72c..d24daf5 100644 --- a/ogr/ogr_gensql.h +++ b/ogr/ogr_gensql.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_gensql.h 10645 2007-01-18 02:22:39Z warmerdam $ + * $Id: ogr_gensql.h 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Classes related to generic implementation of ExecuteSQL(). @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,6 +32,18 @@ #define _OGR_GENSQL_H_INCLUDED #include "ogrsf_frmts.h" +#include "swq.h" +#include "cpl_hash_set.h" + +#define GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poFDefn, iGeom) \ + ((poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT + (iGeom)) + +#define IS_GEOM_FIELD_INDEX(poFDefn, idx) \ + (((idx) >= (poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT) && \ + ((idx) < (poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT + (poFDefn)->GetGeomFieldCount())) + +#define ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX(poFDefn, idx) \ + ((idx) - ((poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT)) /************************************************************************/ /* OGRGenSQLResultsLayer */ @@ -39,28 +52,31 @@ class CPL_DLL OGRGenSQLResultsLayer : public OGRLayer { private: - OGRDataSource *poSrcDS; + GDALDataset *poSrcDS; OGRLayer *poSrcLayer; void *pSelectInfo; + char *pszWHERE; + OGRLayer **papoTableLayers; OGRFeatureDefn *poDefn; int PrepareSummary(); + + int *panGeomFieldToSrcGeomField; int nIndexSize; long *panFIDIndex; + int bOrderByValid; int nNextIndexFID; OGRFeature *poSummaryFeature; int iFIDFieldIndex; - OGRField *pasOrderByIndex; - int nExtraDSCount; - OGRDataSource **papoExtraDS; + GDALDataset **papoExtraDS; OGRFeature *TranslateFeature( OGRFeature * ); void CreateOrderByIndex(); @@ -69,11 +85,24 @@ class CPL_DLL OGRGenSQLResultsLayer : public OGRLayer int Compare( OGRField *pasFirst, OGRField *pasSecond ); void ClearFilters(); + void ApplyFiltersToSource(); + + void FindAndSetIgnoredFields(); + void ExploreExprForIgnoredFields(swq_expr_node* expr, CPLHashSet* hSet); + void AddFieldDefnToSet(int iTable, int iColumn, CPLHashSet* hSet); + + int ContainGeomSpecialField(swq_expr_node* expr); + + void InvalidateOrderByIndex(); + int MustEvaluateSpatialFilterOnGenSQL(); + public: - OGRGenSQLResultsLayer( OGRDataSource *poSrcDS, - void *pSelectInfo, - OGRGeometry *poSpatFilter ); + OGRGenSQLResultsLayer( GDALDataset *poSrcDS, + void *pSelectInfo, + OGRGeometry *poSpatFilter, + const char *pszWHERE, + const char *pszDialect ); virtual ~OGRGenSQLResultsLayer(); virtual OGRGeometry *GetSpatialFilter(); @@ -85,12 +114,15 @@ class CPL_DLL OGRGenSQLResultsLayer : public OGRLayer virtual OGRFeatureDefn *GetLayerDefn(); - virtual OGRSpatialReference *GetSpatialRef(); - virtual int GetFeatureCount( int bForce = TRUE ); - virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); virtual int TestCapability( const char * ); + + virtual void SetSpatialFilter( OGRGeometry * poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); }; #endif /* ndef _OGR_GENSQL_H_INCLUDED */ diff --git a/ogr/ogr_geometry.h b/ogr/ogr_geometry.h index cb4513b..9890575 100644 --- a/ogr/ogr_geometry.h +++ b/ogr/ogr_geometry.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_geometry.h 18155 2009-12-02 15:27:29Z warmerdam $ + * $Id: ogr_geometry.h 27088 2014-03-24 23:18:08Z bishop $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Classes for manipulating simple features that is not specific @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -55,19 +56,17 @@ class OGRRawPoint }; typedef struct GEOSGeom_t *GEOSGeom; +typedef struct GEOSContextHandle_HS *GEOSContextHandle_t; /************************************************************************/ /* OGRGeometry */ /************************************************************************/ +class OGRPoint; + /** * Abstract base class for all geometry classes. * - * Note that the family of spatial analysis methods (Equal(), Disjoint(), ..., - * ConvexHull(), Buffer(), ...) are not implemented at ths time. Some other - * required and optional geometry methods have also been omitted at this - * time. - * * Some spatial analysis methods require that OGR is built on the GEOS library * to work properly. The precise meaning of methods that describe spatial relationships * between geometries is described in the SFCOM, or other simple features interface @@ -84,6 +83,7 @@ class CPL_DLL OGRGeometry protected: int nCoordDimension; + int getIsoGeometryType() const; public: OGRGeometry(); @@ -99,11 +99,12 @@ class CPL_DLL OGRGeometry virtual void empty() = 0; virtual OGRGeometry *clone() const = 0; virtual void getEnvelope( OGREnvelope * psEnvelope ) const = 0; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const = 0; // IWks Interface virtual int WkbSize() const = 0; virtual OGRErr importFromWkb( unsigned char *, int=-1 )=0; - virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char * ) const = 0; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOgc ) const = 0; virtual OGRErr importFromWkt( char ** ppszInput ) = 0; virtual OGRErr exportToWkt( char ** ppszDstText ) const = 0; @@ -112,10 +113,14 @@ class CPL_DLL OGRGeometry virtual const char *getGeometryName() const = 0; virtual void dumpReadable( FILE *, const char * = NULL, char** papszOptions = NULL ) const; virtual void flattenTo2D() = 0; - virtual char * exportToGML() const; + virtual char * exportToGML( const char* const * papszOptions = NULL ) const; virtual char * exportToKML() const; virtual char * exportToJson() const; - virtual GEOSGeom exportToGEOS() const; + + static GEOSContextHandle_t createGEOSContext(); + static void freeGEOSContext(GEOSContextHandle_t hGEOSCtxt); + virtual GEOSGeom exportToGEOS(GEOSContextHandle_t hGEOSCtxt) const; + virtual void closeRings(); virtual void setCoordinateDimension( int nDimension ); @@ -139,21 +144,31 @@ class CPL_DLL OGRGeometry virtual OGRBoolean Overlaps( const OGRGeometry * ) const; // virtual OGRBoolean Relate( const OGRGeometry *, const char * ) const; - virtual OGRGeometry *getBoundary() const; + virtual OGRGeometry *Boundary() const; virtual double Distance( const OGRGeometry * ) const; virtual OGRGeometry *ConvexHull() const; virtual OGRGeometry *Buffer( double dfDist, int nQuadSegs = 30 ) const; virtual OGRGeometry *Intersection( const OGRGeometry *) const; virtual OGRGeometry *Union( const OGRGeometry * ) const; + virtual OGRGeometry *UnionCascaded() const; virtual OGRGeometry *Difference( const OGRGeometry * ) const; - virtual OGRGeometry *SymmetricDifference( const OGRGeometry * ) const; - - // backward compatibility methods. - OGRBoolean Intersect( OGRGeometry * ) const; - OGRBoolean Equal( OGRGeometry * ) const; - + virtual OGRGeometry *SymDifference( const OGRGeometry * ) const; + virtual OGRErr Centroid( OGRPoint * poPoint ) const; + virtual OGRGeometry *Simplify(double dTolerance) const; + OGRGeometry *SimplifyPreserveTopology(double dTolerance) const; + + virtual OGRGeometry *Polygonize() const; + + // backward compatibility to non-standard method names. + OGRBoolean Intersect( OGRGeometry * ) const CPL_WARN_DEPRECATED("Non standard method. Use Intersects() instead"); + OGRBoolean Equal( OGRGeometry * ) const CPL_WARN_DEPRECATED("Non standard method. Use Equals() instead"); + virtual OGRGeometry *SymmetricDifference( const OGRGeometry * ) const CPL_WARN_DEPRECATED("Non standard method. Use SymDifference() instead"); + virtual OGRGeometry *getBoundary() const CPL_WARN_DEPRECATED("Non standard method. Use Boundary() instead"); + // Special HACK for DB2 7.2 support static int bGenerate_DB2_V72_BYTE_ORDER; + + virtual void swapXY(); }; /************************************************************************/ @@ -181,7 +196,7 @@ class CPL_DLL OGRPoint : public OGRGeometry // IWks Interface virtual int WkbSize() const; virtual OGRErr importFromWkb( unsigned char *, int=-1 ); - virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char * ) const; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOgc ) const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ppszDstText ) const; @@ -190,6 +205,7 @@ class CPL_DLL OGRPoint : public OGRGeometry virtual OGRGeometry *clone() const; virtual void empty(); virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; virtual OGRBoolean IsEmpty() const; // IPoint @@ -211,6 +227,8 @@ class CPL_DLL OGRPoint : public OGRGeometry virtual OGRwkbGeometryType getGeometryType() const; virtual OGRErr transform( OGRCoordinateTransformation *poCT ); virtual void flattenTo2D(); + + virtual void swapXY(); }; /************************************************************************/ @@ -260,7 +278,7 @@ class CPL_DLL OGRLineString : public OGRCurve // IWks Interface virtual int WkbSize() const; virtual OGRErr importFromWkb( unsigned char *, int = -1 ); - virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char * ) const; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOgc ) const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ppszDstText ) const; @@ -269,6 +287,7 @@ class CPL_DLL OGRLineString : public OGRCurve virtual OGRGeometry *clone() const; virtual void empty(); virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; virtual OGRBoolean IsEmpty() const; // ICurve methods @@ -276,6 +295,8 @@ class CPL_DLL OGRLineString : public OGRCurve virtual void StartPoint(OGRPoint *) const; virtual void EndPoint(OGRPoint *) const; virtual void Value( double, OGRPoint * ) const; + virtual double Project(const OGRPoint *) const; + virtual OGRLineString* getSubLine(double, double, int) const; // ILineString methods int getNumPoints() const { return nPointCount; } @@ -289,9 +310,10 @@ class CPL_DLL OGRLineString : public OGRCurve // non standard. virtual void setCoordinateDimension( int nDimension ); - void setNumPoints( int ); + void setNumPoints( int nNewPointCount, int bZeroizeNewContent = TRUE ); void setPoint( int, OGRPoint * ); void setPoint( int, double, double ); + void setZ( int, double ); void setPoint( int, double, double, double ); void setPoints( int, OGRRawPoint *, double * = NULL ); void setPoints( int, double * padfX, double * padfY, @@ -301,9 +323,13 @@ class CPL_DLL OGRLineString : public OGRCurve void addPoint( double, double, double ); void getPoints( OGRRawPoint *, double * = NULL ) const; + void getPoints( void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ = NULL, int nZStride = 0 ) const; void addSubLineString( const OGRLineString *, int nStartVertex = 0, int nEndVertex = -1 ); + void reversePoints( void ); // non-standard from OGRGeometry virtual OGRwkbGeometryType getGeometryType() const; @@ -311,6 +337,8 @@ class CPL_DLL OGRLineString : public OGRCurve virtual OGRErr transform( OGRCoordinateTransformation *poCT ); virtual void flattenTo2D(); virtual void segmentize(double dfMaxLength); + + virtual void swapXY(); }; /************************************************************************/ @@ -367,7 +395,7 @@ class CPL_DLL OGRLinearRing : public OGRLineString // object cant be serialized on its own. virtual int WkbSize() const; virtual OGRErr importFromWkb( unsigned char *, int=-1 ); - virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char * ) const; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOgc ) const; }; /************************************************************************/ @@ -382,7 +410,6 @@ class CPL_DLL OGRSurface : public OGRGeometry { public: virtual double get_Area() const = 0; - virtual OGRErr Centroid( OGRPoint * poPoint ) const = 0; virtual OGRErr PointOnSurface( OGRPoint * poPoint ) const = 0; }; @@ -420,19 +447,19 @@ class CPL_DLL OGRPolygon : public OGRSurface // ISurface Interface virtual double get_Area() const; - virtual int Centroid( OGRPoint * poPoint ) const; virtual int PointOnSurface( OGRPoint * poPoint ) const; // IWks Interface virtual int WkbSize() const; virtual OGRErr importFromWkb( unsigned char *, int = -1 ); - virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char * ) const; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOgc ) const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ppszDstText ) const; // IGeometry virtual int getDimension() const; virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; // ISpatialRelation virtual OGRBoolean Equals( OGRGeometry * ) const; @@ -449,9 +476,14 @@ class CPL_DLL OGRPolygon : public OGRSurface OGRLinearRing *getInteriorRing( int ); const OGRLinearRing *getInteriorRing( int ) const; + OGRLinearRing *stealExteriorRing(); + OGRLinearRing *stealInteriorRing(int); + OGRBoolean IsPointOnSurface( const OGRPoint * ) const; virtual void closeRings(); + + virtual void swapXY(); }; /************************************************************************/ @@ -470,6 +502,9 @@ class CPL_DLL OGRGeometryCollection : public OGRGeometry int nGeomCount; OGRGeometry **papoGeoms; + OGRErr importFromWkbInternal( unsigned char * pabyData, int nSize, int nRecLevel ); + OGRErr importFromWktInternal( char **ppszInput, int nRecLevel ); + public: OGRGeometryCollection(); virtual ~OGRGeometryCollection(); @@ -487,15 +522,17 @@ class CPL_DLL OGRGeometryCollection : public OGRGeometry // IWks Interface virtual int WkbSize() const; virtual OGRErr importFromWkb( unsigned char *, int = -1 ); - virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char * ) const; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOgc ) const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ppszDstText ) const; + virtual double get_Length() const; virtual double get_Area() const; // IGeometry methods virtual int getDimension() const; virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; // IGeometryCollection int getNumGeometries() const; @@ -512,6 +549,8 @@ class CPL_DLL OGRGeometryCollection : public OGRGeometry virtual OGRErr removeGeometry( int iIndex, int bDelete = TRUE ); void closeRings(); + + virtual void swapXY(); }; /************************************************************************/ @@ -535,7 +574,10 @@ class CPL_DLL OGRMultiPolygon : public OGRGeometryCollection virtual OGRGeometry *clone() const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ) const; - + + // IGeometry methods + virtual int getDimension() const; + // Non standard virtual OGRErr addGeometryDirectly( OGRGeometry * ); @@ -553,7 +595,7 @@ class CPL_DLL OGRMultiPolygon : public OGRGeometryCollection class CPL_DLL OGRMultiPoint : public OGRGeometryCollection { private: - OGRErr importFromWkt_Bracketed( char ** ); + OGRErr importFromWkt_Bracketed( char **, int bHasM, int bHasZ ); public: OGRMultiPoint(); @@ -563,7 +605,10 @@ class CPL_DLL OGRMultiPoint : public OGRGeometryCollection virtual OGRGeometry *clone() const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ) const; - + + // IGeometry methods + virtual int getDimension() const; + // Non standard virtual OGRErr addGeometryDirectly( OGRGeometry * ); }; @@ -587,6 +632,9 @@ class CPL_DLL OGRMultiLineString : public OGRGeometryCollection virtual OGRGeometry *clone() const; virtual OGRErr importFromWkt( char ** ); virtual OGRErr exportToWkt( char ** ) const; + + // IGeometry methods + virtual int getDimension() const; // Non standard virtual OGRErr addGeometryDirectly( OGRGeometry * ); @@ -603,6 +651,12 @@ class CPL_DLL OGRMultiLineString : public OGRGeometryCollection class CPL_DLL OGRGeometryFactory { + static OGRErr createFromFgfInternal( unsigned char *pabyData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn, + int nBytes, + int *pnBytesConsumed, + int nRecLevel ); public: static OGRErr createFromWkb( unsigned char *, OGRSpatialReference *, OGRGeometry **, int = -1 ); @@ -611,12 +665,13 @@ class CPL_DLL OGRGeometryFactory static OGRErr createFromFgf( unsigned char *, OGRSpatialReference *, OGRGeometry **, int = -1, int * = NULL ); static OGRGeometry *createFromGML( const char * ); - static OGRGeometry *createFromGEOS( GEOSGeom ); + static OGRGeometry *createFromGEOS( GEOSContextHandle_t hGEOSCtxt, GEOSGeom ); static void destroyGeometry( OGRGeometry * ); static OGRGeometry *createGeometry( OGRwkbGeometryType ); static OGRGeometry * forceToPolygon( OGRGeometry * ); + static OGRGeometry * forceToLineString( OGRGeometry *, bool bOnlyInOrder = true ); static OGRGeometry * forceToMultiPolygon( OGRGeometry * ); static OGRGeometry * forceToMultiPoint( OGRGeometry * ); static OGRGeometry * forceToMultiLineString( OGRGeometry * ); @@ -625,9 +680,6 @@ class CPL_DLL OGRGeometryFactory int nPolygonCount, int *pbResultValidGeometry, const char **papszOptions = NULL); - - static void *getGEOSGeometryFactory(); - static int haveGEOS(); static OGRGeometry* transformWithOptions( const OGRGeometry* poSrcGeom, @@ -642,4 +694,15 @@ class CPL_DLL OGRGeometryFactory double dfMaxAngleStepSizeDegrees ); }; +OGRwkbGeometryType CPL_DLL OGRFromOGCGeomType( const char *pszGeomType ); +const char CPL_DLL * OGRToOGCGeomType( OGRwkbGeometryType eGeomType ); + +/* Prepared geometry API (needs GEOS >= 3.1.0) */ +typedef struct _OGRPreparedGeometry OGRPreparedGeometry; +int OGRHasPreparedGeometrySupport(); +OGRPreparedGeometry* OGRCreatePreparedGeometry( const OGRGeometry* poGeom ); +void OGRDestroyPreparedGeometry( OGRPreparedGeometry* poPreparedGeom ); +int OGRPreparedGeometryIntersects( const OGRPreparedGeometry* poPreparedGeom, + const OGRGeometry* poOtherGeom ); + #endif /* ndef _OGR_GEOMETRY_H_INCLUDED */ diff --git a/ogr/ogr_geos.h b/ogr/ogr_geos.h index edb546c..f9d24cf 100644 --- a/ogr/ogr_geos.h +++ b/ogr/ogr_geos.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_geos.h 17469 2009-07-28 18:28:27Z warmerdam $ + * $Id: ogr_geos.h 27483 2014-06-30 20:49:41Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Definitions related to support for use of GEOS in OGR. @@ -33,6 +33,10 @@ #define OGR_GEOS_H_INCLUDED #ifdef HAVE_GEOS +// To avoid accidental use of non reentrant GEOS API. +// (check only effective in GEOS >= 3.5) +# define GEOS_USE_ONLY_R_API + # include #else diff --git a/ogr/ogr_miattrind.cpp b/ogr/ogr_miattrind.cpp index c18f3ff..f59fcfc 100644 --- a/ogr/ogr_miattrind.cpp +++ b/ogr/ogr_miattrind.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_miattrind.cpp 14048 2008-03-20 18:47:21Z rouault $ + * $Id: ogr_miattrind.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements interface to MapInfo .ID files used as attribute @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -32,7 +33,7 @@ #include "mitab/mitab_priv.h" #include "cpl_minixml.h" -CPL_CVSID("$Id: ogr_miattrind.cpp 14048 2008-03-20 18:47:21Z rouault $"); +CPL_CVSID("$Id: ogr_miattrind.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRMIAttrIndex */ @@ -59,6 +60,7 @@ class OGRMIAttrIndex : public OGRAttrIndex GByte *BuildKey( OGRField *psKey ); long GetFirstMatch( OGRField *psKey ); long *GetAllMatches( OGRField *psKey ); + long *GetAllMatches( OGRField *psKey, long* panFIDList, int* nFIDCount, int* nLength ); OGRErr AddEntry( OGRField *psKey, long nFID ); OGRErr RemoveEntry( OGRField *psKey, long nFID ); @@ -85,7 +87,10 @@ class OGRMILayerAttrIndex : public OGRLayerAttrIndex char *pszMetadataFilename; char *pszMIINDFilename; - + + int bINDAsReadOnly; + int bUnlinkINDFile; + OGRMILayerAttrIndex(); virtual ~OGRMILayerAttrIndex(); @@ -103,6 +108,7 @@ class OGRMILayerAttrIndex : public OGRLayerAttrIndex /* custom to OGRMILayerAttrIndex */ OGRErr SaveConfigToXML(); OGRErr LoadConfigFromXML(); + OGRErr LoadConfigFromXML(const char* pszRawXML); void AddAttrInd( int iField, int iINDIndex ); OGRLayer *GetLayer() { return poLayer; } @@ -118,6 +124,10 @@ OGRMILayerAttrIndex::OGRMILayerAttrIndex() poINDFile = NULL; nIndexCount = 0; papoIndexList = NULL; + bUnlinkINDFile = FALSE; + bINDAsReadOnly = TRUE; + pszMIINDFilename = NULL; + pszMetadataFilename = NULL; } /************************************************************************/ @@ -134,6 +144,9 @@ OGRMILayerAttrIndex::~OGRMILayerAttrIndex() poINDFile = NULL; } + if (bUnlinkINDFile) + VSIUnlink( pszMIINDFilename ); + for( int i = 0; i < nIndexCount; i++ ) delete papoIndexList[i]; CPLFree( papoIndexList ); @@ -159,6 +172,10 @@ OGRErr OGRMILayerAttrIndex::Initialize( const char *pszIndexPathIn, poLayer = poLayerIn; pszIndexPath = CPLStrdup( pszIndexPathIn ); + + /* try to process the XML string directly */ + if (EQUALN(pszIndexPathIn, "", 21)) + return LoadConfigFromXML(pszIndexPathIn); pszMetadataFilename = CPLStrdup( CPLResetExtension( pszIndexPathIn, "idm" ) ); @@ -185,37 +202,13 @@ OGRErr OGRMILayerAttrIndex::Initialize( const char *pszIndexPathIn, /* LoadConfigFromXML() */ /************************************************************************/ -OGRErr OGRMILayerAttrIndex::LoadConfigFromXML() +OGRErr OGRMILayerAttrIndex::LoadConfigFromXML(const char* pszRawXML) { - FILE *fp; - int nXMLSize; - char *pszRawXML; - - CPLAssert( poINDFile == NULL ); - -/* -------------------------------------------------------------------- */ -/* Read the XML file. */ -/* -------------------------------------------------------------------- */ - fp = VSIFOpen( pszMetadataFilename, "rb" ); - if( fp == NULL ) - return OGRERR_NONE; - - VSIFSeek( fp, 0, SEEK_END ); - nXMLSize = VSIFTell( fp ); - VSIFSeek( fp, 0, SEEK_SET ); - - pszRawXML = (char *) CPLMalloc(nXMLSize+1); - pszRawXML[nXMLSize] = '\0'; - VSIFRead( pszRawXML, nXMLSize, 1, fp ); - - VSIFClose( fp ); - /* -------------------------------------------------------------------- */ /* Parse the XML. */ /* -------------------------------------------------------------------- */ CPLXMLNode *psRoot = CPLParseXMLString( pszRawXML ); - CPLFree( pszRawXML ); if( psRoot == NULL ) return OGRERR_FAILURE; @@ -224,12 +217,18 @@ OGRErr OGRMILayerAttrIndex::LoadConfigFromXML() /* Open the index file. */ /* -------------------------------------------------------------------- */ poINDFile = new TABINDFile(); + + if (pszMIINDFilename == NULL) + pszMIINDFilename = CPLStrdup(CPLGetXMLValue(psRoot,"MIIDFilename","")); + + if( pszMIINDFilename == NULL ) + return OGRERR_FAILURE; /* NOTE: Replaced r+ with r according to explanation in Ticket #1620. * This change has to be observed if it doesn't cause any * problems in future. (mloskot) */ - if( poINDFile->Open( pszMetadataFilename, "r" ) != 0 ) + if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 ) { CPLDestroyXMLNode( psRoot ); CPLError( CE_Failure, CPLE_OpenFailed, @@ -237,7 +236,6 @@ OGRErr OGRMILayerAttrIndex::LoadConfigFromXML() pszMIINDFilename ); return OGRERR_FAILURE; } - /* -------------------------------------------------------------------- */ /* Process each attrindex. */ /* -------------------------------------------------------------------- */ @@ -275,6 +273,37 @@ OGRErr OGRMILayerAttrIndex::LoadConfigFromXML() return OGRERR_NONE; } +OGRErr OGRMILayerAttrIndex::LoadConfigFromXML() +{ + FILE *fp; + int nXMLSize; + char *pszRawXML; + + CPLAssert( poINDFile == NULL ); + +/* -------------------------------------------------------------------- */ +/* Read the XML file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszMetadataFilename, "rb" ); + if( fp == NULL ) + return OGRERR_NONE; + + VSIFSeek( fp, 0, SEEK_END ); + nXMLSize = VSIFTell( fp ); + VSIFSeek( fp, 0, SEEK_SET ); + + pszRawXML = (char *) CPLMalloc(nXMLSize+1); + pszRawXML[nXMLSize] = '\0'; + VSIFRead( pszRawXML, nXMLSize, 1, fp ); + + VSIFClose( fp ); + + OGRErr eErr = LoadConfigFromXML(pszRawXML); + CPLFree(pszRawXML); + + return eErr; +} + /************************************************************************/ /* SaveConfigToXML() */ /************************************************************************/ @@ -392,6 +421,31 @@ OGRErr OGRMILayerAttrIndex::CreateIndex( int iField ) return OGRERR_FAILURE; } } + else if (bINDAsReadOnly) + { + poINDFile->Close(); + if( poINDFile->Open( pszMIINDFilename, "r+" ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s as write-only.", + pszMIINDFilename ); + + if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot re-open %s as read-only.", + pszMIINDFilename ); + delete poINDFile; + poINDFile = NULL; + } + + return OGRERR_FAILURE; + } + else + { + bINDAsReadOnly = FALSE; + } + } /* -------------------------------------------------------------------- */ /* Do we have this field indexed already? */ @@ -457,6 +511,8 @@ OGRErr OGRMILayerAttrIndex::CreateIndex( int iField ) AddAttrInd( iField, iINDIndex ); + bUnlinkINDFile = FALSE; + /* -------------------------------------------------------------------- */ /* Save the new configuration. */ /* -------------------------------------------------------------------- */ @@ -511,12 +567,13 @@ OGRErr OGRMILayerAttrIndex::DropIndex( int iField ) /* Save the new configuration, or if there is nothing left try */ /* to clean up the index files. */ /* -------------------------------------------------------------------- */ + if( nIndexCount > 0 ) return SaveConfigToXML(); else { + bUnlinkINDFile = TRUE; VSIUnlink( pszMetadataFilename ); - VSIUnlink( pszMIINDFilename ); return OGRERR_NONE; } @@ -718,33 +775,42 @@ long OGRMIAttrIndex::GetFirstMatch( OGRField *psKey ) /* GetAllMatches() */ /************************************************************************/ -long *OGRMIAttrIndex::GetAllMatches( OGRField *psKey ) - +long *OGRMIAttrIndex::GetAllMatches( OGRField *psKey, long* panFIDList, int* nFIDCount, int* nLength ) { GByte *pabyKey = BuildKey( psKey ); - long *panFIDList = NULL, nFID; - int nFIDCount=0, nFIDMax=2; + long nFID; - panFIDList = (long *) CPLMalloc(sizeof(long) * 2); + if (panFIDList == NULL) + { + panFIDList = (long *) CPLMalloc(sizeof(long) * 2); + *nFIDCount = 0; + *nLength = 2; + } nFID = poINDFile->FindFirst( iIndex, pabyKey ); while( nFID > 0 ) { - if( nFIDCount >= nFIDMax-1 ) + if( *nFIDCount >= *nLength-1 ) { - nFIDMax = nFIDMax * 2 + 10; - panFIDList = (long *) CPLRealloc(panFIDList, sizeof(long)*nFIDMax); + *nLength = (*nLength) * 2 + 10; + panFIDList = (long *) CPLRealloc(panFIDList, sizeof(long)* (*nLength)); } - panFIDList[nFIDCount++] = nFID - 1; + panFIDList[(*nFIDCount)++] = nFID - 1; nFID = poINDFile->FindNext( iIndex, pabyKey ); } - panFIDList[nFIDCount] = OGRNullFID; + panFIDList[*nFIDCount] = OGRNullFID; return panFIDList; } +long *OGRMIAttrIndex::GetAllMatches( OGRField *psKey ) +{ + int nFIDCount, nLength; + return GetAllMatches( psKey, NULL, &nFIDCount, &nLength ); +} + /************************************************************************/ /* Clear() */ /************************************************************************/ diff --git a/ogr/ogr_p.h b/ogr/ogr_p.h index 2de6252..d97af45 100644 --- a/ogr/ogr_p.h +++ b/ogr/ogr_p.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_p.h 17696 2009-09-26 15:56:39Z rouault $ + * $Id: ogr_p.h 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Some private helper functions and stuff for OGR implementation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -37,8 +38,13 @@ #include "cpl_string.h" #include "cpl_conv.h" +#include "cpl_minixml.h" #include "ogr_core.h" +#include "ogr_geometry.h" + +/* A default name for the default geometry column, instead of '' */ +#define OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME "_ogr_geometry_" #ifdef CPL_MSB # define OGR_SWAP(x) (x == wkbNDR) @@ -62,8 +68,11 @@ const char CPL_DLL * OGRWktReadPoints( const char * pszInput, int * pnReadPoints ); void CPL_DLL OGRMakeWktCoordinate( char *, double, double, double, int ); + #endif +void OGRFormatDouble( char *pszBuffer, int nBufferLen, double dfVal, char chDecimalSep, int nPrecision = 15 ); + /* -------------------------------------------------------------------- */ /* Date-time parsing and processing functions */ /* -------------------------------------------------------------------- */ @@ -83,6 +92,8 @@ char CPL_DLL * OGRGetXMLDateTime(int year, int month, int day, int hour, int minute, int second, int TZFlag); char CPL_DLL * OGRGetXML_UTF8_EscapedString(const char* pszString); +int OGRCompareDate( OGRField *psFirstTuple, + OGRField *psSecondTuple ); /* used by ogr_gensql.cpp and ogrfeaturequery.cpp */ /* General utility option processing. */ int CPL_DLL OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions ); @@ -90,10 +101,6 @@ int CPL_DLL OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOpti /************************************************************************/ /* Support for special attributes (feature query and selection) */ /************************************************************************/ -CPL_C_START -#include "swq.h" -CPL_C_END - #define SPF_FID 0 #define SPF_OGR_GEOMETRY 1 #define SPF_OGR_STYLE 2 @@ -102,7 +109,10 @@ CPL_C_END #define SPECIAL_FIELD_COUNT 5 extern const char* SpecialFieldNames[SPECIAL_FIELD_COUNT]; + +#ifdef _SWQ_H_INCLUDED_ extern const swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT]; +#endif /************************************************************************/ /* Some SRS related stuff, search in SRS data files. */ @@ -113,4 +123,29 @@ OGRErr CPL_DLL OSRGetEllipsoidInfo( int, char **, double *, double *); /* Fast atof function */ double OGRFastAtof(const char* pszStr); +OGRErr CPL_DLL OGRCheckPermutation(int* panPermutation, int nSize); + +/* GML related */ + +OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode, + int bGetSecondaryGeometryOption, + int nRecLevel = 0, + int bIgnoreGSG = FALSE, + int bOrientation = TRUE, + int bFaceHoleNegative = FALSE ); + +/************************************************************************/ +/* PostGIS EWKB encoding */ +/************************************************************************/ + +OGRGeometry CPL_DLL *OGRGeometryFromEWKB( GByte *pabyWKB, int nLength, int* pnSRID ); +OGRGeometry CPL_DLL *OGRGeometryFromHexEWKB( const char *pszBytea, int* pnSRID ); +char CPL_DLL * OGRGeometryToHexEWKB( OGRGeometry * poGeometry, int nSRSId ); + +/************************************************************************/ +/* WKB Type Handling encoding */ +/************************************************************************/ + +OGRErr OGRReadWKBGeometryType( unsigned char * pabyData, OGRwkbGeometryType *eGeometryType, OGRBoolean *b3D ); + #endif /* ndef OGR_P_H_INCLUDED */ diff --git a/ogr/ogr_spatialref.h b/ogr/ogr_spatialref.h index c004197..b0f2f8a 100644 --- a/ogr/ogr_spatialref.h +++ b/ogr/ogr_spatialref.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_spatialref.h 18490 2010-01-09 05:44:49Z warmerdam $ + * $Id: ogr_spatialref.h 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Classes for manipulating spatial reference systems in a @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -65,8 +66,8 @@ class CPL_DLL OGR_SRSNode int nChildren; - void ClearChildren(); int NeedsQuoting() const; + OGRErr importFromWkt( char **, int nRecLevel, int* pnNodes ); public: OGR_SRSNode(const char * = NULL); @@ -85,6 +86,7 @@ class CPL_DLL OGR_SRSNode void AddChild( OGR_SRSNode * ); int FindChild( const char * ) const; void DestroyChild( int ); + void ClearChildren(); void StripNodes( const char * ); const char *GetValue() const { return pszValue; } @@ -135,10 +137,18 @@ class CPL_DLL OGRSpatialReference int nRefCount; int bNormInfoSet; - OGRErr ValidateProjection(); - int IsAliasFor( const char *, const char * ); + static OGRErr Validate(OGR_SRSNode *poRoot); + static OGRErr ValidateAuthority(OGR_SRSNode *poRoot); + static OGRErr ValidateAxis(OGR_SRSNode *poRoot); + static OGRErr ValidateUnit(OGR_SRSNode *poRoot); + static OGRErr ValidateVertDatum(OGR_SRSNode *poRoot); + static OGRErr ValidateProjection( OGR_SRSNode* poRoot ); + static int IsAliasFor( const char *, const char * ); void GetNormInfo() const; + OGRErr importFromURNPart(const char* pszAuthority, + const char* pszCode, + const char* pszURN); public: OGRSpatialReference(const OGRSpatialReference&); OGRSpatialReference(const char * = NULL); @@ -157,6 +167,7 @@ class CPL_DLL OGRSpatialReference OGRSpatialReference *Clone() const; OGRSpatialReference *CloneGeogCS() const; + void dumpReadable(); OGRErr exportToWkt( char ** ) const; OGRErr exportToPrettyWkt( char **, int = FALSE) const; OGRErr exportToProj4( char ** ) const; @@ -175,15 +186,20 @@ class CPL_DLL OGRSpatialReference OGRErr importFromESRI( char ** ); OGRErr importFromPCI( const char *, const char * = NULL, double * = NULL ); +#define USGS_ANGLE_DECIMALDEGREES 0 +#define USGS_ANGLE_PACKEDDMS TRUE /* 1 */ +#define USGS_ANGLE_RADIANS 2 OGRErr importFromUSGS( long iProjSys, long iZone, - double *padfPrjParams, - long iDatum, int bAnglesInPackedDMSFormat = TRUE ); + double *padfPrjParams, long iDatum, + int nUSGSAngleFormat = USGS_ANGLE_PACKEDDMS ); OGRErr importFromPanorama( long, long, long, double* ); OGRErr importFromOzi( const char *, const char *, const char * ); + OGRErr importFromOzi( const char * const* papszLines ); OGRErr importFromWMSAUTO( const char *pszAutoDef ); OGRErr importFromXML( const char * ); OGRErr importFromDict( const char *pszDict, const char *pszCode ); OGRErr importFromURN( const char * ); + OGRErr importFromCRSURL( const char * ); OGRErr importFromERM( const char *pszProj, const char *pszDatum, const char *pszUnits ); OGRErr importFromUrl( const char * ); @@ -199,8 +215,9 @@ class CPL_DLL OGRSpatialReference OGRErr Fixup(); int EPSGTreatsAsLatLong(); + int EPSGTreatsAsNorthingEasting(); const char *GetAxis( const char *pszTargetKey, int iAxis, - OGRAxisOrientation *peOrientation ); + OGRAxisOrientation *peOrientation ) const; OGRErr SetAxes( const char *pszTargetKey, const char *pszXAxisName, OGRAxisOrientation eXAxisOrientation, @@ -218,11 +235,15 @@ class CPL_DLL OGRSpatialReference OGRErr SetNode( const char *, const char * ); OGRErr SetNode( const char *, double ); - + OGRErr SetLinearUnitsAndUpdateParameters( const char *pszName, double dfInMeters ); OGRErr SetLinearUnits( const char *pszName, double dfInMeters ); + OGRErr SetTargetLinearUnits( const char *pszTargetKey, + const char *pszName, double dfInMeters ); double GetLinearUnits( char ** = NULL ) const; + double GetTargetLinearUnits( const char *pszTargetKey, + char ** ppszRetName = NULL ) const; OGRErr SetAngularUnits( const char *pszName, double dfInRadians ); double GetAngularUnits( char ** = NULL ) const; @@ -231,14 +252,19 @@ class CPL_DLL OGRSpatialReference int IsGeographic() const; int IsProjected() const; + int IsGeocentric() const; int IsLocal() const; + int IsVertical() const; + int IsCompound() const; int IsSameGeogCS( const OGRSpatialReference * ) const; + int IsSameVertCS( const OGRSpatialReference * ) const; int IsSame( const OGRSpatialReference * ) const; void Clear(); OGRErr SetLocalCS( const char * ); OGRErr SetProjCS( const char * ); OGRErr SetProjection( const char * ); + OGRErr SetGeocCS( const char * pszGeocName ); OGRErr SetGeogCS( const char * pszGeogName, const char * pszDatumName, const char * pszEllipsoidName, @@ -249,6 +275,12 @@ class CPL_DLL OGRSpatialReference double dfConvertToRadians = 0.0 ); OGRErr SetWellKnownGeogCS( const char * ); OGRErr CopyGeogCSFrom( const OGRSpatialReference * poSrcSRS ); + OGRErr SetVertCS( const char *pszVertCSName, + const char *pszVertDatumName, + int nVertDatumClass = 2005 ); + OGRErr SetCompoundCS( const char *pszName, + const OGRSpatialReference *poHorizSRS, + const OGRSpatialReference *poVertSRS ); OGRErr SetFromUserInput( const char * ); @@ -342,6 +374,9 @@ class CPL_DLL OGRSpatialReference OGRErr SetGH( double dfCentralMeridian, double dfFalseEasting, double dfFalseNorthing ); + /** Interrupted Goode Homolosine */ + OGRErr SetIGH(); + /** Gall Stereograpic */ OGRErr SetGS( double dfCentralMeridian, double dfFalseEasting, double dfFalseNorthing ); @@ -355,6 +390,7 @@ class CPL_DLL OGRSpatialReference OGRErr SetGnomonic(double dfCenterLat, double dfCenterLong, double dfFalseEasting, double dfFalseNorthing ); + /** Hotine Oblique Mercator */ OGRErr SetHOM( double dfCenterLat, double dfCenterLong, double dfAzimuth, double dfRectToSkew, double dfScale, @@ -366,6 +402,17 @@ class CPL_DLL OGRSpatialReference double dfScale, double dfFalseEasting, double dfFalseNorthing ); + OGRErr SetOM( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Hotine Oblique Mercator Azimuth Center / Variant B */ + OGRErr SetHOMAC( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + /** International Map of the World Polyconic */ OGRErr SetIWMPolyconic( double dfLat1, double dfLat2, double dfCenterLong, @@ -494,6 +541,13 @@ class CPL_DLL OGRSpatialReference OGRErr SetStatePlane( int nZone, int bNAD83 = TRUE, const char *pszOverrideUnitName = NULL, double dfOverrideUnit = 0.0 ); + + OGRErr ImportFromESRIStatePlaneWKT( + int nCode, const char* pszDatumName, const char* pszUnitsName, + int nPCSCode, const char* pszCSName = 0 ); + OGRErr ImportFromESRIWisconsinWKT( + const char* pszPrjName, double dfCentralMeridian, double dfLatOfOrigin, + const char* pszUnitsName, const char* pszCSName = 0 ); }; /************************************************************************/ diff --git a/ogr/ogr_srs_api.h b/ogr/ogr_srs_api.h index 57bacde..8fe73fd 100644 --- a/ogr/ogr_srs_api.h +++ b/ogr/ogr_srs_api.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_srs_api.h 17970 2009-11-06 20:00:29Z warmerdam $ + * $Id: ogr_srs_api.h 27109 2014-03-28 20:26:34Z kyle $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: C API and constant declarations for OGR Spatial References. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -115,7 +116,10 @@ typedef enum { #define SRS_PT_GEOSTATIONARY_SATELLITE \ "Geostationary_Satellite" #define SRS_PT_GOODE_HOMOLOSINE "Goode_Homolosine" +#define SRS_PT_IGH "Interrupted_Goode_Homolosine" #define SRS_PT_GNOMONIC "Gnomonic" +#define SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER \ + "Hotine_Oblique_Mercator_Azimuth_Center" #define SRS_PT_HOTINE_OBLIQUE_MERCATOR \ "Hotine_Oblique_Mercator" #define SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN \ @@ -223,6 +227,47 @@ typedef enum { #define SRS_UL_CHAIN_CONV "20.116684023368047" #define SRS_UL_ROD "Rod" /* based on US Foot */ #define SRS_UL_ROD_CONV "5.02921005842012" +#define SRS_UL_LINK_Clarke "Link_Clarke" +#define SRS_UL_LINK_Clarke_CONV "0.2011661949" + +#define SRS_UL_KILOMETER "Kilometer" +#define SRS_UL_KILOMETER_CONV "1000." +#define SRS_UL_DECIMETER "Decimeter" +#define SRS_UL_DECIMETER_CONV "0.1" +#define SRS_UL_CENTIMETER "Centimeter" +#define SRS_UL_CENTIMETER_CONV "0.01" +#define SRS_UL_MILLIMETER "Millimeter" +#define SRS_UL_MILLIMETER_CONV "0.001" +#define SRS_UL_INTL_NAUT_MILE "Nautical_Mile_International" +#define SRS_UL_INTL_NAUT_MILE_CONV "1852.0" +#define SRS_UL_INTL_INCH "Inch_International" +#define SRS_UL_INTL_INCH_CONV "0.0254" +#define SRS_UL_INTL_FOOT "Foot_International" +#define SRS_UL_INTL_FOOT_CONV "0.3048" +#define SRS_UL_INTL_YARD "Yard_International" +#define SRS_UL_INTL_YARD_CONV "0.9144" +#define SRS_UL_INTL_STAT_MILE "Statute_Mile_International" +#define SRS_UL_INTL_STAT_MILE_CONV "1609.344" +#define SRS_UL_INTL_FATHOM "Fathom_International" +#define SRS_UL_INTL_FATHOM_CONV "1.8288" +#define SRS_UL_INTL_CHAIN "Chain_International" +#define SRS_UL_INTL_CHAIN_CONV "20.1168" +#define SRS_UL_INTL_LINK "Link_International" +#define SRS_UL_INTL_LINK_CONV "0.201168" +#define SRS_UL_US_INCH "Inch_US_Surveyor" +#define SRS_UL_US_INCH_CONV "0.025400050800101603" +#define SRS_UL_US_YARD "Yard_US_Surveyor" +#define SRS_UL_US_YARD_CONV "0.914401828803658" +#define SRS_UL_US_CHAIN "Chain_US_Surveyor" +#define SRS_UL_US_CHAIN_CONV "20.11684023368047" +#define SRS_UL_US_STAT_MILE "Statute_Mile_US_Surveyor" +#define SRS_UL_US_STAT_MILE_CONV "1609.347218694437" +#define SRS_UL_INDIAN_YARD "Yard_Indian" +#define SRS_UL_INDIAN_YARD_CONV "0.91439523" +#define SRS_UL_INDIAN_FOOT "Foot_Indian" +#define SRS_UL_INDIAN_FOOT_CONV "0.30479841" +#define SRS_UL_INDIAN_CHAIN "Chain_Indian" +#define SRS_UL_INDIAN_CHAIN_CONV "20.11669506" #define SRS_UA_DEGREE "degree" #define SRS_UA_DEGREE_CONV "0.0174532925199433" @@ -285,9 +330,11 @@ OGRErr CPL_DLL OSRImportFromDict( OGRSpatialReferenceH, const char *, const char * ); OGRErr CPL_DLL OSRImportFromPanorama( OGRSpatialReferenceH, long, long, long, double * ); -OGRErr OSRImportFromOzi( OGRSpatialReferenceH , const char *, const char *, - const char * ); +OGRErr CPL_DLL OSRImportFromOzi( OGRSpatialReferenceH , const char *, const char *, + const char * ); OGRErr CPL_DLL OSRImportFromMICoordSys( OGRSpatialReferenceH, const char *); +OGRErr CPL_DLL OSRImportFromERM( OGRSpatialReferenceH, + const char *, const char *, const char * ); OGRErr CPL_DLL OSRImportFromUrl( OGRSpatialReferenceH, const char * ); OGRErr CPL_DLL CPL_STDCALL OSRExportToWkt( OGRSpatialReferenceH, char ** ); @@ -301,6 +348,7 @@ OGRErr CPL_DLL OSRExportToXML( OGRSpatialReferenceH, char **, const char * ); OGRErr CPL_DLL OSRExportToPanorama( OGRSpatialReferenceH, long *, long *, long *, long *, double * ); OGRErr CPL_DLL OSRExportToMICoordSys( OGRSpatialReferenceH, char ** ); +OGRErr CPL_DLL OSRExportToERM( OGRSpatialReferenceH, char *, char *, char * ); OGRErr CPL_DLL OSRMorphToESRI( OGRSpatialReferenceH ); OGRErr CPL_DLL OSRMorphFromESRI( OGRSpatialReferenceH ); @@ -314,20 +362,27 @@ const char CPL_DLL * CPL_STDCALL OSRGetAttrValue( OGRSpatialReferenceH hSRS, OGRErr CPL_DLL OSRSetAngularUnits( OGRSpatialReferenceH, const char *, double ); double CPL_DLL OSRGetAngularUnits( OGRSpatialReferenceH, char ** ); OGRErr CPL_DLL OSRSetLinearUnits( OGRSpatialReferenceH, const char *, double ); +OGRErr CPL_DLL OSRSetTargetLinearUnits( OGRSpatialReferenceH, const char *, const char *, double ); OGRErr CPL_DLL OSRSetLinearUnitsAndUpdateParameters( OGRSpatialReferenceH, const char *, double ); double CPL_DLL OSRGetLinearUnits( OGRSpatialReferenceH, char ** ); +double CPL_DLL OSRGetTargetLinearUnits( OGRSpatialReferenceH, const char *, char ** ); double CPL_DLL OSRGetPrimeMeridian( OGRSpatialReferenceH, char ** ); int CPL_DLL OSRIsGeographic( OGRSpatialReferenceH ); int CPL_DLL OSRIsLocal( OGRSpatialReferenceH ); int CPL_DLL OSRIsProjected( OGRSpatialReferenceH ); +int CPL_DLL OSRIsCompound( OGRSpatialReferenceH ); +int CPL_DLL OSRIsGeocentric( OGRSpatialReferenceH ); +int CPL_DLL OSRIsVertical( OGRSpatialReferenceH ); int CPL_DLL OSRIsSameGeogCS( OGRSpatialReferenceH, OGRSpatialReferenceH ); +int CPL_DLL OSRIsSameVertCS( OGRSpatialReferenceH, OGRSpatialReferenceH ); int CPL_DLL OSRIsSame( OGRSpatialReferenceH, OGRSpatialReferenceH ); OGRErr CPL_DLL OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char *pszName ); OGRErr CPL_DLL OSRSetProjCS( OGRSpatialReferenceH hSRS, const char * pszName ); +OGRErr CPL_DLL OSRSetGeocCS( OGRSpatialReferenceH hSRS, const char * pszName ); OGRErr CPL_DLL OSRSetWellKnownGeogCS( OGRSpatialReferenceH hSRS, const char * pszName ); OGRErr CPL_DLL CPL_STDCALL OSRSetFromUserInput( OGRSpatialReferenceH hSRS, @@ -340,6 +395,10 @@ OGRErr CPL_DLL OSRSetTOWGS84( OGRSpatialReferenceH hSRS, OGRErr CPL_DLL OSRGetTOWGS84( OGRSpatialReferenceH hSRS, double *, int ); +OGRErr CPL_DLL OSRSetCompoundCS( OGRSpatialReferenceH hSRS, + const char *pszName, + OGRSpatialReferenceH hHorizSRS, + OGRSpatialReferenceH hVertSRS ); OGRErr CPL_DLL OSRSetGeogCS( OGRSpatialReferenceH hSRS, const char * pszGeogName, const char * pszDatumName, @@ -350,6 +409,11 @@ OGRErr CPL_DLL OSRSetGeogCS( OGRSpatialReferenceH hSRS, const char * pszUnits /* = NULL */, double dfConvertToRadians /* = 0.0 */ ); +OGRErr CPL_DLL OSRSetVertCS( OGRSpatialReferenceH hSRS, + const char * pszVertCSName, + const char * pszVertDatumName, + int nVertDatumType ); + double CPL_DLL OSRGetSemiMajor( OGRSpatialReferenceH, OGRErr * /* = NULL */ ); double CPL_DLL OSRGetSemiMinor( OGRSpatialReferenceH, OGRErr * /* = NULL */ ); double CPL_DLL OSRGetInvFlattening( OGRSpatialReferenceH, OGRErr * /*=NULL*/); @@ -385,6 +449,7 @@ OGRErr CPL_DLL OSRSetStatePlaneWithUnits( OGRSpatialReferenceH hSRS, OGRErr CPL_DLL OSRAutoIdentifyEPSG( OGRSpatialReferenceH hSRS ); int CPL_DLL OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS ); +int CPL_DLL OSREPSGTreatsAsNorthingEasting( OGRSpatialReferenceH hSRS ); const char CPL_DLL *OSRGetAxis( OGRSpatialReferenceH hSRS, const char *pszTargetKey, int iAxis, OGRAxisOrientation *peOrientation ); @@ -452,6 +517,9 @@ OGRErr CPL_DLL OSRSetGS( OGRSpatialReferenceH hSRS, double dfCentralMeridian, /** Goode Homolosine */ OGRErr CPL_DLL OSRSetGH( OGRSpatialReferenceH hSRS, double dfCentralMeridian, double dfFalseEasting, double dfFalseNorthing ); + +/** Interrupted Goode Homolosine */ +OGRErr CPL_DLL OSRSetIGH( OGRSpatialReferenceH hSRS ); /** GEOS - Geostationary Satellite View */ OGRErr CPL_DLL OSRSetGEOS( OGRSpatialReferenceH hSRS, @@ -469,6 +537,13 @@ OGRErr CPL_DLL OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat, double dfCenterLong, double dfFalseEasting, double dfFalseNorthing ); +/** Oblique Mercator (aka HOM (variant B) */ +OGRErr CPL_DLL OSRSetOM( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + /** Hotine Oblique Mercator using azimuth angle */ OGRErr CPL_DLL OSRSetHOM( OGRSpatialReferenceH hSRS, double dfCenterLat, double dfCenterLong, @@ -588,7 +663,12 @@ OGRErr CPL_DLL OSRSetSOC( OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin, double dfCentralMeridian, double dfFalseEasting, double dfFalseNorthing ); -/** Transverse Mercator */ +/** Transverse Mercator + * + * Special processing available for Transverse Mercator with GDAL >= 1.10 and PROJ >= 4.8 : + * see OGRSpatialReference::exportToProj4(). + */ + OGRErr CPL_DLL OSRSetTM( OGRSpatialReferenceH hSRS, double dfCenterLat, double dfCenterLong, double dfScale, @@ -645,11 +725,13 @@ OCTTransformEx( OGRCoordinateTransformationH hCT, /* this is really private to OGR. */ char *OCTProj4Normalize( const char *pszProj4Src ); +void OCTCleanupProjMutex( void ); + /* -------------------------------------------------------------------- */ /* Projection transform dictionary query. */ /* -------------------------------------------------------------------- */ -char CPL_DLL ** OPTGetProjectionMethods(); +char CPL_DLL ** OPTGetProjectionMethods( void ); char CPL_DLL ** OPTGetParameterList( const char * pszProjectionMethod, char ** ppszUserName ); int CPL_DLL OPTGetParameterInfo( const char * pszProjectionMethod, diff --git a/ogr/ogr_srs_esri.cpp b/ogr/ogr_srs_esri.cpp index 39ea8d6..261f11d 100644 --- a/ogr/ogr_srs_esri.cpp +++ b/ogr/ogr_srs_esri.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_srs_esri.cpp 18024 2009-11-14 18:22:05Z rouault $ + * $Id: ogr_srs_esri.cpp 27322 2014-05-14 10:25:52Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: OGRSpatialReference translation to/from ESRI .prj definitions. @@ -7,6 +7,8 @@ * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * Copyright (c) 2013, Kyle Shannon * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,10 +32,11 @@ #include "ogr_spatialref.h" #include "ogr_p.h" #include "cpl_csv.h" +#include "cpl_multiproc.h" #include "ogr_srs_esri_names.h" -CPL_CVSID("$Id: ogr_srs_esri.cpp 18024 2009-11-14 18:22:05Z rouault $"); +CPL_CVSID("$Id: ogr_srs_esri.cpp 27322 2014-05-14 10:25:52Z rouault $"); void SetNewName( OGRSpatialReference* pOgr, const char* keyName, const char* newName ); int RemapImgWGSProjcsName(OGRSpatialReference* pOgr, const char* pszProjCSName, @@ -55,6 +58,8 @@ int AddParamBasedOnPrjName( OGRSpatialReference* pOgr, const char* pszProjectionName, char **mappingTable); int RemapGeogCSName(OGRSpatialReference* pOgr, const char *pszGeogCSName); +static int FindCodeFromDict( const char* pszDictFile, const char* CSName, char* code ); + static const char *apszProjMapping[] = { "Albers", SRS_PT_ALBERS_CONIC_EQUAL_AREA, "Cassini", SRS_PT_CASSINI_SOLDNER, @@ -62,8 +67,6 @@ static const char *apszProjMapping[] = { "Plate_Carree", SRS_PT_EQUIRECTANGULAR, "Hotine_Oblique_Mercator_Azimuth_Natural_Origin", SRS_PT_HOTINE_OBLIQUE_MERCATOR, - "Hotine_Oblique_Mercator_Azimuth_Center", - SRS_PT_HOTINE_OBLIQUE_MERCATOR, "Lambert_Conformal_Conic", SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, "Lambert_Conformal_Conic", SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, "Van_der_Grinten_I", SRS_PT_VANDERGRINTEN, @@ -91,13 +94,31 @@ static const char *apszPolarStereographicMapping[] = { SRS_PP_STANDARD_PARALLEL_1, SRS_PP_LATITUDE_OF_ORIGIN, NULL, NULL }; +static const char *apszOrthographicMapping[] = { + "Longitude_Of_Center", SRS_PP_CENTRAL_MERIDIAN, + "Latitude_Of_Center", SRS_PP_LATITUDE_OF_ORIGIN, + NULL, NULL }; + +static const char *apszLambertConformalConicMapping[] = { + "Central_Parallel", SRS_PP_LATITUDE_OF_ORIGIN, + NULL, NULL }; + static char **papszDatumMapping = NULL; +static void* hDatumMappingMutex = NULL; static const char *apszDefaultDatumMapping[] = { "6267", "North_American_1927", SRS_DN_NAD27, "6269", "North_American_1983", SRS_DN_NAD83, NULL, NULL, NULL }; +static const char *apszSpheroidMapping[] = { + "WGS_84", "WGS_1984", + "WGS_72", "WGS_1972", + "GRS_1967_Modified", "GRS_1967_Truncated", + "Krassowsky_1940", "Krasovsky_1940", + "Everest_1830_1937_Adjustment", "Everest_Adjustment_1937", + NULL, NULL }; + static const char *apszUnitMapping[] = { "Meter", "meter", "Meter", "metre", @@ -259,24 +280,25 @@ static const int anUsgsEsriZones[] = 5400, 0 }; -void OGREPSGDatumNameMassage( char ** ppszDatum ); +/* -------------------------------------------------------------------- */ +/* Datum Mapping functions and definitions */ +/* -------------------------------------------------------------------- */ +/* TODO adapt existing code and test */ +#define DM_IDX_EPSG_CODE 0 +#define DM_IDX_ESRI_NAME 1 +#define DM_IDX_EPSG_NAME 2 +#define DM_ELT_SIZE 3 -/************************************************************************/ -/* RemapSpheroidName() */ -/* */ -/* Convert Spheroid name to ESRI style name */ -/************************************************************************/ +#define DM_GET_EPSG_CODE(map, i) map[(i)*DM_ELT_SIZE + DM_IDX_EPSG_CODE] +#define DM_GET_ESRI_NAME(map, i) map[(i)*DM_ELT_SIZE + DM_IDX_ESRI_NAME] +#define DM_GET_EPSG_NAME(map, i) map[(i)*DM_ELT_SIZE + DM_IDX_EPSG_NAME] -static const char* RemapSpheroidName(const char* pszName) -{ - if (strcmp(pszName, "WGS 84") == 0) - return "WGS 1984"; +char *DMGetEPSGCode(int i) { return DM_GET_EPSG_CODE(papszDatumMapping, i); } +char *DMGetESRIName(int i) { return DM_GET_ESRI_NAME(papszDatumMapping, i); } +char *DMGetEPSGName(int i) { return DM_GET_EPSG_NAME(papszDatumMapping, i); } - if (strcmp(pszName, "WGS 72") == 0) - return "WGS 1972"; - return pszName; -} +void OGREPSGDatumNameMassage( char ** ppszDatum ); /************************************************************************/ /* ESRIToUSGSZone() */ @@ -362,6 +384,12 @@ void CleanupESRIDatumMappingTable() CSLDestroy( papszDatumMapping ); papszDatumMapping = NULL; } + + if( hDatumMappingMutex != NULL ) + { + CPLDestroyMutex(hDatumMappingMutex); + hDatumMappingMutex = NULL; + } } CPL_C_END @@ -372,6 +400,7 @@ CPL_C_END static void InitDatumMappingTable() { + CPLMutexHolderD(&hDatumMappingMutex); if( papszDatumMapping != NULL ) return; @@ -406,6 +435,7 @@ static void InitDatumMappingTable() "Failed to find required field in gdal_datum.csv in InitDatumMappingTable(), using default table setup." ); papszDatumMapping = (char **)apszDefaultDatumMapping; + VSIFClose( fp ); return; } @@ -520,9 +550,15 @@ static double OSR_GDV( char **papszNV, const char * pszField, papszTokens = CSLTokenizeString(papszNV[iLine]); if( CSLCount(papszTokens) == 3 ) { + /* http://agdcftp1.wr.usgs.gov/pub/projects/lcc/akcan_lcc/akcan.tar.gz contains */ + /* weird values for the second. Ignore it and the result looks correct */ + double dfSecond = atof(papszTokens[2]); + if (dfSecond < 0.0 || dfSecond >= 60.0) + dfSecond = 0.0; + dfValue = ABS(atof(papszTokens[0])) + atof(papszTokens[1]) / 60.0 - + atof(papszTokens[2]) / 3600.0; + + dfSecond / 3600.0; if( atof(papszTokens[0]) < 0.0 ) dfValue *= -1; @@ -606,8 +642,8 @@ static CPLString OSR_GDS( char **papszNV, const char * pszField, * importFromESRI() by an automatical call to morphFromESRI(). * * Currently only GEOGRAPHIC, UTM, STATEPLANE, GREATBRITIAN_GRID, ALBERS, - * EQUIDISTANT_CONIC, and TRANSVERSE (mercator) projections are supported - * from old style files. + * EQUIDISTANT_CONIC, TRANSVERSE (mercator), POLAR, MERCATOR and POLYCONIC + * projections are supported from old style files. * * At this time there is no equivelent exportToESRI() method. Writing old * style .prj files is not supported by OGRSpatialReference. However the @@ -740,6 +776,14 @@ OGRErr OGRSpatialReference::importFromESRI( char **papszPrj ) OSR_GDV( papszPrj, "PARAM_6", 0.0 ) ); } + else if( EQUAL(osProj,"LAMBERT_AZIMUTHAL") ) + { + SetLAEA( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ) ); + } + else if( EQUAL(osProj,"EQUIDISTANT_CONIC") ) { int nStdPCount = (int) OSR_GDV( papszPrj, "PARAM_1", 0.0 ); @@ -782,6 +826,23 @@ OGRErr OGRSpatialReference::importFromESRI( char **papszPrj ) OSR_GDV( papszPrj, "PARAM_4", 0.0 ) ); } + else if( EQUAL(osProj,"MERCATOR") ) + { + SetMercator( OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_0", 0.0 ), + 1.0, + OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ) ); + } + + else if( EQUAL(osProj,"POLYCONIC") ) + { + SetPolyconic( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ) ); + } + else { CPLDebug( "OGR_ESRI", "Unsupported projection: %s", osProj.c_str() ); @@ -843,7 +904,8 @@ OGRErr OGRSpatialReference::importFromESRI( char **papszPrj ) CopyGeogCSFrom( &oGCS ); } else if( EQUAL(osSpheroid,"KRASOVSKY") - || EQUAL(osSpheroid,"KRASSOVSKY") ) + || EQUAL(osSpheroid,"KRASSOVSKY") + || EQUAL(osSpheroid,"KRASSOWSKY") ) { OGRSpatialReference oGCS; oGCS.importFromEPSG( 4024 ); @@ -984,12 +1046,12 @@ OGRErr OGRSpatialReference::morphToESRI() } /* -------------------------------------------------------------------- */ -/* OBLIQUE_STEREOGRAPHIC maps to ESRI Stereographic */ +/* OBLIQUE_STEREOGRAPHIC maps to ESRI Double_Stereographic */ /* -------------------------------------------------------------------- */ if( pszProjection != NULL && ( EQUAL(pszProjection,SRS_PT_OBLIQUE_STEREOGRAPHIC) )) { - SetNode( "PROJCS|PROJECTION", "Stereographic" ); + SetNode( "PROJCS|PROJECTION", "Double_Stereographic" ); } /* -------------------------------------------------------------------- */ @@ -1049,6 +1111,20 @@ OGRErr OGRSpatialReference::morphToESRI() poGeogCS->GetChild(0)->SetValue( "GCS_North_American_1983" ); pszUTMPrefix = "NAD_1983"; } + else if( nGCSCode == 4167 + || EQUAL(pszGeogCSName,"NZGD2000") + || EQUAL(pszGeogCSName,"NZGD 2000") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_NZGD_2000" ); + pszUTMPrefix = "NZGD_2000"; + } + else if( nGCSCode == 4272 + || EQUAL(pszGeogCSName,"NZGD49") + || EQUAL(pszGeogCSName,"NZGD 49") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_New_Zealand_1949" ); + pszUTMPrefix = "NZGD_1949"; + } /* -------------------------------------------------------------------- */ /* Force Unnamed to Unknown for most common locations. */ @@ -1078,7 +1154,7 @@ OGRErr OGRSpatialReference::morphToESRI() /* If the PROJCS name is unset, use the PROJECTION name in */ /* place of unknown, or unnamed. At the request of Peng Gao. */ /* -------------------------------------------------------------------- */ - if( (poProjCS = GetAttrNode( "PROJCS" )) ) + if( (poProjCS = GetAttrNode( "PROJCS" )) != NULL ) poProjCSNodeChild = poProjCS->GetChild(0); if( poProjCSNodeChild ) @@ -1229,7 +1305,7 @@ OGRErr OGRSpatialReference::morphToESRI() FindProjParm( SRS_PP_LATITUDE_OF_ORIGIN ) ); } } - + /* -------------------------------------------------------------------- */ /* Convert SPHEROID name to use underscores instead of spaces. */ /* -------------------------------------------------------------------- */ @@ -1241,12 +1317,17 @@ OGRErr OGRSpatialReference::morphToESRI() if( poSpheroidChild != NULL ) { - char *pszNewValue = CPLStrdup(RemapSpheroidName(poSpheroidChild->GetValue())); +// char *pszNewValue = CPLStrdup(RemapSpheroidName(poSpheroidChild->GetValue())); + char *pszNewValue = CPLStrdup(poSpheroidChild->GetValue()); MorphNameToESRI( &pszNewValue ); poSpheroidChild->SetValue( pszNewValue ); CPLFree( pszNewValue ); + + GetRoot()->applyRemapper( "SPHEROID", + (char **) apszSpheroidMapping+0, + (char **) apszSpheroidMapping+1, 2 ); } if( poSpheroid != NULL ) @@ -1392,6 +1473,28 @@ OGRErr OSRMorphToESRI( OGRSpatialReferenceH hSRS ) * datums to "standard" names, as defined by Adam Gawne-Cain's reference * translation of EPSG to WKT for the CT specification. * + * Starting with GDAL 1.9.0, missing parameters in TOWGS84, DATUM or GEOGCS + * nodes can be added to the WKT, comparing existing WKT parameters to GDAL's + * databases. Note that this optional procedure is very conservative and should + * not introduce false information into the WKT defintion (altough caution + * should be advised when activating it). Needs the Configuration Option + * GDAL_FIX_ESRI_WKT be set to one of the following values (TOWGS84 is + * recommended for proper datum shift calculations): + * + * GDAL_FIX_ESRI_WKT values + *
  • + * + * + * + *
      TOWGS84   + * Adds missing TOWGS84 parameters (necessary for datum transformations), + * based on named datum and spheroid values.
      DATUM   + * Adds EPSG AUTHORITY nodes and sets SPHEROID name to OGR spec.
      GEOGCS   + * Adds EPSG AUTHORITY nodes and sets GEOGCS, DATUM and SPHEROID + * names to OGR spec. Effectively replaces GEOGCS node with the result of + * importFromEPSG(n), using EPSG code n corresponding to the existing GEOGCS. + * Does not impact PROJCS values.
    + * * This does the same as the C function OSRMorphFromESRI(). * * @return OGRERR_NONE unless something goes badly wrong. @@ -1401,15 +1504,27 @@ OGRErr OGRSpatialReference::morphFromESRI() { OGRErr eErr = OGRERR_NONE; + OGR_SRSNode *poDatum; + char *pszDatumOrig = NULL; if( GetRoot() == NULL ) return OGRERR_NONE; + InitDatumMappingTable(); + +/* -------------------------------------------------------------------- */ +/* Save original datum name for later */ +/* -------------------------------------------------------------------- */ + poDatum = GetAttrNode( "DATUM" ); + if( poDatum != NULL ) + { + poDatum = poDatum->GetChild(0); + pszDatumOrig = CPLStrdup( poDatum->GetValue() ); + } + /* -------------------------------------------------------------------- */ /* Translate DATUM keywords that are oddly named. */ /* -------------------------------------------------------------------- */ - InitDatumMappingTable(); - GetRoot()->applyRemapper( "DATUM", (char **)papszDatumMapping+1, (char **)papszDatumMapping+2, 3 ); @@ -1417,8 +1532,6 @@ OGRErr OGRSpatialReference::morphFromESRI() /* -------------------------------------------------------------------- */ /* Try to remove any D_ in front of the datum name. */ /* -------------------------------------------------------------------- */ - OGR_SRSNode *poDatum; - poDatum = GetAttrNode( "DATUM" ); if( poDatum != NULL ) poDatum = poDatum->GetChild(0); @@ -1433,6 +1546,13 @@ OGRErr OGRSpatialReference::morphFromESRI() } } +/* -------------------------------------------------------------------- */ +/* Translate some SPHEROID keywords that are oddly named. */ +/* -------------------------------------------------------------------- */ + GetRoot()->applyRemapper( "SPHEROID", + (char **)apszSpheroidMapping+1, + (char **)apszSpheroidMapping+0, 2 ); + /* -------------------------------------------------------------------- */ /* Split Lambert_Conformal_Conic into 1SP or 2SP form. */ /* */ @@ -1495,6 +1615,11 @@ OGRErr OGRSpatialReference::morphFromESRI() (char **)apszMercatorMapping + 0, (char **)apszMercatorMapping + 1, 2 ); + if( pszProjection != NULL && EQUAL(pszProjection,"Orthographic") ) + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszOrthographicMapping + 0, + (char **)apszOrthographicMapping + 1, 2 ); + if( pszProjection != NULL && EQUALN(pszProjection,"Stereographic_",14) && EQUALN(pszProjection+strlen(pszProjection)-5,"_Pole",5) ) @@ -1514,6 +1639,16 @@ OGRErr OGRSpatialReference::morphFromESRI() pszProjection = GetAttrValue("PROJECTION"); } +/* -------------------------------------------------------------------- */ +/* Remap Double_Stereographic to Oblique_Stereographic. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && EQUAL(pszProjection,"Double_Stereographic") ) + { + SetNode( "PROJCS|PROJECTION", SRS_PT_OBLIQUE_STEREOGRAPHIC ); + pszProjection = GetAttrValue("PROJECTION"); + } + /* -------------------------------------------------------------------- */ /* Remap Equidistant_Cylindrical parameter. It is same as */ /* Stereographic */ @@ -1526,6 +1661,19 @@ OGRErr OGRSpatialReference::morphFromESRI() (char **)apszPolarStereographicMapping + 1, 2 ); #endif + /* + ** Handle the value of Central_Parallel -> latitude_of_center. + ** See ticket #3191. Other mappings probably need to be added. + */ + if( pszProjection != NULL && + ( EQUAL( pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ) || + EQUAL( pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP ) ) ) + { + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszLambertConformalConicMapping + 0, + (char **)apszLambertConformalConicMapping + 1, 2 ); + } + /* -------------------------------------------------------------------- */ /* Translate PROJECTION keywords that are misnamed. */ /* -------------------------------------------------------------------- */ @@ -1542,6 +1690,194 @@ OGRErr OGRSpatialReference::morphFromESRI() (char **)papszDatumMapping+1, (char **)papszDatumMapping+2, 3 ); +/* -------------------------------------------------------------------- */ +/* Special case for Peru96 related SRS that should use the */ +/* Peru96 DATUM, but in ESRI world, both Peru96 and SIRGAS-Chile */ +/* are translated as D_SIRGAS-Chile. */ +/* -------------------------------------------------------------------- */ + int bPeru96Datum = FALSE; + if( poDatum != NULL && EQUAL(poDatum->GetValue(), "SIRGAS_Chile") ) + { + const char* pszSRSName = GetAttrValue("PROJCS"); + if( pszSRSName == NULL ) + pszSRSName = GetAttrValue("GEOGCS"); + if( strstr(pszSRSName, "Peru96") ) + { + bPeru96Datum = TRUE; + poDatum->SetValue( "Peru96" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Fix TOWGS84, DATUM or GEOGCS */ +/* -------------------------------------------------------------------- */ + /* TODO test more ESRI WKT; also add PROJCS */ + + /* Check GDAL_FIX_ESRI_WKT config option (default=NO); if YES, set to DATUM */ + const char *pszFixWktConfig=CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "NO" ); + if ( EQUAL(pszFixWktConfig,"YES") ) + pszFixWktConfig = "DATUM"; + + if( !EQUAL(pszFixWktConfig, "NO") && poDatum != NULL ) + { + CPLDebug( "OGR_ESRI", + "morphFromESRI() looking for missing TOWGS84, datum=%s, config=%s", + pszDatumOrig, pszFixWktConfig ); + + /* Special case for WGS84 and other common GCS? */ + + for( int i = 0; DMGetESRIName(i) != NULL; i++ ) + { + /* we found the ESRI datum name in the map */ + if( EQUAL(DMGetESRIName(i),pszDatumOrig) ) + { + const char *pszFilename = NULL; + char **papszRecord = NULL; + + /* look for GEOGCS corresponding to this datum */ + pszFilename = CSVFilename("gcs.csv"); + papszRecord = CSVScanFileByName( pszFilename, "DATUM_CODE", + DMGetEPSGCode(i), CC_Integer ); + if ( papszRecord != NULL ) + { + /* skip the SIRGAS-Chile record for Peru96 related SRS */ + if( bPeru96Datum && EQUAL(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"DATUM_NAME")), "SIRGAS-Chile") ) + continue; + + /* make sure we got a valid EPSG code and it is not DEPRECATED */ + int nGeogCS = atoi( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"COORD_REF_SYS_CODE")) ); + // int bDeprecated = atoi( CSLGetField( papszRecord, + // CSVGetFileFieldId(pszFilename,"DEPRECATED")) ); + + CPLDebug( "OGR_ESRI", "morphFromESRI() got GEOGCS node #%d", nGeogCS ); + + // if ( nGeogCS >= 1 && bDeprecated == 0 ) + if ( nGeogCS >= 1 ) + { + OGRSpatialReference oSRSTemp; + if ( oSRSTemp.importFromEPSG( nGeogCS ) == OGRERR_NONE ) + { + /* make clone of GEOGCS and strip CT parms for testing */ + OGRSpatialReference *poSRSTemp2 = NULL; + int bIsSame = FALSE; + char *pszOtherValue = NULL; + double dfThisValue, dfOtherValue; + OGR_SRSNode *poNode = NULL; + + poSRSTemp2 = oSRSTemp.CloneGeogCS(); + poSRSTemp2->StripCTParms(); + bIsSame = this->IsSameGeogCS( poSRSTemp2 ); + exportToWkt ( &pszOtherValue ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() got SRS %s, matching: %d", + pszOtherValue, bIsSame ); + CPLFree( pszOtherValue ); + delete poSRSTemp2; + + /* clone GEOGCS from original if they match and if allowed */ + if ( EQUAL(pszFixWktConfig,"GEOGCS") + && bIsSame ) + { + this->CopyGeogCSFrom( &oSRSTemp ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() cloned GEOGCS from EPSG:%d", + nGeogCS ); + /* exit loop */ + break; + } + /* else try to copy only DATUM or TOWGS84 + we got here either because of config option or + GEOGCS are not strictly equal */ + else if ( EQUAL(pszFixWktConfig,"GEOGCS") || + EQUAL(pszFixWktConfig,"DATUM") || + EQUAL(pszFixWktConfig,"TOWGS84") ) + { + /* test for matching SPHEROID, because there can be 2 datums with same ESRI name + but different spheroids (e.g. EPSG:4618 and EPSG:4291) - see bug #4345 */ + /* instead of testing for matching SPHEROID name (which can be error-prone), test + for matching parameters (semi-major and inverse flattening ) - see bug #4673 */ + bIsSame = TRUE; + dfThisValue = this->GetSemiMajor(); + dfOtherValue = oSRSTemp.GetSemiMajor(); + if ( ABS( dfThisValue - dfOtherValue ) > 0.01 ) + bIsSame = FALSE; + CPLDebug( "OGR_ESRI", + "morphFromESRI() SemiMajor: this = %.15g other = %.15g", + dfThisValue, dfOtherValue ); + dfThisValue = this->GetInvFlattening(); + dfOtherValue = oSRSTemp.GetInvFlattening(); + if ( ABS( dfThisValue - dfOtherValue ) > 0.0001 ) + bIsSame = FALSE; + CPLDebug( "OGR_ESRI", + "morphFromESRI() InvFlattening: this = %g other = %g", + dfThisValue, dfOtherValue ); + + if ( bIsSame ) + { + /* test for matching PRIMEM, because there can be 2 datums with same ESRI name + but different prime meridian (e.g. EPSG:4218 and EPSG:4802) - see bug #4378 */ + /* instead of testing for matching PRIMEM name (which can be error-prone), test + for matching value - see bug #4673 */ + dfThisValue = this->GetPrimeMeridian(); + dfOtherValue = oSRSTemp.GetPrimeMeridian(); + CPLDebug( "OGR_ESRI", + "morphFromESRI() PRIMEM: this = %.15g other = %.15g", + dfThisValue, dfOtherValue ); + if ( ABS( dfThisValue - dfOtherValue ) > 0.0001 ) + bIsSame = FALSE; + } + + /* found a matching spheroid */ + if ( bIsSame ) + { + /* clone DATUM */ + if ( EQUAL(pszFixWktConfig,"GEOGCS") || + EQUAL(pszFixWktConfig,"DATUM") ) + { + OGR_SRSNode *poGeogCS = this->GetAttrNode( "GEOGCS" ); + const OGR_SRSNode *poDatumOther = oSRSTemp.GetAttrNode( "DATUM" ); + if ( poGeogCS && poDatumOther ) + { + /* make sure we preserve the position of the DATUM node */ + int nPos = poGeogCS->FindChild( "DATUM" ); + if ( nPos >= 0 ) + { + poGeogCS->DestroyChild( nPos ); + poGeogCS->InsertChild( poDatumOther->Clone(), nPos ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() cloned DATUM from EPSG:%d", + nGeogCS ); + } + } + } + /* just copy TOWGS84 */ + else if ( EQUAL(pszFixWktConfig,"TOWGS84") ) + { + poNode=oSRSTemp.GetAttrNode( "DATUM|TOWGS84" ); + if ( poNode ) + { + poNode=poNode->Clone(); + GetAttrNode( "DATUM" )->AddChild( poNode ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() found missing TOWGS84 from EPSG:%d", + nGeogCS ); + } + } + /* exit loop */ + break; + } + } + } + } + } + } + } + } + + CPLFree( pszDatumOrig ); + return eErr; } @@ -1660,12 +1996,11 @@ int RemapImgUTMNames( OGRSpatialReference* pOgr, const char* pszProjCSName, cons int RemapNameBasedOnKeyName( OGRSpatialReference* pOgr, const char* pszName, const char* pszkeyName, char **mappingTable ) { - long i, n; + long i; long iIndex = -1; for( i = 0; mappingTable[i] != NULL; i += 2 ) { - n = strlen(pszName); - if( EQUALN(pszName, mappingTable[i],n) ) + if( EQUAL(pszName, mappingTable[i]) ) { iIndex = i; break; @@ -1915,3 +2250,214 @@ int RemapGeogCSName( OGRSpatialReference* pOgr, const char *pszGeogCSName ) return ret; } +/************************************************************************/ +/* ImportFromESRIStatePlaneWKT() */ +/* */ +/* Search a ESRI State Plane WKT and import it. */ +/************************************************************************/ + +OGRErr OGRSpatialReference::ImportFromESRIStatePlaneWKT( int code, const char* datumName, const char* unitsName, int pcsCode, const char* csName ) +{ + int i; + long searchCode = -1; + + /* if the CS name is known */ + if (code == 0 && !datumName && !unitsName && pcsCode == 32767 && csName) + { + char codeS[10]; + if (FindCodeFromDict( "esri_StatePlane_extra.wkt", csName, codeS ) != OGRERR_NONE) + return OGRERR_FAILURE; + return importFromDict( "esri_StatePlane_extra.wkt", codeS); + } + + /* Find state plane prj str by pcs code only */ + if( code == 0 && !datumName && pcsCode != 32767 ) + { + + int unitCode = 1; + if( EQUAL(unitsName, "international_feet") ) + unitCode = 3; + else if( strstr(unitsName, "feet") || strstr(unitsName, "foot") ) + unitCode = 2; + for(i=0; statePlanePcsCodeToZoneCode[i] != 0; i+=2) + { + if( pcsCode == statePlanePcsCodeToZoneCode[i] ) + { + searchCode = statePlanePcsCodeToZoneCode[i+1]; + int unitIndex = searchCode % 10; + if( (unitCode == 1 && !(unitIndex == 0 || unitIndex == 1)) + || (unitCode == 2 && !(unitIndex == 2 || unitIndex == 3 || unitIndex == 4 )) + || (unitCode == 3 && !(unitIndex == 5 || unitIndex == 6 )) ) + { + searchCode -= unitIndex; + switch (unitIndex) + { + case 0: + case 3: + case 5: + if(unitCode == 2) + searchCode += 3; + else if(unitCode == 3) + searchCode += 5; + break; + case 1: + case 2: + case 6: + if(unitCode == 1) + searchCode += 1; + if(unitCode == 2) + searchCode += 2; + else if(unitCode == 3) + searchCode += 6; + break; + case 4: + if(unitCode == 2) + searchCode += 4; + break; + } + } + break; + } + } + } + else /* Find state plane prj str by all inputs. */ + { + /* Need to have a specail EPSG-ESRI zone code mapping first. */ + for(i=0; statePlaneZoneMapping[i] != 0; i+=3) + { + if( code == statePlaneZoneMapping[i] + && (statePlaneZoneMapping[i+1] == -1 || pcsCode == statePlaneZoneMapping[i+1])) + { + code = statePlaneZoneMapping[i+2]; + break; + } + } + searchCode = (long)code * 10; + if(EQUAL(datumName, "HARN")) + { + if( EQUAL(unitsName, "international_feet") ) + searchCode += 5; + else if( strstr(unitsName, "feet") || strstr(unitsName, "foot") ) + searchCode += 3; + } + else if(strstr(datumName, "NAD") && strstr(datumName, "83")) + { + if( EQUAL(unitsName, "meters") ) + searchCode += 1; + else if( EQUAL(unitsName, "international_feet") ) + searchCode += 6; + else if( strstr(unitsName, "feet") || strstr(unitsName, "foot") ) + searchCode += 2; + } + else if(strstr(datumName, "NAD") && strstr(datumName, "27") && !EQUAL(unitsName, "meters")) + { + searchCode += 4; + } + else + searchCode = -1; + } + if(searchCode > 0) + { + char codeS[10]; + sprintf(codeS, "%d", (int)searchCode); + return importFromDict( "esri_StatePlane_extra.wkt", codeS); + } + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ImportFromESRIWisconsinWKT() */ +/* */ +/* Search a ESRI State Plane WKT and import it. */ +/************************************************************************/ + +OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT( const char* prjName, double centralMeridian, double latOfOrigin, const char* unitsName, const char* csName ) +{ + /* if the CS name is known */ + if (!prjName && !unitsName && csName) + { + char codeS[10]; + if (FindCodeFromDict( "esri_Wisconsin_extra.wkt", csName, codeS ) != OGRERR_NONE) + return OGRERR_FAILURE; + return importFromDict( "esri_Wisconsin_extra.wkt", codeS); + } + double* tableWISCRS; + if(EQUALN(prjName, "Lambert_Conformal_Conic", 22)) + tableWISCRS = apszWISCRS_LCC_meter; + else if(EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR)) + tableWISCRS = apszWISCRS_TM_meter; + else + return OGRERR_FAILURE; + int k = -1; + for(int i=0; tableWISCRS[i] != 0; i+=3) + { + if( fabs(centralMeridian - tableWISCRS[i]) <= 0.0000000001 && fabs(latOfOrigin - tableWISCRS[i+1]) <= 0.0000000001) + { + k = (long)tableWISCRS[i+2]; + break; + } + } + if(k > 0) + { + if(!EQUAL(unitsName, "meters")) + k += 100; + char codeS[10]; + sprintf(codeS, "%d", k); + return importFromDict( "esri_Wisconsin_extra.wkt", codeS); + } + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* FindCodeFromDict() */ +/* */ +/* Find the code from a dict file. */ +/************************************************************************/ +static int FindCodeFromDict( const char* pszDictFile, const char* CSName, char* code ) +{ + const char *pszFilename; + FILE *fp; + OGRErr eErr = OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Find and open file. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLFindFile( "gdal", pszDictFile ); + if( pszFilename == NULL ) + return OGRERR_UNSUPPORTED_SRS; + + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Process lines. */ +/* -------------------------------------------------------------------- */ + const char *pszLine; + + while( (pszLine = CPLReadLine(fp)) != NULL ) + + { + if( pszLine[0] == '#' ) + /* do nothing */; + + else if( strstr(pszLine,CSName) ) + { + const char* pComma = strchr(pszLine, ','); + if( pComma ) + { + strncpy( code, pszLine, pComma - pszLine); + code[pComma - pszLine] = '\0'; + eErr = OGRERR_NONE; + } + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFClose( fp ); + + return eErr; +} diff --git a/ogr/ogr_srs_esri_names.h b/ogr/ogr_srs_esri_names.h index 7493004..8477c71 100644 --- a/ogr/ogr_srs_esri_names.h +++ b/ogr/ogr_srs_esri_names.h @@ -2,7 +2,6 @@ static const char *apszGcsNameMapping[] = { "North_American_Datum_1983", "GCS_North_American_1983", "North_American_Datum_1927", "GCS_North_American_1927", "NAD27_CONUS", "GCS_North_American_1927", -"NAD27[CONUS]", "GCS_North_American_1927", "Reseau_Geodesique_de_Nouvelle_Caledonie_1991-93", "GCS_RGNC_1991-93", "Reseau_Geodesique_de_la_Polynesie_Francaise", "GCS_RGPF", "Rauenberg_1983", "GCS_RD/83", @@ -20,60 +19,172 @@ static const char *apszGcsNameMapping[] = { "Azores_Oriental_Islands_1940", "GCS_Azores_Oriental_1940", "Lithuania_1994", "GCS_LKS_1994", "Libyan_Geodetic_Datum_2006", "GCS_LGD2006", -"Lisbon", "GCS_Lisbon_Lisbon", +//"Lisbon", "GCS_Lisbon_Lisbon", "Stockholm_1938", "GCS_RT38", "Latvia_1992", "GCS_LKS_1992", "Azores_Oriental_Islands_1995", "GCS_Azores_Oriental_1995", "Azores_Central_Islands_1948", "GCS_Azores_Central_1948", "Azores_Central_Islands_1995", "GCS_Azores_Central_1995", "ATF", "GCS_ATF_Paris", -"ITRF_2000", "GCS_MONREF_1997", +//"ITRF_2000", "GCS_MONREF_1997", "Faroe_Datum_1954", "GCS_FD_1954", "Vietnam_2000", "GCS_VN_2000", -"Belge_1950", "GCS_Belge_1950_Brussels", +//"Belge_1950", "GCS_Belge_1950_Brussels", "Qatar_1948", "GCS_Qatar_1948", "Qatar", "GCS_Qatar_1974", "Kuwait_Utility", "GCS_KUDAMS", +"ED_1950_16", "GCS_European_1950", +"SAD_1969_Mean", "GCS_South_American_1969", +"Sphere_of_Radius_6370997m", "GCS_Sphere_ARC_INFO", +"Australian_Geodetic_1966", "GCS_Australian_1966", +"Australian_Geodetic_1984", "GCS_Australian_1984", +"AGD84", "GCS_Australian_1984", +"AGD66", "GCS_Australian_1966", +"Rome_1940", "GCS_Monte_Mario", +"Tokyo_Japan", "GCS_Tokyo", +"Graciosa_Base_SW_1948_1", "GCS_Graciosa_Base_SW_1948", +"Datum_Lisboa_Bessel_1", "GCS_Datum_Lisboa_Bessel", +"Datum_Lisboa_Hayford_1", "GCS_Datum_Lisboa_Hayford", +"Observatorio_Metereo_1939_Grupo_Ocidental", "GCS_Observ_Meteorologico_1939", +"Porto_Santo_1936_1", "GCS_Porto_Santo_1936", +"Sao_Braz_1", "GCS_Sao_Braz", +"GDA94", "GCS_GDA_1994", +"HARN", "GCS_North_American_1983_HARN", +"NAD83_HARN", "GCS_North_American_1983_HARN", +"Voirol_1875", "GCS_Voirol_1875", +"Voirol_1960", "GCS_Voirol_Unifie_1960", +"Ain_el_Abd_1970_Bahrain", "GCS_Ain_el_Abd_1970", +"ED_1950_ED77", "GCS_European_1950_ED77", +"Naparima_1955_2", "GCS_Naparima_1955", +"Aratu_Brazil_Campos_Espirito_Santo_and_Santos_basins", "GCS_Aratu", +"Camacupa_Angola_1", "GCS_Camacupa", +"Cape_1", "GCS_Cape", +"Carthage_Tunisia", "GCS_Carthage", +"Deir_ez_Zor_2", "GCS_Deir_ez_Zor", +"Old_Egyptian_1907", "GCS_Egypt_1907", +"PSAD56", "GCS_Provisional_S_American_1956", +"Indian 1975", "GCS_Indian_1975", +"Indian_1960_1", "GCS_Indian_1960", +"Kalianpur_1937_1", "GCS_Kalianpur_1937", +"Kertau_1948", "GCS_Kertau", +"Kertau_1968", "GCS_Kertau", +"Luzon", "GCS_Luzon_1911", +"Malongo_1987_1", "GCS_Malongo_1987", +"Minna_Cameroon", "GCS_Minna", +"Mporaloko_1", "GCS_Mporaloko", +"Nahrwan_Oman", "GCS_Nahrwan_1967", +"Naparima_BWI", "GCS_Naparima_1972", +"Geodetic_Datum_1949", "GCS_New_Zealand_1949", +"Qatar_National", "GCS_Qatar_1974", +"SAD_1969_Mean", "GCS_South_American_1969", +"Tananarive_Observatory_1925", "GCS_Tananarive_1925", +"Tananarive", "GCS_Tananarive_1925", +"Ireland_1965", "GCS_TM65", +"DE_DHDN_whole_country_2001_to_ETRS89", "GCS_Deutsches_Hauptdreiecksnetz", +"Belge_1972_1", "GCS_Belge_1972", +"WGS_72", "GCS_WGS_1972", +"JGD2000", "GCS_JGD_2000", +"NZGD49", "GCS_New_Zealand_1949", +"CH1903_1", "GCS_CH1903", +"DE_42/83_to_ETRS89", "GCS_Pulkovo_1942", +"DE_42_83_to_ETRS89", "GCS_Pulkovo_1942", +"Amersfoort_1", "GCS_Amersfoort", +"CH1903+_L+T1997", "GCS_CH1903+", +"Ord_Survey_G_Britain_1936", "GCS_OSGB_1936", +"European_Datum_1950", "GCS_European_1950", +"Geocentric_Datum_of_Australia_1994", "GCS_GDA_1994", +"NAD83_High_Accuracy_Regional_Network", "GCS_North_American_1983_HARN", +"Bogota_1975", "GCS_Bogota", +"North_American_Datum_1927_CGQ77", "GCS_NAD_1927_CGQ77", +"North_American_Datum_1927_1976", "GCS_NAD_1927_Definition_1976", +"European_Datum_1950_1977", "GCS_European_1950_ED77", +"WGS_1972_Transit_Broadcast_Ephemeris", "GCS_WGS_1972_BE", +"Greek_Geodetic_Reference_System_1987", "GCS_GGRS_1987", +"Militar_Geographische_Institute", "GCS_MGI", +"ED50", "GCS_European_1950", +"ETRS89", "GCS_ETRS_1989", NULL, NULL}; static const char *apszGcsNameMappingBasedOnProjCS[] = { "EUREF_FIN_TM35FIN", "GCS_ETRS_1989", "GCS_EUREF_FIN", +"Nord_Maroc_Degree", "GCS_Merchich", "GCS_Merchich_Degree", +"Sahara_Degree", "GCS_Merchich", "GCS_Merchich_Degree", +"Sud_Maroc_Degree", "GCS_Merchich", "GCS_Merchich_Degree", +"Merchich_Degree_UTM_Zone_28N", "GCS_Merchich", "GCS_Merchich_Degree", +"Lambert_Conformal_Conic", "GCS_Merchich", "GCS_Merchich_Degree", +"UTM", "GCS_Merchich", "GCS_Merchich_Degree", +"UTM_Zone_28_Northern_Hemisphere", "GCS_Merchich", "GCS_Merchich_Degree", +"Portuguese_National_Grid", "GCS_Lisbon", "GCS_Lisbon_Lisbon", +"Belge_Lambert_1950", "GCS_Belge_1950", "GCS_Belge_1950_Brussels", +"MONREF_1997_UTM_Zone_46N", "GCS_ITRF_2000", "GCS_MONREF_1997", +"MONREF_1997_UTM_Zone_47N", "GCS_ITRF_2000", "GCS_MONREF_1997", NULL, NULL, NULL}; + + static const char *apszGcsNameMappingBasedOnUnit[] = { -"Merchich", "Degree", "GCS_Merchich_Degree", "Voirol_Unifie_1960", "Degree", "GCS_Voirol_Unifie_1960_Degree", +"Voirol_1960", "Degree", "GCS_Voirol_Unifie_1960_Degree", +"Voirol 1960", "Degree", "GCS_Voirol_Unifie_1960_Degree", +"Voirol_1875", "Degree", "GCS_Voirol_1875_Degree", +"Voirol 1875", "Degree", "GCS_Voirol_1875_Degree", "NTF", "Grad", "GCS_NTF_Paris", NULL, NULL, NULL}; static const char *apszGcsNameMappingBasedPrime[] = { -"S_JTSK", "Ferro", "GCS_S_JTSK_Ferro", -"MGI", "Ferro", "GCS_MGI_Ferro", +"Bern_1898", "Bern", "GCS_Bern_1898_Bern", "Madrid_1870", "Madrid", "GCS_Madrid_1870_Madrid", +"MGI", "Ferro", "GCS_MGI_Ferro", +"MGI", "Stockholm", "GCS_RT38_Stockholm", "Monte_Mario", "Rome", "GCS_Monte_Mario_Rome", "NGO_1948", "Oslo", "GCS_NGO_1948_Oslo", -"MGI", "Stockholm", "GCS_RT38_Stockholm", +"S_JTSK", "Ferro", "GCS_S_JTSK_Ferro", "Stockholm_1938", "Stockholm", "GCS_RT38_Stockholm", -"Bern_1898", "Bern", "GCS_Bern_1898_Bern", NULL, NULL, NULL}; static const char *apszInvFlatteningMapping[] = { "293.464999999", "293.465", +"293.465000003", "293.465", +"293.465073361", "293.465", "293.466020000", "293.46602", -"294.26067636900", "294.260676369", +"293.466021293", "293.46602", +"293.4663077168286", "293.466307656", +"293.4664236085404", "293.466307656", +"294.2606763690", "294.260676369", "294.9786981999", "294.9786982", "294.978698213", "294.9786982", "295.9999999999", "296.0", "297.0000000000", "297.0", +"297.0000000284", "297.0", +"297.0000535480", "297.0", +"298.2499972761", "298.25", +"298.2500000654", "298.25", +"298.2500112226", "298.25", "298.256999999", "298.257", "298.2600000000", "298.26", +"298.2571643544962", "298.257223563", +"298.25716435449", "298.257222101", +"298.257222096042", "298.257222101", "298.25722210100", "298.257222101", "298.25722356299", "298.257223563", +"298.25722356300", "298.257223563", +"298.25999858999", "298.26", "298.2684109950054", "298.268410995005", -"298.299999999", "298.3", +"298.2999", "298.3", +"298.3000", "298.3", +"299.1527033239203", "299.1528128", "299.15281280000", "299.1528128", +"299.15281283", "299.1528128", +"299.15281310607", "299.1528128", +"299.15281327254", "299.1528128", +"299.32496460000", "299.3249646", +"299.32496405862", "299.3249646", +"299.32497531503", "299.3249646", +"300.80158474106", "300.8017", +"300.80169943849", "300.8017", "300.80169999999", "300.8017", "300.80170000000", "300.8017", +"300.80170009712", "300.8017", NULL, NULL}; static const char *apszParamValueMapping[] = { @@ -85,6 +196,13 @@ static const char *apszParamValueMapping[] = { "Transverse_Mercator", "false_easting", "299999.4798609", "300000.0", "Transverse_Mercator", "false_northing", "399999.30648", "400000.0", "Transverse_Mercator", "false_northing", "499999.1331", "500000.0", +"Transverse_Mercator", "central_meridian","51.21666666666668", "51.21666666666667", +"Transverse_Mercator", "Scale_Factor", "0.999601272", "0.9996012717", +"Lambert_Conformal_Conic", "central_meridian", "-90.33333333333334", "-90.33333333333333", +"Lambert_Conformal_Conic", "central_meridian", "-76.83333333333334", "-76.83333333333333", +"Krovak", "longitude_of_center", "24.83333333333334", "24.83333333333333", +"Hotine_Oblique_Mercator_Azimuth_Center", "longitude_of_center", "7.439583333333334", "7.439583333333333", +"Hotine_Oblique_Mercator_Azimuth_Center", "latitude_of_center", "46.95240555555557", "46.95240555555556", NULL, NULL, NULL, NULL}; static const char *apszParamNameMapping[] = { @@ -104,6 +222,7 @@ static const char *apszDeleteParametersBasedOnProjection[] = { "Mercator", "scale_factor", "Miller_Cylindrical", "latitude_of_center", "Equidistant_Cylindrical", "pseudo_standard_parallel_1", +"Equidistant_Cylindrical", "latitude_of_origin", "Plate_Carree", "latitude_of_origin", "Plate_Carree", "pseudo_standard_parallel_1", "Plate_Carree", "standard_parallel_1", @@ -113,6 +232,482 @@ NULL, NULL}; static const char *apszAddParametersBasedOnProjection[] = { "Cassini", "scale_factor", "1.0", -"Lambert_Conformal_Conic", "scale_factor", "1.0", "Mercator", "standard_parallel_1", "0.0", NULL, NULL, NULL}; + +static int statePlaneZoneMapping[] = { +/* old zone code, prj code, new zone code */ + 3126, -1, 101, + 3151, -1, 102, + 3176, -1, 202, + 3201, -1, 203, + 3226, -1, 301, + 3251, -1, 302, + 3326, -1, 403, + 3351, -1, 404, + 3376, 26945, 405, + 3426, -1, 407, + 3451, -1, 501, + 3476, -1, 502, + 3526, -1, 600, + 3551, -1, 700, + 3576, -1, 903, + 3626, -1, 902, + 3651, -1, 1001, + 3676, -1, 1002, + 3726, -1, 1102, + 3751, -1, 1103, + 3776, -1, 1201, + 3801, -1, 1202, + 3826, -1, 1301, + 3851, -1, 1302, + 3876, -1, 1401, + 3926, -1, 1501, + 3951, -1, 1502, + 3976, -1, 1601, + 4026, -1, 1701, + 6426, -1, 1703, + 4076, -1, 1801, + 4101, -1, 1802, + 4126, -1, 1900, + 4151, -1, 2001, + 4176, -1, 2002, + 4226, -1, 2102, + 4251, -1, 2103, + 6351, -1, 2111, + 6376, -1, 2112, + 6401, -1, 2113, + 4276, -1, 2201, + 4326, -1, 2203, + 4351, -1, 2301, + 4376, -1, 2302, + 4400, 32045, 3400, + 4401, -1, 2401, + 4426, -1, 2402, + 4451, -1, 2403, + 4476, 32100, 2500, + 4476, -1, 2501, + 4701, 32111, 2900, + 4801, 2260, 3101, + 4801, 32115, 3101, + 4526, -1, 2503, + 4551, -1, 2601, + 4576, -1, 2602, + 4626, -1, 2702, + 4651, -1, 2703, + 4676, -1, 2800, + 4726, -1, 3001, + 4751, -1, 3002, + 4776, -1, 3003, + 4826, -1, 3102, + 4851, -1, 3103, + 4876, -1, 3104, + 4926, -1, 3301, + 4951, -1, 3302, + 4976, -1, 3401, + 5026, -1, 3501, + 5051, -1, 3502, + 5076, -1, 3601, + 5126, -1, 3701, + 5151, -1, 3702, + 5176, -1, 3800, + 5226, -1, 3902, + 5251, -1, 4001, + 5276, -1, 4002, + 5301, -1, 4100, + 5326, -1, 4201, + 5351, -1, 4202, + 5376, -1, 4203, + 5401, -1, 4204, + 5426, -1, 4205, + 5451, -1, 4301, + 5476, -1, 4302, + 5501, -1, 4303, + 5526, -1, 4400, + 5551, -1, 4501, + 5576, -1, 4502, + 5601, -1, 4601, + 5626, -1, 4602, + 5651, -1, 4701, + 5676, -1, 4702, + 5701, -1, 4801, + 5726, -1, 4802, + 5751, -1, 4803, + 5776, -1, 4901, + 5801, -1, 4902, + 5826, -1, 4903, + 5851, -1, 4904, + 6101, -1, 5001, + 6126, -1, 5002, + 6151, -1, 5003, + 6176, -1, 5004, + 6201, -1, 5005, + 6226, -1, 5006, + 6251, -1, 5007, + 6276, -1, 5008, + 6301, -1, 5009, + 6326, -1, 5010, + 5876, -1, 5101, + 5901, -1, 5102, + 5926, -1, 5103, + 5951, -1, 5104, + 5976, -1, 5105, + 6001, -1, 5201, + 6026, -1, 5200, + 6076, -1, 5200, + 6051, -1, 5202, + 0, 0, 0 + }; + +/* This is not a complete mapping. Need to add more. */ +static int statePlanePcsCodeToZoneCode[] = { +/* pcs code, state plane prj str index*/ +2222, 2016, +2223, 2026, +2224, 2036, +2225, 4012, +2226, 4022, +2227, 4032, +2228, 4042, +2229, 4052, +2230, 4062, +2231, 5012, +2232, 5022, +2233, 5032, +2234, 6002, +2235, 7002, +2236, 9012, +2237, 9022, +2238, 9032, +2239, 10012, +2240, 10022, +2241, 11012, +2242, 11022, +2243, 11032, +2251, 21116, +2252, 21126, +2253, 21136, +2256, 25006, +2265, 33016, +2266, 33026, +2965, 13012, +2966, 13022, +2246, 16012, +2247, 16022, +2248, 19002, +2249, 20012, +2250, 20022, +2254, 23012, +2255, 23022, +2257, 30012, +2258, 30022, +2259, 30032, +2260, 31012, +2261, 31022, +2262, 31032, +2263, 31042, +2264, 32002, +2267, 35012, +2268, 35022, +2269, 36016, +2270, 36026, +2271, 37012, +2272, 37022, +2273, 39006, +2274, 41002, +2275, 42012, +2276, 42022, +2277, 42032, +2278, 42042, +2279, 42052, +2280, 43016, +2281, 43026, +2282, 43036, +2283, 45012, +2284, 45022, +2285, 46012, +2286, 46022, +2287, 48012, +2288, 48022, +2289, 48032, +2867, 2015, +2868, 2025, +2869, 2035, +2896, 21115, +2897, 21125, +2898, 21135, +2901, 25005, +2909, 33015, +2910, 33025, +2913, 36015, +2914, 36025, +2921, 43015, +2922, 43025, +2923, 43035, +2870, 4013, +2871, 4023, +2872, 4033, +2873, 4043, +2874, 4053, +2875, 4063, +2876, 5013, +2877, 5023, +2878, 5033, +2879, 6003, +2880, 7003, +2881, 9013, +2882, 9023, +2883, 9033, +2884, 10013, +2885, 10023, +2886, 11013, +2887, 11023, +2888, 11033, +2967, 13013, +2968, 13023, +2891, 16013, +2892, 16023, +2893, 19003, +2894, 20013, +2895, 20023, +2899, 23013, +2900, 23023, +2902, 30013, +2903, 30023, +2904, 30033, +2905, 31013, +2906, 31023, +2907, 31033, +2908, 31043, +2911, 35013, +2912, 35023, +2915, 41003, +2916, 42013, +2917, 42023, +2918, 42033, +2919, 42043, +2920, 42053, +2924, 45013, +2925, 45023, +2926, 46013, +2927, 46023, +2928, 48013, +2929, 48023, +2930, 48033, +// following are state systems (not complete) +2964, 102965, +2991, 102991, +2992, 102992, +2993, 102993, +2994, 102994, +// following are NAD 1983 SPCS Zone +26929, 1011, +26930, 1021, +26931, 50011, +26932, 50021, +26933, 50031, +26934, 50041, +26935, 50051, +26936, 50061, +26937, 50071, +26938, 50081, +26939, 50091, +26940, 50101, +26948, 2011, +26949, 2021, +26950, 2031, +26951, 3011, +26952, 3021, +26941, 4011, +26942, 4021, +26943, 4031, +26944, 4041, +26945, 4051, +26946, 4061, +26953, 5011, +26954, 5021, +26955, 5031, +26956, 6001, +26957, 7001, +26958, 9011, +26959, 9021, +26960, 9031, +26966, 10011, +26967, 10021, +26961, 51011, +26962, 51021, +26963, 51031, +26964, 51041, +26965, 51051, +26968, 11011, +26969, 11021, +26970, 11031, +26971, 12011, +26972, 12021, +26973, 13011, +26974, 13021, +26975, 14011, +26976, 14021, +26977, 15011, +26978, 15021, +26979, 16011, +26980, 16021, +26981, 17011, +26982, 17021, +26983, 18011, +26984, 18021, +26985, 19001, +26986, 20011, +26987, 20021, +26988, 21111, +26989, 21121, +26990, 21131, +26991, 22011, +26992, 22021, +26993, 22031, +26994, 23011, +26995, 23021, +26996, 24011, +26997, 24021, +26998, 24031, +32100, 25001, +32104, 26001, +32107, 27011, +32108, 27021, +32109, 27031, +32110, 28001, +32111, 29001, +32112, 30011, +32113, 30021, +32114, 30031, +32115, 31011, +32116, 31021, +32117, 31031, +32118, 31041, +32119, 32001, +32120, 33011, +32121, 33021, +32122, 34011, +32123, 34021, +32124, 35011, +32125, 35021, +32126, 36011, +32127, 36021, +32128, 37011, +32129, 37021, +32130, 38001, +32133, 39001, +32134, 40011, +32135, 40021, +32136, 41001, +32137, 42011, +32138, 42021, +32139, 42031, +32140, 42041, +32141, 42051, +32142, 43011, +32143, 43021, +32144, 43031, +32145, 44001, +32146, 45011, +32147, 45021, +32148, 46011, +32149, 46021, +32150, 47011, +32151, 47021, +32152, 48011, +32153, 48021, +32154, 48031, +32155, 49011, +32156, 49021, +32157, 49031, +32158, 49041, +32161, 52000, +65161, 54001, +0, 0 +}; + +/* ==================================================================== */ +/* WISCRS Table */ +/* ==================================================================== */ +static double apszWISCRS_LCC_meter[] = { +// Central_Meridian, Latitude_Of_Origin, SR code + -91.1527777777, 46.6696483772, 103303.0, + -92.4577777777, 45.8987148658, 103306.0, + -91.2944444444, 44.9778568986, 103308.0, + -89.3944444444, 43.4625466458, 103310.0, + -90.9388888888, 43.2000556050, 103311.0, + -89.4222222222, 43.0695160375, 103312.0, + -91.2888888888, 45.8722811263, 103317.0, + -89.8388888888, 42.6375622769, 103322.0, + -89.2416666666, 43.8070001177, 103323.0, + -89.8388888888, 42.6375622769, 103332.0, + -89.0333333333, 45.1542371052, 103333.0, + -89.7700000000, 44.9009044236, 103336.0, + -89.2416666666, 43.8070001177, 103338.0, + -90.6416666666, 44.0000739286, 103341.0, + -89.5444444444, 45.7042237702, 103343.0, + -92.2277777777, 44.6361488719, 103346.0, + -92.2277777777, 44.6361488719, 103347.0, + -89.5000000000, 44.4168239752, 103349.0, + -90.4305555555, 43.3223129275, 103352.0, + -91.1166666666, 45.9000991313, 103356.0, + -90.4833333333, 45.1778220858, 103360.0, + -90.7833333333, 43.5750329397, 103362.0, + -89.4888888888, 46.0778440905, 103363.0, + -88.5416666667, 42.6694620969, 103364.0, + -91.7833333333, 45.9612198333, 103365.0, + -89.2416666666, 44.1139440458, 103369.0, + -90.0000000000, 44.3625954694, 103371.0, + 0.0, 0,0, 0,0 +}; + +static double apszWISCRS_TM_meter[] = { +// Central_Meridian, Latitude_Of_Origin, SR code + -90.0000000000, 43.3666666666, 103300.0, + -90.6222222222, 45.7061111111, 103301.0, + -91.8500000000, 45.1333333333, 103302.0, + -88.0000000000, 43.0000000000, 103304.0, + -91.7972222222, 43.4813888888, 103305.0, + -88.5000000000, 42.7194444444, 103307.0, + -90.7083333333, 43.6000000000, 103309.0, + -88.7750000000, 41.4722222222, 103313.0, + -87.2722222222, 44.4000000000, 103314.0, + -91.9166666666, 45.8833333333, 103315.0, + -91.8944444444, 44.4083333333, 103316.0, + -88.1416666666, 45.4388888888, 103318.0, + -88.5000000000, 42.7194444444, 103319.0, + -88.6333333333, 44.0055555556, 103320.0, + -90.8000000000, 41.4111111111, 103321.0, + -90.1611111111, 42.5388888888, 103324.0, + -90.2555555555, 45.4333333333, 103325.0, + -90.8442965194, 44.2533351277, 103326.0, + -88.7750000000, 41.4722222222, 103327.0, + -90.0000000000, 43.3666666666, 103328.0, + -87.8944444444, 42.2166666666, 103329.0, + -87.5500000000, 43.2666666666, 103330.0, + -91.3166666666, 43.4511111111, 103331.0, + -89.7333333333, 44.8444444444, 103334.0, + -87.5500000000, 43.2666666666, 103335.0, + -87.7111111111, 44.6916666666, 103337.0, + -88.4166666666, 44.7166666666, 103339.0, + -87.8944444444, 42.2166666666, 103340.0, + -87.9083333333, 44.3972222222, 103342.0, + -88.5000000000, 42.7194444444, 103344.0, + -87.8944444444, 42.2166666666, 103345.0, + -92.6333333333, 44.6611111111, 103348.0, + -90.4888888889, 44.5555555556, 103350.0, + -87.8944444444, 42.2166666666, 103351.0, + -89.0722222222, 41.9444444444, 103353.0, + -91.0666666666, 43.9194444444, 103354.0, + -89.9000000000, 42.8194444444, 103355.0, + -88.6055555556, 44.0361111111, 103357.0, + -87.5500000000, 43.2666666666, 103358.0, + -92.6333333333, 44.0361111111, 103359.0, + -91.3666666666, 43.1611111111, 103361.0, + -88.0638888888, 42.9180555555, 103366.0, + -88.2250000000, 42.5694444444, 103367.0, + -88.8166666666, 43.4202777777, 103368.0, + -88.5000000000, 42.7194444444, 103370.0, + 0.0, 0,0, 0,0 +}; diff --git a/ogr/ogr_srs_ozi.cpp b/ogr/ogr_srs_ozi.cpp new file mode 100644 index 0000000..f020147 --- /dev/null +++ b/ogr/ogr_srs_ozi.cpp @@ -0,0 +1,546 @@ +/****************************************************************************** + * $Id: ogr_srs_ozi.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference translation from OziExplorer + * georeferencing information. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2009, Andrey Kiselev + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_conv.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogr_srs_ozi.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* OSRImportFromOzi() */ +/************************************************************************/ + +OGRErr OSRImportFromOzi( OGRSpatialReferenceH hSRS, + const char *pszDatum, const char *pszProj, + const char *pszProjParms ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromOzi", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromOzi( pszDatum, pszProj, + pszProjParms ); +} + +/************************************************************************/ +/* importFromOzi() */ +/************************************************************************/ + +/** + * Note : This method is obsolete, but has been kept to avoid breaking the API. + * It can be removed in GDAL 2.0 + */ + +/** + * Import coordinate system from OziExplorer projection definition. + * + * This method will import projection definition in style, used by + * OziExplorer software. + * + * This function is the equivalent of the C function OSRImportFromOzi(). + * + * @param pszDatum Datum string. This is a fifth string in the + * OziExplorer .MAP file. + * + * @param pszProj Projection string. Search for line starting with + * "Map Projection" name in the OziExplorer .MAP file and supply it as a + * whole in this parameter. + * + * @param pszProjParms String containing projection parameters. Search for + * "Projection Setup" name in the OziExplorer .MAP file and supply it as a + * whole in this parameter. + * + * @return OGRERR_NONE on success or an error code in case of failure. + * + * @deprecated Use importFromOzi( const char * const* papszLines ) instead + */ + +OGRErr OGRSpatialReference::importFromOzi( const char *pszDatum, + const char *pszProj, + const char *pszProjParms ) + +{ + const char* papszLines[8]; + + // Fake + papszLines[0] = ""; + papszLines[1] = ""; + papszLines[2] = ""; + papszLines[3] = ""; + papszLines[4] = pszDatum; /* Must be in that position */ + papszLines[5] = pszProj; /* Must be after 5th line */ + papszLines[6] = pszProjParms; /* Must be after 5th line */ + papszLines[7] = NULL; + + return importFromOzi(papszLines); +} + +/************************************************************************/ +/* importFromOzi() */ +/************************************************************************/ + +/** + * Import coordinate system from OziExplorer projection definition. + * + * This method will import projection definition in style, used by + * OziExplorer software. + * + * @param papszLines Map file lines. This is an array of strings containing + * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer. + * + * @return OGRERR_NONE on success or an error code in case of failure. + * + * @since OGR 1.10 + */ + +OGRErr OGRSpatialReference::importFromOzi( const char * const* papszLines ) +{ + int iLine; + const char *pszDatum, *pszProj = NULL, *pszProjParms = NULL; + + Clear(); + + int nLines = CSLCount((char**)papszLines); + if( nLines < 5 ) + return OGRERR_NOT_ENOUGH_DATA; + + pszDatum = papszLines[4]; + + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "Map Projection", 14) ) + { + pszProj = papszLines[iLine]; + } + else if ( EQUALN(papszLines[iLine], "Projection Setup", 16) ) + { + pszProjParms = papszLines[iLine]; + } + } + + if ( ! ( pszDatum && pszProj && pszProjParms ) ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + char **papszProj = CSLTokenizeStringComplex( pszProj, ",", TRUE, TRUE ); + char **papszProjParms = CSLTokenizeStringComplex( pszProjParms, ",", + TRUE, TRUE ); + char **papszDatum = NULL; + + if (CSLCount(papszProj) < 2) + { + goto not_enough_data; + } + + if ( EQUALN(papszProj[1], "Latitude/Longitude", 18) ) + { + } + + else if ( EQUALN(papszProj[1], "Mercator", 8) ) + { + if (CSLCount(papszProjParms) < 6) goto not_enough_data; + double dfScale = CPLAtof(papszProjParms[3]); + if (papszProjParms[3][0] == 0) dfScale = 1; /* if unset, default to scale = 1 */ + SetMercator( CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + dfScale, + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Transverse Mercator", 19) ) + { + if (CSLCount(papszProjParms) < 6) goto not_enough_data; + SetTM( CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[3]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Lambert Conformal Conic", 23) ) + { + if (CSLCount(papszProjParms) < 8) goto not_enough_data; + SetLCC( CPLAtof(papszProjParms[6]), CPLAtof(papszProjParms[7]), + CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Sinusoidal", 10) ) + { + if (CSLCount(papszProjParms) < 6) goto not_enough_data; + SetSinusoidal( CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Albers Equal Area", 17) ) + { + if (CSLCount(papszProjParms) < 8) goto not_enough_data; + SetACEA( CPLAtof(papszProjParms[6]), CPLAtof(papszProjParms[7]), + CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "(UTM) Universal Transverse Mercator", 35) && nLines > 5 ) + { + /* Look for the UTM zone in the calibration point data */ + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "Point", 5) ) + { + char **papszTok = NULL; + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + if ( CSLCount(papszTok) < 17 + || EQUAL(papszTok[2], "") + || EQUAL(papszTok[13], "") + || EQUAL(papszTok[14], "") + || EQUAL(papszTok[15], "") + || EQUAL(papszTok[16], "") ) + { + CSLDestroy(papszTok); + continue; + } + SetUTM( CPLAtofM(papszTok[13]), EQUAL(papszTok[16], "N") ); + CSLDestroy(papszTok); + break; + } + } + if ( iLine == nLines ) /* Try to guess the UTM zone */ + { + float fMinLongitude = INT_MAX; + float fMaxLongitude = INT_MIN; + float fMinLatitude = INT_MAX; + float fMaxLatitude = INT_MIN; + int bFoundMMPLL = FALSE; + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MMPLL", 5) ) + { + char **papszTok = NULL; + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + if ( CSLCount(papszTok) < 4 ) + { + CSLDestroy(papszTok); + continue; + } + float fLongitude = CPLAtofM(papszTok[2]); + float fLatitude = CPLAtofM(papszTok[3]); + CSLDestroy(papszTok); + + bFoundMMPLL = TRUE; + + if ( fMinLongitude > fLongitude ) + fMinLongitude = fLongitude; + if ( fMaxLongitude < fLongitude ) + fMaxLongitude = fLongitude; + if ( fMinLatitude > fLatitude ) + fMinLatitude = fLatitude; + if ( fMaxLatitude < fLatitude ) + fMaxLatitude = fLatitude; + } + } + float fMedianLatitude = ( fMinLatitude + fMaxLatitude ) / 2; + float fMedianLongitude = ( fMinLongitude + fMaxLongitude ) / 2; + if ( bFoundMMPLL && fMaxLatitude <= 90 ) + { + int nUtmZone; + if ( fMedianLatitude >= 56 && fMedianLatitude <= 64 && + fMedianLongitude >= 3 && fMedianLongitude <= 12 ) + nUtmZone = 32; /* Norway exception */ + else if ( fMedianLatitude >= 72 && fMedianLatitude <= 84 && + fMedianLongitude >= 0 && fMedianLongitude <= 42 ) + nUtmZone = (int) ((fMedianLongitude + 3 ) / 12) * 2 + 31; /* Svalbard exception */ + else + nUtmZone = (int) ((fMedianLongitude + 180 ) / 6) + 1; + SetUTM( nUtmZone, fMedianLatitude >= 0 ); + } + else + CPLDebug( "OSR_Ozi", "UTM Zone not found"); + } + } + + else if ( EQUALN(papszProj[1], "(I) France Zone I", 17) ) + { + SetLCC1SP( 49.5, 2.337229167, 0.99987734, 600000, 1200000 ); + } + + else if ( EQUALN(papszProj[1], "(II) France Zone II", 19) ) + { + SetLCC1SP( 46.8, 2.337229167, 0.99987742, 600000, 2200000 ); + } + + else if ( EQUALN(papszProj[1], "(III) France Zone III", 21) ) + { + SetLCC1SP( 44.1, 2.337229167, 0.99987750, 600000, 3200000 ); + } + + else if ( EQUALN(papszProj[1], "(IV) France Zone IV", 19) ) + { + SetLCC1SP( 42.165, 2.337229167, 0.99994471, 234.358, 4185861.369 ); + } + +/* + * Note : The following projections have not been implemented yet + * + */ + +/* + else if ( EQUALN(papszProj[1], "(BNG) British National Grid", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(IG) Irish Grid", 15) ) + { + } + + else if ( EQUALN(papszProj[1], "(NZG) New Zealand Grid", 22) ) + { + } + + else if ( EQUALN(papszProj[1], "(NZTM2) New Zealand TM 2000", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(SG) Swedish Grid", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(SUI) Swiss Grid", 26) ) + { + } + + else if ( EQUALN(papszProj[1], "(A)Lambert Azimuthual Equal Area", 32) ) + { + } + + else if ( EQUALN(papszProj[1], "(EQC) Equidistant Conic", 23) ) + { + } + + else if ( EQUALN(papszProj[1], "Polyconic (American)", 20) ) + { + } + + else if ( EQUALN(papszProj[1], "Van Der Grinten", 15) ) + { + } + + else if ( EQUALN(papszProj[1], "Vertical Near-Sided Perspective", 31) ) + { + } + + else if ( EQUALN(papszProj[1], "(WIV) Wagner IV", 15) ) + { + } + + else if ( EQUALN(papszProj[1], "Bonne", 5) ) + { + } + + else if ( EQUALN(papszProj[1], "(MT0) Montana State Plane Zone 2500", 35) ) + { + } + + else if ( EQUALN(papszProj[1], "ITA1) Italy Grid Zone 1", 23) ) + { + } + + else if ( EQUALN(papszProj[1], "ITA2) Italy Grid Zone 2", 23) ) + { + } + + else if ( EQUALN(papszProj[1], "(VICMAP-TM) Victoria Aust.(pseudo AMG)", 38) ) + { + } + + else if ( EQUALN(papszProj[1], "VICGRID) Victoria Australia", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(VG94) VICGRID94 Victoria Australia", 35) ) + { + } + + else if ( EQUALN(papszProj[1], "Gnomonic", 8) ) + { + } +*/ + + else + { + CPLDebug( "OSR_Ozi", "Unsupported projection: \"%s\"", papszProj[1] ); + SetLocalCS( CPLString().Printf("\"Ozi\" projection \"%s\"", + papszProj[1]) ); + } + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum/spheroid. */ +/* -------------------------------------------------------------------- */ + papszDatum = CSLTokenizeString2( pszDatum, ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + if ( papszDatum == NULL) + goto not_enough_data; + + if ( !IsLocal() ) + { + +/* -------------------------------------------------------------------- */ +/* Verify that we can find the CSV file containing the datums */ +/* -------------------------------------------------------------------- */ + if( CSVScanFileByName( CSVFilename( "ozi_datum.csv" ), + "EPSG_DATUM_CODE", + "4326", CC_Integer ) == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open OZI support file %s.\n" + "Try setting the GDAL_DATA environment variable to point\n" + "to the directory containing OZI csv files.", + CSVFilename( "ozi_datum.csv" ) ); + goto other_error; + } + +/* -------------------------------------------------------------------- */ +/* Search for matching datum */ +/* -------------------------------------------------------------------- */ + const char *pszOziDatum = CSVFilename( "ozi_datum.csv" ); + CPLString osDName = CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "NAME" ); + if( strlen(osDName) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find datum %s in ozi_datum.csv.", + papszDatum[0] ); + goto other_error; + } + + int nDatumCode = atoi( CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "EPSG_DATUM_CODE" ) ); + + if ( nDatumCode > 0 ) // There is a matching EPSG code + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( nDatumCode ); + CopyGeogCSFrom( &oGCS ); + } + else // We use the parameters from the CSV files + { + CPLString osEllipseCode = CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "ELLIPSOID_CODE" ); + double dfDeltaX = CPLAtof(CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "DELTAX" ) ); + double dfDeltaY = CPLAtof(CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "DELTAY" ) ); + double dfDeltaZ = CPLAtof(CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "DELTAZ" ) ); + + + /* -------------------------------------------------------------------- */ + /* Verify that we can find the CSV file containing the ellipsoids */ + /* -------------------------------------------------------------------- */ + if( CSVScanFileByName( CSVFilename( "ozi_ellips.csv" ), + "ELLIPSOID_CODE", + "20", CC_Integer ) == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open OZI support file %s.\n" + "Try setting the GDAL_DATA environment variable to point\n" + "to the directory containing OZI csv files.", + CSVFilename( "ozi_ellips.csv" ) ); + goto other_error; + } + + /* -------------------------------------------------------------------- */ + /* Lookup the ellipse code. */ + /* -------------------------------------------------------------------- */ + const char *pszOziEllipse = CSVFilename( "ozi_ellips.csv" ); + + CPLString osEName = CSVGetField( pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode, + CC_ApproxString, "NAME" ); + if( strlen(osEName) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find ellipsoid %s in ozi_ellips.csv.", + osEllipseCode.c_str() ); + goto other_error; + } + + double dfA = CPLAtof(CSVGetField( pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode, + CC_ApproxString, "A" )); + double dfInvF = CPLAtof(CSVGetField( pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode, + CC_ApproxString, "INVF" )); + + /* -------------------------------------------------------------------- */ + /* Create geographic coordinate system. */ + /* -------------------------------------------------------------------- */ + + SetGeogCS( osDName, osDName, osEName, dfA, dfInvF ); + SetTOWGS84( dfDeltaX, dfDeltaY, dfDeltaZ ); + + } + } + +/* -------------------------------------------------------------------- */ +/* Grid units translation */ +/* -------------------------------------------------------------------- */ + if( IsLocal() || IsProjected() ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + + FixupOrdering(); + + CSLDestroy(papszProj); + CSLDestroy(papszProjParms); + CSLDestroy(papszDatum); + + return OGRERR_NONE; + +not_enough_data: + + CSLDestroy(papszProj); + CSLDestroy(papszProjParms); + CSLDestroy(papszDatum); + + return OGRERR_NOT_ENOUGH_DATA; + +other_error: + + CSLDestroy(papszProj); + CSLDestroy(papszProjParms); + CSLDestroy(papszDatum); + + return OGRERR_FAILURE; +} + diff --git a/ogr/ogr_srs_proj4.cpp b/ogr/ogr_srs_proj4.cpp index d9d7819..c741ebf 100644 --- a/ogr/ogr_srs_proj4.cpp +++ b/ogr/ogr_srs_proj4.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_srs_proj4.cpp 18034 2009-11-15 20:13:38Z rouault $ + * $Id: ogr_srs_proj4.cpp 27436 2014-06-06 19:13:49Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: OGRSpatialReference interface to PROJ.4. @@ -7,6 +7,8 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * Copyright (c) 2014, Kyle Shannon * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,7 +35,7 @@ extern int EPSGGetWGS84Transform( int nGeogCS, double *padfTransform ); -CPL_CVSID("$Id: ogr_srs_proj4.cpp 18034 2009-11-15 20:13:38Z rouault $"); +CPL_CVSID("$Id: ogr_srs_proj4.cpp 27436 2014-06-06 19:13:49Z rouault $"); /* -------------------------------------------------------------------- */ /* The following list comes from osrs/proj/src/pj_ellps.c */ @@ -105,6 +107,31 @@ static const OGRProj4Datum ogr_pj_datums[] = { { "OSGB36", "OSGB_1936", 4277, 6277} }; +typedef struct +{ + const char* pszProj4PMName; + const char* pszWKTPMName; + const char* pszFromGreenwich; + int nPMCode; +} OGRProj4PM; + +/* Derived from pj_datums.c */ +static const OGRProj4PM ogr_pj_pms [] = { + { "greenwich", "Greenwich", "0dE", 8901 }, + { "lisbon", "Lisbon", "9d07'54.862\"W", 8902 }, + { "paris", "Paris", "2d20'14.025\"E", 8903 }, + { "bogota", "Bogota", "74d04'51.3\"W", 8904 }, + { "madrid", "Madrid", "3d41'16.58\"W", 8905 }, + { "rome", "Rome", "12d27'8.4\"E", 8906 }, + { "bern", "Bern", "7d26'22.5\"E", 8907 }, + { "jakarta", "Jakarta", "106d48'27.79\"E", 8908 }, + { "ferro", "Ferro", "17d40'W", 8909 }, + { "brussels", "Brussels", "4d22'4.71\"E", 8910 }, + { "stockholm", "Stockholm", "18d3'29.8\"E", 8911 }, + { "athens", "Athens", "23d42'58.815\"E", 8912 }, + { "oslo", "Oslo", "10d43'22.5\"E", 8913 } +}; + static const char* OGRGetProj4Datum(const char* pszDatum, int nEPSGDatum) { @@ -120,6 +147,45 @@ static const char* OGRGetProj4Datum(const char* pszDatum, return NULL; } +static const OGRProj4PM* OGRGetProj4PMFromProj4Name(const char* pszProj4PMName) +{ + unsigned int i; + for(i=0;ipszFromGreenwich); + pszPM = psProj4PM->pszWKTPMName; + nPMCode = psProj4PM->nPMCode; } else { @@ -445,6 +509,11 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) { } + else if( EQUAL(pszProj,"geocent") ) + { + SetGeocCS( "Geocentric" ); + } + else if( EQUAL(pszProj,"bonne") ) { SetBonne( OSR_GDV( papszNV, "lat_1", 0.0 ), @@ -479,11 +548,35 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) else if( EQUAL(pszProj,"tmerc") ) { + const char *pszAxis = CSLFetchNameValue( papszNV, "axis" ); + + if( pszAxis == NULL || !EQUAL(pszAxis,"wsu") ) + SetTM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + else + SetTMSO( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + /* For etmerc, we translate it into standard TM for the WKT */ + /* point of view, but make sure that the original proj.4 */ + /* definition is preserved for accurate reprojection */ + else if( EQUAL(pszProj,"etmerc") && + CSLFetchNameValue( papszNV, "axis" ) == NULL ) + { + bAddProj4Extension = TRUE; + SetTM( OSR_GDV( papszNV, "lat_0", 0.0 ), - OSR_GDV( papszNV, "lon_0", 0.0 ), - OSR_GDV( papszNV, "k", 1.0 ), - OSR_GDV( papszNV, "x_0", 0.0 ), - OSR_GDV( papszNV, "y_0", 0.0 ) ); + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); } else if( EQUAL(pszProj,"utm") ) @@ -531,8 +624,7 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) OSR_GDV( papszNV, "y_0", 0.0 ) ); } - else if( EQUALN(pszProj,"stere",5) /* mostly sterea */ - && CSLFetchNameValue(papszNV,"k") != NULL ) + else if( EQUAL(pszProj,"sterea") ) { SetOS( OSR_GDV( papszNV, "lat_0", 0.0 ), OSR_GDV( papszNV, "lon_0", 0.0 ), @@ -545,7 +637,7 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) { SetStereographic( OSR_GDV( papszNV, "lat_0", 0.0 ), OSR_GDV( papszNV, "lon_0", 0.0 ), - 1.0, + OSR_GDV( papszNV, "k", 1.0 ), OSR_GDV( papszNV, "x_0", 0.0 ), OSR_GDV( papszNV, "y_0", 0.0 ) ); } @@ -693,6 +785,11 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) OSR_GDV( papszNV, "y_0", 0.0 ) ); } + else if( EQUAL(pszProj,"igh") ) + { + SetIGH(); + } + else if( EQUAL(pszProj,"geos") ) { SetGEOS( OSR_GDV( papszNV, "lon_0", 0.0 ), @@ -704,7 +801,8 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) else if( EQUAL(pszProj,"lcc") ) { if( OSR_GDV(papszNV, "lat_0", 0.0 ) - == OSR_GDV(papszNV, "lat_1", 0.0 ) ) + == OSR_GDV(papszNV, "lat_1", 0.0 ) && + CSLFetchNameValue( papszNV, "lat_2" ) == NULL ) { /* 1SP form */ SetLCC1SP( OSR_GDV( papszNV, "lat_0", 0.0 ), @@ -727,23 +825,44 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) else if( EQUAL(pszProj,"omerc") ) { - SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), - OSR_GDV( papszNV, "lonc", 0.0 ), - OSR_GDV( papszNV, "alpha", 0.0 ), - 0.0, /* ??? */ - OSR_GDV( papszNV, "k", 1.0 ), - OSR_GDV( papszNV, "x_0", 0.0 ), - OSR_GDV( papszNV, "y_0", 0.0 ) ); + if( CSLFetchNameValue(papszNV,"no_uoff") != NULL + || CSLFetchNameValue(papszNV,"no_off") != NULL ) + { + /* From PJ_omerc, when alpha is defined but not gamma */ + /* the default gama value is alpha */ + /* if (alp || gam) { + if (alp) { + gamma0 = asin(sin(alpha_c) / D); + if (!gam) + gamma = alpha_c; */ + SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lonc", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + OSR_GDV( papszNV, "gamma", OSR_GDV( papszNV, "alpha", 0.0 ) ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + else + { + SetHOMAC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lonc", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + OSR_GDV( papszNV, "gamma", OSR_GDV( papszNV, "alpha", 0.0 ) ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } } else if( EQUAL(pszProj,"somerc") ) { - SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), - OSR_GDV( papszNV, "lon_0", 0.0 ), - 90.0, 90.0, - OSR_GDV( papszNV, "k", 1.0 ), - OSR_GDV( papszNV, "x_0", 0.0 ), - OSR_GDV( papszNV, "y_0", 0.0 ) ); + SetHOMAC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + 90.0, 90.0, + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); } else if( EQUAL(pszProj,"krovak") ) @@ -826,6 +945,14 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) OSR_GDV( papszNV, "y_0", 0.0 ) ); } + else if( strstr(pszProj4,"wktext") != NULL ) + { + // Fake out a projected coordinate system for otherwise + // unrecognised projections for which we are already planning + // to embed the actual PROJ.4 string via extension node. + SetProjection( "custom_proj4" ); + } + else { CPLDebug( "OGR_PROJ4", "Unsupported projection: %s", pszProj ); @@ -932,6 +1059,14 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) { dfSemiMinor = OSR_GDV( papszNV, "b", -1.0 ); dfInvFlattening = OSR_GDV( papszNV, "rf", -1.0 ); + if ( dfSemiMinor == -1.0 && dfInvFlattening == -1.0 ) + { + double dfFlattening = OSR_GDV( papszNV, "f", -1.0 ); + if ( dfFlattening == 0.0 ) + dfSemiMinor = dfSemiMajor; + else if ( dfFlattening != -1.0 ) + dfInvFlattening = 1.0 / dfFlattening; + } } if( dfSemiMinor == -1.0 && dfInvFlattening == -1.0 ) @@ -986,10 +1121,19 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) CSLDestroy(papszToWGS84); } +/* -------------------------------------------------------------------- */ +/* Handle nadgrids via an extension node. */ +/* -------------------------------------------------------------------- */ + pszValue = CSLFetchNameValue(papszNV, "nadgrids"); + if( pszValue != NULL ) + { + SetExtension( "DATUM", "PROJ4_GRIDS", pszValue ); + } + /* -------------------------------------------------------------------- */ /* Linear units translation */ /* -------------------------------------------------------------------- */ - if( IsProjected() || IsLocal() ) + if( IsProjected() || IsLocal() || IsGeocentric() ) { pszValue = CSLFetchNameValue(papszNV, "to_meter"); @@ -1006,18 +1150,81 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) else SetLinearUnits( "unknown", CPLAtofM(pszValue) ); } + /* + ** All units reported by cs2cs -lu are supported, fall back to meter. + */ else if( (pszValue = CSLFetchNameValue(papszNV, "units")) != NULL ) { - if( EQUAL(pszValue,"meter" ) || EQUAL(pszValue,"m") ) + if( EQUAL(pszValue,"meter" ) || EQUAL(pszValue,"m") || EQUAL(pszValue,"metre") ) SetLinearUnits( SRS_UL_METER, 1.0 ); - else if( EQUAL(pszValue,"us-ft" ) ) - SetLinearUnits( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) ); - else if( EQUAL(pszValue,"ft" ) ) - SetLinearUnits( SRS_UL_FOOT, CPLAtof(SRS_UL_FOOT_CONV) ); - else if( EQUAL(pszValue,"yd" ) ) - SetLinearUnits( pszValue, 0.9144 ); - else if( EQUAL(pszValue,"us-yd" ) ) - SetLinearUnits( pszValue, 0.914401828803658 ); + /* + ** Leave as 'kilometre' instead of SRS_UL_KILOMETER due to + ** historical usage + */ + else if( EQUAL( pszValue,"km") ) + SetLinearUnits( "kilometre", + CPLAtof( SRS_UL_KILOMETER_CONV ) ); + else if( EQUAL( pszValue,"us-ft" ) ) + SetLinearUnits( SRS_UL_US_FOOT, + CPLAtof( SRS_UL_US_FOOT_CONV ) ); + /* + ** Leave as 'Foot (International)' or SRS_UL_FOOT instead of + ** SRS_UL_INTL_FOOT due to historical usage + */ + else if( EQUAL( pszValue,"ft" ) ) + SetLinearUnits( SRS_UL_FOOT, + CPLAtof( SRS_UL_FOOT_CONV) ); + else if( EQUAL( pszValue,"yd" ) ) + SetLinearUnits( SRS_UL_INTL_YARD, + CPLAtof( SRS_UL_INTL_YARD_CONV ) ); + else if( EQUAL( pszValue,"us-yd" ) ) + SetLinearUnits( SRS_UL_US_YARD, + CPLAtof( SRS_UL_US_YARD_CONV ) ); + else if( EQUAL( pszValue,"dm" ) ) + SetLinearUnits( SRS_UL_DECIMETER, + CPLAtof( SRS_UL_DECIMETER_CONV ) ); + else if( EQUAL( pszValue,"cm" ) ) + SetLinearUnits( SRS_UL_CENTIMETER, + CPLAtof( SRS_UL_CENTIMETER_CONV ) ); + else if( EQUAL( pszValue,"mm" ) ) + SetLinearUnits( SRS_UL_MILLIMETER, + CPLAtof( SRS_UL_MILLIMETER_CONV ) ); + else if( EQUAL( pszValue,"kmi" ) ) + SetLinearUnits( SRS_UL_INTL_NAUT_MILE, + CPLAtof( SRS_UL_INTL_NAUT_MILE_CONV ) ); + else if( EQUAL( pszValue,"in" ) ) + SetLinearUnits( SRS_UL_INTL_INCH, + CPLAtof( SRS_UL_INTL_INCH_CONV ) ); + else if( EQUAL( pszValue,"mi" ) ) + SetLinearUnits( SRS_UL_INTL_STAT_MILE, + CPLAtof( SRS_UL_INTL_STAT_MILE_CONV ) ); + else if( EQUAL( pszValue,"fath" ) ) + SetLinearUnits( SRS_UL_INTL_FATHOM, + CPLAtof( SRS_UL_INTL_FATHOM_CONV ) ); + else if( EQUAL( pszValue,"ch" ) ) + SetLinearUnits( SRS_UL_INTL_CHAIN, + CPLAtof( SRS_UL_INTL_CHAIN_CONV ) ); + else if( EQUAL( pszValue,"link" ) ) + SetLinearUnits( SRS_UL_INTL_LINK, + CPLAtof( SRS_UL_INTL_LINK_CONV ) ); + else if( EQUAL( pszValue,"us-in" ) ) + SetLinearUnits( SRS_UL_US_INCH, + CPLAtof( SRS_UL_US_INCH_CONV ) ); + else if( EQUAL( pszValue, "us-ch" ) ) + SetLinearUnits( SRS_UL_US_CHAIN, + CPLAtof( SRS_UL_US_CHAIN_CONV ) ); + else if( EQUAL( pszValue, "us-mi" ) ) + SetLinearUnits( SRS_UL_US_STAT_MILE, + CPLAtof( SRS_UL_US_STAT_MILE_CONV ) ); + else if( EQUAL( pszValue, "ind-yd" ) ) + SetLinearUnits( SRS_UL_INDIAN_YARD, + CPLAtof( SRS_UL_INDIAN_YARD_CONV ) ); + else if( EQUAL( pszValue, "ind-ft" ) ) + SetLinearUnits( SRS_UL_INDIAN_FOOT, + CPLAtof( SRS_UL_INDIAN_FOOT_CONV ) ); + else if( EQUAL( pszValue, "ind-ch" ) ) + SetLinearUnits( SRS_UL_INDIAN_CHAIN, + CPLAtof( SRS_UL_INDIAN_CHAIN_CONV ) ); else // This case is untranslatable. Should add all proj.4 unts SetLinearUnits( pszValue, 1.0 ); } @@ -1046,18 +1253,199 @@ OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) } } +/* -------------------------------------------------------------------- */ +/* Handle geoidgrids via an extension node and COMPD_CS. */ +/* -------------------------------------------------------------------- */ + pszValue = CSLFetchNameValue(papszNV, "geoidgrids"); + OGR_SRSNode *poVERT_CS = NULL; + if( pszValue != NULL ) + { + OGR_SRSNode *poHorizSRS = GetRoot()->Clone(); + + Clear(); + + CPLString osName = poHorizSRS->GetChild(0)->GetValue(); + osName += " + "; + osName += "Unnamed Vertical Datum"; + + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( poHorizSRS ); + + poVERT_CS = new OGR_SRSNode( "VERT_CS" ); + GetRoot()->AddChild( poVERT_CS ); + poVERT_CS->AddChild( new OGR_SRSNode( "Unnamed" ) ); + + CPLString osTarget = GetRoot()->GetValue(); + osTarget += "|VERT_CS|VERT_DATUM"; + + SetNode( osTarget, "Unnamed" ); + + poVERT_CS->GetChild(1)->AddChild( new OGR_SRSNode( "2005" ) ); + SetExtension( osTarget, "PROJ4_GRIDS", pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Handle vertical units. */ +/* -------------------------------------------------------------------- */ + if( poVERT_CS != NULL ) + { + const char *pszUnitName = NULL; + const char *pszUnitConv = NULL; + + pszValue = CSLFetchNameValue(papszNV, "vto_meter"); + + if( pszValue != NULL && CPLAtofM(pszValue) > 0.0 ) + { + double dfValue = CPLAtofM(pszValue); + + if( fabs(dfValue - CPLAtof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + { + pszUnitName = SRS_UL_US_FOOT; + pszUnitConv = SRS_UL_US_FOOT_CONV; + } + else if( fabs(dfValue - CPLAtof(SRS_UL_FOOT_CONV)) < 0.00000001 ) + { + pszUnitName = SRS_UL_FOOT; + pszUnitConv = SRS_UL_FOOT_CONV; + } + else if( dfValue == 1.0 ) + { + pszUnitName = SRS_UL_METER; + pszUnitConv = "1.0"; + } + else + { + pszUnitName = "unknown"; + pszUnitConv = pszValue; + } + } + else if( (pszValue = CSLFetchNameValue(papszNV, "vunits")) != NULL ) + { + if( EQUAL(pszValue,"meter" ) || EQUAL(pszValue,"m") || EQUAL(pszValue,"metre") ) + { + pszUnitName = SRS_UL_METER; + pszUnitConv = "1.0"; + } + else if( EQUAL(pszValue,"us-ft" ) ) + { + pszUnitName = SRS_UL_US_FOOT; + pszUnitConv = SRS_UL_US_FOOT_CONV; + } + else if( EQUAL(pszValue,"ft" ) ) + { + pszUnitName = SRS_UL_FOOT; + pszUnitConv = SRS_UL_FOOT_CONV; + } + else if( EQUAL(pszValue,"yd" ) ) + { + pszUnitName = "Yard"; + pszUnitConv = "0.9144"; + } + else if( EQUAL(pszValue,"us-yd" ) ) + { + pszUnitName = "US Yard"; + pszUnitConv = "0.914401828803658"; + } + } + + if( pszUnitName != NULL ) + { + OGR_SRSNode *poUnits; + + poUnits = new OGR_SRSNode( "UNIT" ); + poUnits->AddChild( new OGR_SRSNode( pszUnitName ) ); + poUnits->AddChild( new OGR_SRSNode( pszUnitConv ) ); + + poVERT_CS->AddChild( poUnits ); + } + } + + /* Add AXIS to VERT_CS node */ + if( poVERT_CS != NULL ) + { + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Up" ) ); + poAxis->AddChild( new OGR_SRSNode( "UP" ) ); + + poVERT_CS->AddChild( poAxis ); + } /* -------------------------------------------------------------------- */ /* do we want to insert a PROJ.4 EXTENSION item? */ /* -------------------------------------------------------------------- */ - if( strstr(pszProj4,"wktext") != NULL ) + if( strstr(pszProj4,"wktext") != NULL || bAddProj4Extension ) SetExtension( GetRoot()->GetValue(), "PROJ4", pszProj4 ); - + +/* -------------------------------------------------------------------- */ +/* Preserve authority (for example IGNF) */ +/* -------------------------------------------------------------------- */ + const char *pszINIT = CSLFetchNameValue(papszNV,"init"); + const char *pszColumn; + if( pszINIT != NULL && (pszColumn = strchr(pszINIT, ':')) != NULL && + GetRoot()->FindChild( "AUTHORITY" ) < 0 ) + { + CPLString osAuthority; + osAuthority.assign(pszINIT, pszColumn - pszINIT); + OGR_SRSNode* poAuthNode = new OGR_SRSNode( "AUTHORITY" ); + poAuthNode->AddChild( new OGR_SRSNode( osAuthority ) ); + poAuthNode->AddChild( new OGR_SRSNode( pszColumn + 1 ) ); + + GetRoot()->AddChild( poAuthNode ); + } + CSLDestroy( papszNV ); - + return OGRERR_NONE; } +/************************************************************************/ +/* LinearToProj4() */ +/************************************************************************/ + +static const char *LinearToProj4( double dfLinearConv, + const char *pszLinearUnits ) + +{ + if( dfLinearConv == 1.0 ) + return "m"; + + else if( dfLinearConv == 1000.0 ) + return "km"; + + else if( dfLinearConv == 0.0254 ) + return "in"; + + else if( EQUAL(pszLinearUnits,SRS_UL_FOOT) + || fabs(dfLinearConv - atof(SRS_UL_FOOT_CONV)) < 0.000000001 ) + return "ft"; + + else if( EQUAL(pszLinearUnits,"IYARD") || dfLinearConv == 0.9144 ) + return "yd"; + + else if( dfLinearConv == 0.914401828803658 ) + return "us-yd"; + + else if( dfLinearConv == 0.001 ) + return "mm"; + + else if( dfLinearConv == 0.01 ) + return "cm"; + + else if( EQUAL(pszLinearUnits,SRS_UL_US_FOOT) + || fabs(dfLinearConv - atof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + return "us-ft"; + + else if( EQUAL(pszLinearUnits,SRS_UL_NAUTICAL_MILE) ) + return "kmi"; + + else if( EQUAL(pszLinearUnits,"Mile") + || EQUAL(pszLinearUnits,"IMILE") ) + return "mi"; + else + return NULL; +} + /************************************************************************/ /* OSRExportToProj4() */ @@ -1082,6 +1470,13 @@ OGRErr CPL_STDCALL OSRExportToProj4( OGRSpatialReferenceH hSRS, /* exportToProj4() */ /************************************************************************/ +#define SAFE_PROJ4_STRCAT(szNewStr) do { \ + if(CPLStrlcat(szProj4, szNewStr, sizeof(szProj4)) >= sizeof(szProj4)) { \ + CPLError(CE_Failure, CPLE_AppDefined, "String overflow when formatting proj.4 string"); \ + *ppszProj4 = CPLStrdup(""); \ + return OGRERR_FAILURE; \ + } } while(0); + /** * \brief Export coordinate system in PROJ.4 format. * @@ -1092,6 +1487,13 @@ OGRErr CPL_STDCALL OSRExportToProj4( OGRSpatialReferenceH hSRS, * LOCAL_CS coordinate systems are not translatable. An empty string * will be returned along with OGRERR_NONE. * + * Special processing for Transverse Mercator with GDAL >= 1.10 and PROJ >= 4.8 : + * if the OSR_USE_ETMERC configuration option is set to YES, the PROJ.4 + * definition built from the SRS will use the 'etmerc' projection method, + * rather than the default 'tmerc'. This will give better accuracy (at the + * expense of computational speed) when reprojection occurs near the edges + * of the validity area for the projection. + * * This method is the equivelent of the C function OSRExportToProj4(). * * @param ppszProj4 pointer to which dynamically allocated PROJ.4 definition @@ -1113,7 +1515,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const { *ppszProj4 = CPLStrdup(""); CPLError( CE_Failure, CPLE_NotSupported, - "No translation an empty SRS to PROJ.4 format is known."); + "No translation for an empty SRS to PROJ.4 format is known."); return OGRERR_UNSUPPORTED_SRS; } @@ -1148,6 +1550,11 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const { sprintf( szProj4+strlen(szProj4), "+proj=longlat " ); } + else if( IsGeocentric() ) + { + sprintf( szProj4+strlen(szProj4), "+proj=geocent " ); + } + else if( pszProjection == NULL && !IsGeographic() ) { // LOCAL_CS, or incompletely initialized coordinate systems. @@ -1157,7 +1564,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) { sprintf( szProj4+strlen(szProj4), - "+proj=cea +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=cea +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), @@ -1167,7 +1574,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_BONNE) ) { sprintf( szProj4+strlen(szProj4), - "+proj=bonne +lon_0=%.16g +lat_1=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=bonne +lon_0=%.16g +lat_1=%.16g +x_0=%.16g +y_0=%.16g ", GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), @@ -1177,7 +1584,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_CASSINI_SOLDNER) ) { sprintf( szProj4+strlen(szProj4), - "+proj=cass +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=cass +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), @@ -1204,7 +1611,17 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const int bNorth; int nZone = GetUTMZone( &bNorth ); - if( nZone != 0 ) + if( CSLTestBoolean(CPLGetConfigOption("OSR_USE_ETMERC", "FALSE")) ) + { + sprintf( szProj4+strlen(szProj4), + "+proj=etmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + else if( nZone != 0 ) { if( bNorth ) sprintf( szProj4+strlen(szProj4), "+proj=utm +zone=%d ", @@ -1215,7 +1632,18 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const } else sprintf( szProj4+strlen(szProj4), - "+proj=tmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=tmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED) ) + { + sprintf( szProj4+strlen(szProj4), + "+proj=tmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g +axis=wsu ", GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), @@ -1251,7 +1679,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) ) { sprintf( szProj4+strlen(szProj4), - "+proj=merc +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=merc +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), @@ -1261,7 +1689,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_OBLIQUE_STEREOGRAPHIC) ) { sprintf( szProj4+strlen(szProj4), - "+proj=sterea +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=sterea +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", // "+proj=stere +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), @@ -1273,7 +1701,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_STEREOGRAPHIC) ) { sprintf( szProj4+strlen(szProj4), - "+proj=stere +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + "+proj=stere +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), @@ -1316,14 +1744,14 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_GAUSSSCHREIBERTMERCATOR) ) { - sprintf( szProj4+strlen(szProj4), - "+proj=gstmerc +lat_0=%.16g +lon_0=%.16g" - " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", - GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,-21.116666667), - GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,55.53333333309), - GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), - GetNormProjParm(SRS_PP_FALSE_EASTING,160000.000), - GetNormProjParm(SRS_PP_FALSE_NORTHING,50000.000) ); + sprintf( szProj4+strlen(szProj4), + "+proj=gstmerc +lat_0=%.16g +lon_0=%.16g" + " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,-21.116666667), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,55.53333333309), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,160000.000), + GetNormProjParm(SRS_PP_FALSE_NORTHING,50000.000) ); } else if( EQUAL(pszProjection,SRS_PT_GNOMONIC) ) @@ -1382,7 +1810,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_MILLER_CYLINDRICAL) ) { sprintf( szProj4+strlen(szProj4), - "+proj=mill +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +R_A ", + "+proj=mill +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +R_A ", GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), @@ -1520,6 +1948,11 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); } + else if( EQUAL(pszProjection,SRS_PT_IGH) ) + { + sprintf( szProj4+strlen(szProj4), "+proj=igh " ); + } + else if( EQUAL(pszProjection,SRS_PT_GEOSTATIONARY_SATELLITE) ) { sprintf( szProj4+strlen(szProj4), @@ -1531,7 +1964,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const } else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) - || EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) + || EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) { sprintf( szProj4+strlen(szProj4), "+proj=lcc +lat_1=%.16g +lat_2=%.16g +lat_0=%.16g +lon_0=%.16g" @@ -1559,10 +1992,43 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) { - /* not clear how ProjParm[3] - angle from rectified to skewed grid - - should be applied ... see the +not_rot flag for PROJ.4. - Just ignoring for now. */ + /* special case for swiss oblique mercator : see bug 423 */ + if( fabs(GetNormProjParm(SRS_PP_AZIMUTH,0.0) - 90.0) < 0.0001 + && fabs(GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,0.0)-90.0) < 0.0001 ) + { + sprintf( szProj4+strlen(szProj4), + "+proj=somerc +lat_0=%.16g +lon_0=%.16g" + " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + else + { + sprintf( szProj4+strlen(szProj4), + "+proj=omerc +lat_0=%.16g +lonc=%.16g +alpha=%.16g" + " +k=%.16g +x_0=%.16g +y_0=%.16g +no_uoff ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_AZIMUTH,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + + // RSO variant - http://trac.osgeo.org/proj/ticket/62 + // Note that gamma is only supported by PROJ 4.8.0 and later. + if( GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0) != 1000.0 ) + { + sprintf( szProj4+strlen(szProj4), "+gamma=%.16g ", + GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0)); + } + } + } + else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER)) + { /* special case for swiss oblique mercator : see bug 423 */ if( fabs(GetNormProjParm(SRS_PP_AZIMUTH,0.0) - 90.0) < 0.0001 && fabs(GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,0.0)-90.0) < 0.0001 ) @@ -1587,6 +2053,14 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + + // RSO variant - http://trac.osgeo.org/proj/ticket/62 + // Note that gamma is only supported by PROJ 4.8.0 and later. + if( GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0) != 1000.0 ) + { + sprintf( szProj4+strlen(szProj4), "+gamma=%.16g ", + GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0)); + } } } @@ -1823,22 +2297,24 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const { pszPROJ4Ellipse = "WGS84"; } - else if( EQUAL(pszDatum,"North_American_Datum_1927") ) + else if( pszDatum != NULL && EQUAL(pszDatum,"North_American_Datum_1927") ) { // pszPROJ4Ellipse = "clrk66:+datum=nad27"; /* NAD 27 */ pszPROJ4Ellipse = "clrk66"; } - else if( EQUAL(pszDatum,"North_American_Datum_1983") ) + else if( pszDatum != NULL && EQUAL(pszDatum,"North_American_Datum_1983") ) { // pszPROJ4Ellipse = "GRS80:+datum=nad83"; /* NAD 83 */ pszPROJ4Ellipse = "GRS80"; } - + + char szEllipseDef[128]; + if( pszPROJ4Ellipse == NULL ) - sprintf( szProj4+strlen(szProj4), "+a=%.16g +b=%.16g ", + sprintf( szEllipseDef, "+a=%.16g +b=%.16g ", GetSemiMajor(), GetSemiMinor() ); else - sprintf( szProj4+strlen(szProj4), "+ellps=%s ", + sprintf( szEllipseDef, "+ellps=%s ", pszPROJ4Ellipse ); /* -------------------------------------------------------------------- */ @@ -1851,6 +2327,7 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const const char *pszAuthority; int nEPSGGeogCS = -1; const char *pszGeogCSAuthority; + const char *pszProj4Grids = GetExtension( "DATUM", "PROJ4_GRIDS" ); pszAuthority = GetAuthorityName( "DATUM" ); @@ -1866,78 +2343,104 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const /* nothing */; else if( EQUAL(pszDatum,SRS_DN_NAD27) || nEPSGDatum == 6267 ) - pszPROJ4Datum = "+datum=NAD27"; + pszPROJ4Datum = "NAD27"; else if( EQUAL(pszDatum,SRS_DN_NAD83) || nEPSGDatum == 6269 ) - pszPROJ4Datum = "+datum=NAD83"; + pszPROJ4Datum = "NAD83"; else if( EQUAL(pszDatum,SRS_DN_WGS84) || nEPSGDatum == 6326 ) - pszPROJ4Datum = "+datum=WGS84"; + pszPROJ4Datum = "WGS84"; else if( (pszPROJ4Datum = OGRGetProj4Datum(pszDatum, nEPSGDatum)) != NULL ) { - strcat( szProj4, "+datum=" ); - /* The datum name contained in pszPROJ4Datum will be appended below */ + /* nothing */ } - else if( poTOWGS84 != NULL ) + if( pszProj4Grids != NULL ) { - int bOverflow = FALSE; - int iChild; - for(iChild=0;iChildGetChildCount() && !bOverflow;iChild++) - { - if (strlen(poTOWGS84->GetChild(iChild)->GetValue()) > 24) - bOverflow = TRUE; - } - - if( !bOverflow && poTOWGS84->GetChildCount() > 2 - && (poTOWGS84->GetChildCount() < 6 - || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"") - && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"") - && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"") - && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) ) - { - sprintf( szTOWGS84, "+towgs84=%s,%s,%s", - poTOWGS84->GetChild(0)->GetValue(), - poTOWGS84->GetChild(1)->GetValue(), - poTOWGS84->GetChild(2)->GetValue() ); - pszPROJ4Datum = szTOWGS84; - } - else if( !bOverflow && poTOWGS84->GetChildCount() > 6) - { - sprintf( szTOWGS84, "+towgs84=%s,%s,%s,%s,%s,%s,%s", - poTOWGS84->GetChild(0)->GetValue(), - poTOWGS84->GetChild(1)->GetValue(), - poTOWGS84->GetChild(2)->GetValue(), - poTOWGS84->GetChild(3)->GetValue(), - poTOWGS84->GetChild(4)->GetValue(), - poTOWGS84->GetChild(5)->GetValue(), - poTOWGS84->GetChild(6)->GetValue() ); - pszPROJ4Datum = szTOWGS84; - } + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + SAFE_PROJ4_STRCAT( "+nadgrids=" ); + SAFE_PROJ4_STRCAT( pszProj4Grids ); + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; } - else if( nEPSGGeogCS != -1 ) + if( pszPROJ4Datum == NULL + || CSLTestBoolean(CPLGetConfigOption("OVERRIDE_PROJ_DATUM_WITH_TOWGS84", "YES")) ) { - double padfTransform[7]; - if( EPSGGetWGS84Transform( nEPSGGeogCS, padfTransform ) ) + if( poTOWGS84 != NULL ) { - sprintf( szTOWGS84, "+towgs84=%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g", - padfTransform[0], - padfTransform[1], - padfTransform[2], - padfTransform[3], - padfTransform[4], - padfTransform[5], - padfTransform[6] ); - pszPROJ4Datum = szTOWGS84; + int iChild; + if( poTOWGS84->GetChildCount() >= 3 + && (poTOWGS84->GetChildCount() < 7 + || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) ) + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + SAFE_PROJ4_STRCAT( "+towgs84="); + for(iChild = 0; iChild < 3; iChild ++) + { + if (iChild > 0 ) SAFE_PROJ4_STRCAT( "," ); + SAFE_PROJ4_STRCAT( poTOWGS84->GetChild(iChild)->GetValue() ); + } + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } + else if( poTOWGS84->GetChildCount() >= 7) + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + SAFE_PROJ4_STRCAT( "+towgs84="); + for(iChild = 0; iChild < 7; iChild ++) + { + if (iChild > 0 ) SAFE_PROJ4_STRCAT( "," ); + SAFE_PROJ4_STRCAT( poTOWGS84->GetChild(iChild)->GetValue() ); + } + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } + } + + // If we don't know the datum, trying looking up TOWGS84 parameters + // based on the EPSG GCS code. + else if( nEPSGGeogCS != -1 && pszPROJ4Datum == NULL ) + { + double padfTransform[7]; + if( EPSGGetWGS84Transform( nEPSGGeogCS, padfTransform ) ) + { + sprintf( szTOWGS84, "+towgs84=%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g", + padfTransform[0], + padfTransform[1], + padfTransform[2], + padfTransform[3], + padfTransform[4], + padfTransform[5], + padfTransform[6] ); + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + + SAFE_PROJ4_STRCAT( szTOWGS84 ); + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } } } if( pszPROJ4Datum != NULL ) { - strcat( szProj4, pszPROJ4Datum ); - strcat( szProj4, " " ); + SAFE_PROJ4_STRCAT( "+datum=" ); + SAFE_PROJ4_STRCAT( pszPROJ4Datum ); + SAFE_PROJ4_STRCAT( " " ); + } + else // The ellipsedef may already have been appended and will now + // be empty, otherwise append now. + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; } /* -------------------------------------------------------------------- */ @@ -1953,61 +2456,24 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") ) nCode = atoi(GetAuthorityCode( "PRIMEM" )); - switch( nCode ) - { - case 8902: - strcpy( szPMValue, "lisbon" ); - break; - - case 8903: - strcpy( szPMValue, "paris" ); - break; - - case 8904: - strcpy( szPMValue, "bogota" ); - break; - - case 8905: - strcpy( szPMValue, "madrid" ); - break; - - case 8906: - strcpy( szPMValue, "rome" ); - break; - - case 8907: - strcpy( szPMValue, "bern" ); - break; - - case 8908: - strcpy( szPMValue, "jakarta" ); - break; - - case 8909: - strcpy( szPMValue, "ferro" ); - break; - - case 8910: - strcpy( szPMValue, "brussels" ); - break; - - case 8911: - strcpy( szPMValue, "stockholm" ); - break; - - case 8912: - strcpy( szPMValue, "athens" ); - break; - - case 8913: - strcpy( szPMValue, "oslo" ); - break; + const OGRProj4PM* psProj4PM = NULL; + if (nCode > 0) + psProj4PM = OGRGetProj4PMFromCode(nCode); + if (psProj4PM == NULL) + psProj4PM = OGRGetProj4PMFromVal(dfFromGreenwich); - default: + if (psProj4PM != NULL) + { + strcpy( szPMValue, psProj4PM->pszProj4PMName ); + } + else + { sprintf( szPMValue, "%.16g", dfFromGreenwich ); } - sprintf( szProj4+strlen(szProj4), "+pm=%s ", szPMValue ); + SAFE_PROJ4_STRCAT( "+pm=" ); + SAFE_PROJ4_STRCAT( szPMValue ); + SAFE_PROJ4_STRCAT( " " ); } /* -------------------------------------------------------------------- */ @@ -2021,55 +2487,78 @@ OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const if( strstr(szProj4,"longlat") != NULL ) pszPROJ4Units = NULL; - - else if( dfLinearConv == 1.0 ) - pszPROJ4Units = "m"; + else + { + pszPROJ4Units = LinearToProj4( dfLinearConv, pszLinearUnits ); - else if( dfLinearConv == 1000.0 ) - pszPROJ4Units = "km"; - - else if( dfLinearConv == 0.0254 ) - pszPROJ4Units = "in"; - - else if( EQUAL(pszLinearUnits,SRS_UL_FOOT) - || fabs(dfLinearConv - atof(SRS_UL_FOOT_CONV)) < 0.000000001 ) - pszPROJ4Units = "ft"; - - else if( EQUAL(pszLinearUnits,"IYARD") || dfLinearConv == 0.9144 ) - pszPROJ4Units = "yd"; - - else if( dfLinearConv == 0.001 ) - pszPROJ4Units = "mm"; - - else if( dfLinearConv == 0.01 ) - pszPROJ4Units = "cm"; + if( pszPROJ4Units == NULL ) + { + char szLinearConv[128]; + sprintf( szLinearConv, "%.16g", dfLinearConv ); + SAFE_PROJ4_STRCAT( "+to_meter=" ); + SAFE_PROJ4_STRCAT( szLinearConv ); + SAFE_PROJ4_STRCAT( " " ); + } + } - else if( EQUAL(pszLinearUnits,SRS_UL_US_FOOT) - || fabs(dfLinearConv - atof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) - pszPROJ4Units = "us-ft"; + if( pszPROJ4Units != NULL ) + { + SAFE_PROJ4_STRCAT( "+units="); + SAFE_PROJ4_STRCAT( pszPROJ4Units ); + SAFE_PROJ4_STRCAT( " " ); + } - else if( EQUAL(pszLinearUnits,SRS_UL_NAUTICAL_MILE) ) - pszPROJ4Units = "kmi"; +/* -------------------------------------------------------------------- */ +/* If we have vertical datum grids, attach them to the proj.4 string.*/ +/* -------------------------------------------------------------------- */ + const char *pszProj4Geoids = GetExtension( "VERT_DATUM", "PROJ4_GRIDS" ); + + if( pszProj4Geoids != NULL ) + { + SAFE_PROJ4_STRCAT( "+geoidgrids=" ); + SAFE_PROJ4_STRCAT( pszProj4Geoids ); + SAFE_PROJ4_STRCAT( " " ); + } - else if( EQUAL(pszLinearUnits,"Mile") - || EQUAL(pszLinearUnits,"IMILE") ) - pszPROJ4Units = "mi"; +/* -------------------------------------------------------------------- */ +/* Handle vertical units, but only if we have them. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poVERT_CS = GetRoot()->GetNode( "VERT_CS" ); + const OGR_SRSNode *poVUNITS = NULL; - else + if( poVERT_CS != NULL ) + poVUNITS = poVERT_CS->GetNode( "UNIT" ); + + if( poVUNITS != NULL && poVUNITS->GetChildCount() >= 2 ) { - sprintf( szProj4+strlen(szProj4), "+to_meter=%.16g ", - dfLinearConv ); - } + pszPROJ4Units = NULL; - if( pszPROJ4Units != NULL ) - sprintf( szProj4+strlen(szProj4), "+units=%s ", - pszPROJ4Units ); + dfLinearConv = CPLAtof( poVUNITS->GetChild(1)->GetValue() ); + + pszPROJ4Units = LinearToProj4( dfLinearConv, + poVUNITS->GetChild(0)->GetValue() ); + + if( pszPROJ4Units == NULL ) + { + char szLinearConv[128]; + sprintf( szLinearConv, "%.16g", dfLinearConv ); + SAFE_PROJ4_STRCAT( "+vto_meter=" ); + SAFE_PROJ4_STRCAT( szLinearConv ); + SAFE_PROJ4_STRCAT( " " ); + } + else + { + SAFE_PROJ4_STRCAT( "+vunits="); + SAFE_PROJ4_STRCAT( pszPROJ4Units ); + SAFE_PROJ4_STRCAT( " " ); + } + } /* -------------------------------------------------------------------- */ /* Add the no_defs flag to ensure that no values from */ /* proj_def.dat are implicitly used with our definitions. */ /* -------------------------------------------------------------------- */ - sprintf( szProj4+strlen(szProj4), "+no_defs " ); + SAFE_PROJ4_STRCAT( "+no_defs " ); *ppszProj4 = CPLStrdup( szProj4 ); diff --git a/ogr/ogr_srs_xml.cpp b/ogr/ogr_srs_xml.cpp index 3e66022..bca763f 100644 --- a/ogr/ogr_srs_xml.cpp +++ b/ogr/ogr_srs_xml.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_srs_xml.cpp 16587 2009-03-15 00:09:42Z rouault $ + * $Id: ogr_srs_xml.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: OGRSpatialReference interface to OGC XML (014r4). @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2001, Frank Warmerdam (warmerdam@pobox.com) + * Copyright (c) 2008-2012, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -1331,6 +1332,7 @@ OGRErr OSRImportFromXML( OGRSpatialReferenceH hSRS, const char *pszXML ) { VALIDATE_POINTER1( hSRS, "OSRImportFromXML", CE_Failure ); + VALIDATE_POINTER1( pszXML, "OSRImportFromXML", CE_Failure ); return ((OGRSpatialReference *) hSRS)->importFromXML( pszXML ); } diff --git a/ogr/ogr_srsnode.cpp b/ogr/ogr_srsnode.cpp index 4a657c2..5b6858b 100644 --- a/ogr/ogr_srsnode.cpp +++ b/ogr/ogr_srsnode.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogr_srsnode.cpp 18443 2010-01-05 19:33:15Z rouault $ + * $Id: ogr_srsnode.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGR_SRSNode class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2010-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,7 @@ #include "ogr_spatialref.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogr_srsnode.cpp 18443 2010-01-05 19:33:15Z rouault $"); +CPL_CVSID("$Id: ogr_srsnode.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGR_SRSNode() */ @@ -576,9 +577,26 @@ OGRErr OGR_SRSNode::exportToPrettyWkt( char ** ppszResult, int nDepth ) const OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput ) +{ + int nNodes = 0; + return importFromWkt( ppszInput, 0, &nNodes ); +} + +OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput, int nRecLevel, int* pnNodes ) + { const char *pszInput = *ppszInput; int bInQuotedString = FALSE; + + /* Sanity checks */ + if( nRecLevel == 10 ) + { + return OGRERR_CORRUPT_DATA; + } + if( *pnNodes == 1000 ) + { + return OGRERR_CORRUPT_DATA; + } /* -------------------------------------------------------------------- */ /* Clear any existing children of this node. */ @@ -637,7 +655,8 @@ OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput ) poNewChild = new OGR_SRSNode(); - eErr = poNewChild->importFromWkt( (char **) &pszInput ); + (*pnNodes) ++; + eErr = poNewChild->importFromWkt( (char **) &pszInput, nRecLevel + 1, pnNodes ); if( eErr != OGRERR_NONE ) { delete poNewChild; @@ -646,6 +665,10 @@ OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput ) AddChild( poNewChild ); + // swallow whitespace + while( isspace(*pszInput) ) + pszInput++; + } while( *pszInput == ',' ); if( *pszInput != ')' && *pszInput != ']' ) @@ -765,7 +788,8 @@ OGRErr OGR_SRSNode::applyRemapper( const char *pszNode, { for( i = 0; papszSrcValues[i] != NULL; i += nStepSize ) { - if( EQUAL(papszSrcValues[i],pszValue) ) + if( EQUAL(papszSrcValues[i],pszValue) && + ! EQUAL(papszDstValues[i],"") ) { SetValue( papszDstValues[i] ); break; @@ -824,6 +848,26 @@ void OGR_SRSNode::StripNodes( const char * pszName ) /* FixupOrdering() */ /************************************************************************/ +/* EXTENSION ... being a OSR extension... is arbitrary placed before the AUTHORITY */ +static const char * const apszPROJCSRule[] = +{ "PROJCS", "GEOGCS", "PROJECTION", "PARAMETER", "UNIT", "AXIS", "EXTENSION", "AUTHORITY", + NULL }; + +static const char * const apszDATUMRule[] = +{ "DATUM", "SPHEROID", "TOWGS84", "AUTHORITY", NULL }; + +static const char * const apszGEOGCSRule[] = +{ "GEOGCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL }; + +static const char * const apszGEOCCSRule[] = +{ "GEOCCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL }; + +static const char * const apszVERTCSRule[] = +{ "VERT_CS", "VERT_DATUM", "UNIT", "AXIS", "AUTHORITY", NULL }; + +static const char * const *apszOrderingRules[] = { + apszPROJCSRule, apszGEOGCSRule, apszDATUMRule, apszGEOCCSRule, apszVERTCSRule, NULL }; + /** * Correct parameter ordering to match CT Specification. * @@ -839,19 +883,6 @@ void OGR_SRSNode::StripNodes( const char * pszName ) * wrong. */ -static const char * const apszPROJCSRule[] = -{ "PROJCS", "GEOGCS", "PROJECTION", "PARAMETER", "UNIT", "AXIS", "AUTHORITY", - NULL }; - -static const char * const apszDATUMRule[] = -{ "DATUM", "SPHEROID", "TOWGS84", "AUTHORITY", NULL }; - -static const char * const apszGEOGCSRule[] = -{ "GEOGCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL }; - -static const char * const *apszOrderingRules[] = { - apszPROJCSRule, apszGEOGCSRule, apszDATUMRule, NULL }; - OGRErr OGR_SRSNode::FixupOrdering() { diff --git a/ogr/ogrct.cpp b/ogr/ogrct.cpp index baaed60..3b4c0e7 100644 --- a/ogr/ogrct.cpp +++ b/ogr/ogrct.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrct.cpp 18520 2010-01-11 03:59:14Z warmerdam $ + * $Id: ogrct.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRSCoordinateTransformation class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -38,7 +39,7 @@ #include "proj_api.h" #endif -CPL_CVSID("$Id: ogrct.cpp 18520 2010-01-11 03:59:14Z warmerdam $"); +CPL_CVSID("$Id: ogrct.cpp 27044 2014-03-16 23:41:27Z rouault $"); /* ==================================================================== */ /* PROJ.4 interface stuff. */ @@ -47,18 +48,22 @@ CPL_CVSID("$Id: ogrct.cpp 18520 2010-01-11 03:59:14Z warmerdam $"); typedef struct { double u, v; } projUV; #define projPJ void * - +#define projCtx void * #define RAD_TO_DEG 57.29577951308232 #define DEG_TO_RAD .0174532925199432958 +#else + +#if PJ_VERSION < 480 +#define projCtx void * +#endif + #endif static void *hPROJMutex = NULL; static projPJ (*pfn_pj_init_plus)(const char *) = NULL; static projPJ (*pfn_pj_init)(int, char**) = NULL; -static projUV (*pfn_pj_fwd)(projUV, projPJ) = NULL; -static projUV (*pfn_pj_inv)(projUV, projPJ) = NULL; static void (*pfn_pj_free)(projPJ) = NULL; static int (*pfn_pj_transform)(projPJ, projPJ, long, int, double *, double *, double * ) = NULL; @@ -67,6 +72,11 @@ static char *(*pfn_pj_strerrno)(int) = NULL; static char *(*pfn_pj_get_def)(projPJ,int) = NULL; static void (*pfn_pj_dalloc)(void *) = NULL; +static projPJ (*pfn_pj_init_plus_ctx)( projCtx, const char * ) = NULL; +static int (*pfn_pj_ctx_get_errno)( projCtx ) = NULL; +static projCtx (*pfn_pj_ctx_alloc)(void) = NULL; +static void (*pfn_pj_ctx_free)( projCtx ) = NULL; + #if (defined(WIN32) || defined(WIN32CE)) && !defined(__MINGW32__) # define LIBNAME "proj.dll" #elif defined(__MINGW32__) @@ -85,6 +95,19 @@ static void (*pfn_pj_dalloc)(void *) = NULL; # define LIBNAME "libproj.so" #endif +/************************************************************************/ +/* OCTCleanupProjMutex() */ +/************************************************************************/ + +void OCTCleanupProjMutex() +{ + if( hPROJMutex != NULL ) + { + CPLDestroyMutex(hPROJMutex); + hPROJMutex = NULL; + } +} + /************************************************************************/ /* OGRProj4CT */ /************************************************************************/ @@ -95,7 +118,6 @@ class OGRProj4CT : public OGRCoordinateTransformation void *psPJSource; int bSourceLatLong; double dfSourceToRadians; - double dfSourceFromRadians; int bSourceWrap; double dfSourceWrapLong; @@ -103,15 +125,29 @@ class OGRProj4CT : public OGRCoordinateTransformation OGRSpatialReference *poSRSTarget; void *psPJTarget; int bTargetLatLong; - double dfTargetToRadians; double dfTargetFromRadians; int bTargetWrap; double dfTargetWrapLong; + int bIdentityTransform; + int nErrorCount; int bCheckWithInvertProj; double dfThreshold; + + projCtx pjctx; + + int InitializeNoLock( OGRSpatialReference *poSource, + OGRSpatialReference *poTarget ); + + int nMaxCount; + double *padfOriX; + double *padfOriY; + double *padfOriZ; + double *padfTargetX; + double *padfTargetY; + double *padfTargetZ; public: OGRProj4CT(); @@ -165,8 +201,6 @@ static int LoadProjLibrary() #ifdef PROJ_STATIC pfn_pj_init = pj_init; pfn_pj_init_plus = pj_init_plus; - pfn_pj_fwd = pj_fwd; - pfn_pj_inv = pj_inv; pfn_pj_free = pj_free; pfn_pj_transform = pj_transform; pfn_pj_get_errno_ref = (int *(*)(void)) pj_get_errno_ref; @@ -174,7 +208,13 @@ static int LoadProjLibrary() pfn_pj_dalloc = pj_dalloc; #if PJ_VERSION >= 446 pfn_pj_get_def = pj_get_def; -#endif +#endif +#if PJ_VERSION >= 480 + pfn_pj_ctx_alloc = pj_ctx_alloc; + pfn_pj_ctx_free = pj_ctx_free; + pfn_pj_init_plus_ctx = pj_init_plus_ctx; + pfn_pj_ctx_get_errno = pj_ctx_get_errno; +#endif #else CPLPushErrorHandler( CPLQuietErrorHandler ); @@ -187,10 +227,6 @@ static int LoadProjLibrary() pfn_pj_init_plus = (projPJ (*)(const char *)) CPLGetSymbol( pszLibName, "pj_init_plus" ); - pfn_pj_fwd = (projUV (*)(projUV,projPJ)) - CPLGetSymbol( pszLibName, "pj_fwd" ); - pfn_pj_inv = (projUV (*)(projUV,projPJ)) - CPLGetSymbol( pszLibName, "pj_inv" ); pfn_pj_free = (void (*)(projPJ)) CPLGetSymbol( pszLibName, "pj_free" ); pfn_pj_transform = (int (*)(projPJ,projPJ,long,int,double*, @@ -206,10 +242,37 @@ static int LoadProjLibrary() CPLGetSymbol( pszLibName, "pj_get_def" ); pfn_pj_dalloc = (void (*)(void*)) CPLGetSymbol( pszLibName, "pj_dalloc" ); - CPLPopErrorHandler(); + /* PROJ 4.8.0 symbols */ + pfn_pj_ctx_alloc = (projCtx (*)( void )) + CPLGetSymbol( pszLibName, "pj_ctx_alloc" ); + pfn_pj_ctx_free = (void (*)( projCtx )) + CPLGetSymbol( pszLibName, "pj_ctx_free" ); + pfn_pj_init_plus_ctx = (projPJ (*)( projCtx, const char * )) + CPLGetSymbol( pszLibName, "pj_init_plus_ctx" ); + pfn_pj_ctx_get_errno = (int (*)( projCtx )) + CPLGetSymbol( pszLibName, "pj_ctx_get_errno" ); + + CPLPopErrorHandler(); + CPLErrorReset(); #endif + if (pfn_pj_ctx_alloc != NULL && + pfn_pj_ctx_free != NULL && + pfn_pj_init_plus_ctx != NULL && + pfn_pj_ctx_get_errno != NULL && + CSLTestBoolean(CPLGetConfigOption("USE_PROJ_480_FEATURES", "YES"))) + { + CPLDebug("OGRCT", "PROJ >= 4.8.0 features enabled"); + } + else + { + pfn_pj_ctx_alloc = NULL; + pfn_pj_ctx_free = NULL; + pfn_pj_init_plus_ctx = NULL; + pfn_pj_ctx_get_errno = NULL; + } + if( pfn_pj_transform == NULL ) { CPLError( CE_Failure, CPLE_AppDefined, @@ -237,6 +300,7 @@ char *OCTProj4Normalize( const char *pszProj4Src ) { char *pszNewProj4Def, *pszCopy; projPJ psPJSource = NULL; + CPLMutexHolderD( &hPROJMutex ); if( !LoadProjLibrary() || pfn_pj_dalloc == NULL || pfn_pj_get_def == NULL ) @@ -333,7 +397,7 @@ OGRCreateCoordinateTransformation( OGRSpatialReference *poSource, { OGRProj4CT *poCT; - if( !LoadProjLibrary() ) + if( pfn_pj_init == NULL && !LoadProjLibrary() ) { CPLError( CE_Failure, CPLE_NotSupported, "Unable to load PROJ.4 library (%s), creation of\n" @@ -359,6 +423,24 @@ OGRCreateCoordinateTransformation( OGRSpatialReference *poSource, /* OCTNewCoordinateTransformation() */ /************************************************************************/ +/** + * Create transformation object. + * + * This is the same as the C++ function OGRCreateCoordinateTransformation(). + * + * Input spatial reference system objects are assigned + * by copy (calling clone() method) and no ownership transfer occurs. + * + * OCTDestroyCoordinateTransformation() should + * be used to destroy transformation objects. + * + * The PROJ.4 library must be available at run-time. + * + * @param hSourceSRS source spatial reference system. + * @param hTargetSRS target spatial reference system. + * @return NULL on failure or a ready to use transformation object. + */ + OGRCoordinateTransformationH CPL_STDCALL OCTNewCoordinateTransformation( OGRSpatialReferenceH hSourceSRS, OGRSpatialReferenceH hTargetSRS ) @@ -382,10 +464,24 @@ OGRProj4CT::OGRProj4CT() psPJSource = NULL; psPJTarget = NULL; + bIdentityTransform = FALSE; nErrorCount = 0; bCheckWithInvertProj = FALSE; dfThreshold = 0; + + nMaxCount = 0; + padfOriX = NULL; + padfOriY = NULL; + padfOriZ = NULL; + padfTargetX = NULL; + padfTargetY = NULL; + padfTargetZ = NULL; + + if (pfn_pj_ctx_alloc != NULL) + pjctx = pfn_pj_ctx_alloc(); + else + pjctx = NULL; } /************************************************************************/ @@ -407,13 +503,33 @@ OGRProj4CT::~OGRProj4CT() delete poSRSTarget; } - CPLMutexHolderD( &hPROJMutex ); + if (pjctx != NULL) + { + pfn_pj_ctx_free(pjctx); - if( psPJSource != NULL ) - pfn_pj_free( psPJSource ); + if( psPJSource != NULL ) + pfn_pj_free( psPJSource ); - if( psPJTarget != NULL ) - pfn_pj_free( psPJTarget ); + if( psPJTarget != NULL ) + pfn_pj_free( psPJTarget ); + } + else + { + CPLMutexHolderD( &hPROJMutex ); + + if( psPJSource != NULL ) + pfn_pj_free( psPJSource ); + + if( psPJTarget != NULL ) + pfn_pj_free( psPJTarget ); + } + + CPLFree(padfOriX); + CPLFree(padfOriY); + CPLFree(padfOriZ); + CPLFree(padfTargetX); + CPLFree(padfTargetY); + CPLFree(padfTargetZ); } /************************************************************************/ @@ -424,8 +540,23 @@ int OGRProj4CT::Initialize( OGRSpatialReference * poSourceIn, OGRSpatialReference * poTargetIn ) { + if (pjctx != NULL) + { + return InitializeNoLock(poSourceIn, poTargetIn); + } + CPLMutexHolderD( &hPROJMutex ); + return InitializeNoLock(poSourceIn, poTargetIn); +} +/************************************************************************/ +/* InitializeNoLock() */ +/************************************************************************/ + +int OGRProj4CT::InitializeNoLock( OGRSpatialReference * poSourceIn, + OGRSpatialReference * poTargetIn ) + +{ if( poSourceIn == NULL || poTargetIn == NULL ) return FALSE; @@ -440,7 +571,6 @@ int OGRProj4CT::Initialize( OGRSpatialReference * poSourceIn, /* systems. */ /* -------------------------------------------------------------------- */ dfSourceToRadians = DEG_TO_RAD; - dfSourceFromRadians = RAD_TO_DEG; bSourceWrap = FALSE; dfSourceWrapLong = 0.0; @@ -452,12 +582,9 @@ int OGRProj4CT::Initialize( OGRSpatialReference * poSourceIn, dfSourceToRadians = atof(poUNITS->GetChild(1)->GetValue()); if( dfSourceToRadians == 0.0 ) dfSourceToRadians = DEG_TO_RAD; - else - dfSourceFromRadians = 1 / dfSourceToRadians; } } - dfTargetToRadians = DEG_TO_RAD; dfTargetFromRadians = RAD_TO_DEG; bTargetWrap = FALSE; dfTargetWrapLong = 0.0; @@ -467,10 +594,8 @@ int OGRProj4CT::Initialize( OGRSpatialReference * poSourceIn, OGR_SRSNode *poUNITS = poSRSTarget->GetAttrNode( "GEOGCS|UNIT" ); if( poUNITS && poUNITS->GetChildCount() >= 2 ) { - dfTargetToRadians = atof(poUNITS->GetChild(1)->GetValue()); - if( dfTargetToRadians == 0.0 ) - dfTargetToRadians = DEG_TO_RAD; - else + double dfTargetToRadians = atof(poUNITS->GetChild(1)->GetValue()); + if( dfTargetToRadians != 0.0 ) dfTargetFromRadians = 1 / dfTargetToRadians; } } @@ -522,89 +647,127 @@ int OGRProj4CT::Initialize( OGRSpatialReference * poSourceIn, // means debug output could be one "increment" late. static int nDebugReportCount = 0; - char *pszProj4Defn = NULL; + char *pszSrcProj4Defn = NULL; - if( poSRSSource->exportToProj4( &pszProj4Defn ) != OGRERR_NONE ) + if( poSRSSource->exportToProj4( &pszSrcProj4Defn ) != OGRERR_NONE ) { - CPLFree( pszProj4Defn ); + CPLFree( pszSrcProj4Defn ); return FALSE; } - if( strlen(pszProj4Defn) == 0 ) + if( strlen(pszSrcProj4Defn) == 0 ) { - CPLFree( pszProj4Defn ); + CPLFree( pszSrcProj4Defn ); CPLError( CE_Failure, CPLE_AppDefined, "No PROJ.4 translation for source SRS, coordinate\n" "transformation initialization has failed." ); return FALSE; } - psPJSource = pfn_pj_init_plus( pszProj4Defn ); + if (pjctx) + psPJSource = pfn_pj_init_plus_ctx( pjctx, pszSrcProj4Defn ); + else + psPJSource = pfn_pj_init_plus( pszSrcProj4Defn ); if( psPJSource == NULL ) { - if( pfn_pj_get_errno_ref != NULL + if( pjctx != NULL) + { + int pj_errno = pfn_pj_ctx_get_errno(pjctx); + + /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ + CPLMutexHolderD(&hPROJMutex); + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to initialize PROJ.4 with `%s'.\n%s", + pszSrcProj4Defn, pfn_pj_strerrno(pj_errno) ); + } + else if( pfn_pj_get_errno_ref != NULL && pfn_pj_strerrno != NULL ) { int *p_pj_errno = pfn_pj_get_errno_ref(); CPLError( CE_Failure, CPLE_NotSupported, "Failed to initialize PROJ.4 with `%s'.\n%s", - pszProj4Defn, pfn_pj_strerrno(*p_pj_errno) ); + pszSrcProj4Defn, pfn_pj_strerrno(*p_pj_errno) ); } else { CPLError( CE_Failure, CPLE_NotSupported, "Failed to initialize PROJ.4 with `%s'.\n", - pszProj4Defn ); + pszSrcProj4Defn ); } } if( nDebugReportCount < 10 ) - CPLDebug( "OGRCT", "Source: %s", pszProj4Defn ); - - CPLFree( pszProj4Defn ); + CPLDebug( "OGRCT", "Source: %s", pszSrcProj4Defn ); if( psPJSource == NULL ) + { + CPLFree( pszSrcProj4Defn ); return FALSE; + } /* -------------------------------------------------------------------- */ /* Establish PROJ.4 handle for target if projection. */ /* -------------------------------------------------------------------- */ - pszProj4Defn = NULL; - if( poSRSTarget->exportToProj4( &pszProj4Defn ) != OGRERR_NONE ) + char *pszDstProj4Defn = NULL; + + if( poSRSTarget->exportToProj4( &pszDstProj4Defn ) != OGRERR_NONE ) { - CPLFree( pszProj4Defn ); + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); return FALSE; } - if( strlen(pszProj4Defn) == 0 ) + if( strlen(pszDstProj4Defn) == 0 ) { - CPLFree( pszProj4Defn ); + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); CPLError( CE_Failure, CPLE_AppDefined, "No PROJ.4 translation for destination SRS, coordinate\n" "transformation initialization has failed." ); return FALSE; } - psPJTarget = pfn_pj_init_plus( pszProj4Defn ); + if (pjctx) + psPJTarget = pfn_pj_init_plus_ctx( pjctx, pszDstProj4Defn ); + else + psPJTarget = pfn_pj_init_plus( pszDstProj4Defn ); if( psPJTarget == NULL ) CPLError( CE_Failure, CPLE_NotSupported, "Failed to initialize PROJ.4 with `%s'.", - pszProj4Defn ); + pszDstProj4Defn ); if( nDebugReportCount < 10 ) { - CPLDebug( "OGRCT", "Target: %s", pszProj4Defn ); + CPLDebug( "OGRCT", "Target: %s", pszDstProj4Defn ); nDebugReportCount++; } - CPLFree( pszProj4Defn ); - if( psPJTarget == NULL ) + { + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); return FALSE; + } + + /* Determine if we really have a transformation to do */ + bIdentityTransform = (strcmp(pszSrcProj4Defn, pszDstProj4Defn) == 0); + + /* In case of identity transform, under the following conditions, */ + /* we can also avoid transforming from deegrees <--> radians. */ + if( bIdentityTransform && bSourceLatLong && !bSourceWrap && + bTargetLatLong && !bTargetWrap && + abs(dfSourceToRadians * dfTargetFromRadians - 1.0) < 1e-10 ) + { + /*bSourceLatLong = FALSE; + bTargetLatLong = FALSE;*/ + } + + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); return TRUE; } @@ -665,6 +828,8 @@ int CPL_STDCALL OCTTransform( OGRCoordinateTransformationH hTransform, int nCount, double *x, double *y, double *z ) { + VALIDATE_POINTER1( hTransform, "OCTTransform", FALSE ); + return ((OGRCoordinateTransformation*) hTransform)-> Transform( nCount, x, y,z ); } @@ -711,63 +876,62 @@ int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, /* -------------------------------------------------------------------- */ /* Do the transformation using PROJ.4. */ /* -------------------------------------------------------------------- */ - CPLMutexHolderD( &hPROJMutex ); - - if (bCheckWithInvertProj) + if( !bIdentityTransform && pjctx == NULL ) + { + /* The mutex has already been created */ + CPLAssert(hPROJMutex != NULL); + CPLAcquireMutex(hPROJMutex, 1000.0); + } + + if( bIdentityTransform ) + err = 0; + else if (bCheckWithInvertProj) { /* For some projections, we cannot detect if we are trying to reproject */ /* coordinates outside the validity area of the projection. So let's do */ /* the reverse reprojection and compare with the source coordinates */ - - double *ori_x = NULL; - double *ori_y = NULL; - double *ori_z = NULL; - ori_x = (double*)CPLMalloc(sizeof(double)*nCount); - memcpy(ori_x, x, sizeof(double)*nCount); - ori_y = (double*)CPLMalloc(sizeof(double)*nCount); - memcpy(ori_y, y, sizeof(double)*nCount); + if (nCount > nMaxCount) + { + nMaxCount = nCount; + padfOriX = (double*) CPLRealloc(padfOriX, sizeof(double)*nCount); + padfOriY = (double*) CPLRealloc(padfOriY, sizeof(double)*nCount); + padfOriZ = (double*) CPLRealloc(padfOriZ, sizeof(double)*nCount); + padfTargetX = (double*) CPLRealloc(padfTargetX, sizeof(double)*nCount); + padfTargetY = (double*) CPLRealloc(padfTargetY, sizeof(double)*nCount); + padfTargetZ = (double*) CPLRealloc(padfTargetZ, sizeof(double)*nCount); + } + memcpy(padfOriX, x, sizeof(double)*nCount); + memcpy(padfOriY, y, sizeof(double)*nCount); if (z) { - ori_z = (double*)CPLMalloc(sizeof(double)*nCount); - memcpy(ori_z, z, sizeof(double)*nCount); + memcpy(padfOriZ, z, sizeof(double)*nCount); } err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); if (err == 0) { - double* target_x = (double*)CPLMalloc(sizeof(double)*nCount); - double* target_y = (double*)CPLMalloc(sizeof(double)*nCount); - double* target_z = NULL; - memcpy(target_x, x, sizeof(double)*nCount); - memcpy(target_y, y, sizeof(double)*nCount); + memcpy(padfTargetX, x, sizeof(double)*nCount); + memcpy(padfTargetY, y, sizeof(double)*nCount); if (z) { - target_z = (double*)CPLMalloc(sizeof(double)*nCount); - memcpy(target_z, z, sizeof(double)*nCount); + memcpy(padfTargetZ, z, sizeof(double)*nCount); } err = pfn_pj_transform( psPJTarget, psPJSource , nCount, 1, - target_x, target_y, target_z ); + padfTargetX, padfTargetY, (z) ? padfTargetZ : NULL); if (err == 0) { for( i = 0; i < nCount; i++ ) { if ( x[i] != HUGE_VAL && y[i] != HUGE_VAL && - (fabs(target_x[i] - ori_x[i]) > dfThreshold || - fabs(target_y[i] - ori_y[i]) > dfThreshold) ) + (fabs(padfTargetX[i] - padfOriX[i]) > dfThreshold || + fabs(padfTargetY[i] - padfOriY[i]) > dfThreshold) ) { x[i] = HUGE_VAL; y[i] = HUGE_VAL; } } } - - CPLFree(target_x); - CPLFree(target_y); - CPLFree(target_z); } - CPLFree(ori_x); - CPLFree(ori_y); - CPLFree(ori_z); } else { @@ -787,6 +951,10 @@ int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, if( ++nErrorCount < 20 ) { + if (pjctx != NULL) + /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ + CPLAcquireMutex(hPROJMutex, 1000.0); + const char *pszError = NULL; if( pfn_pj_strerrno != NULL ) pszError = pfn_pj_strerrno( err ); @@ -797,6 +965,10 @@ int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, err ); else CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); + + if (pjctx != NULL) + /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ + CPLReleaseMutex(hPROJMutex); } else if( nErrorCount == 20 ) { @@ -805,9 +977,14 @@ int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, err ); } + if (pjctx == NULL) + CPLReleaseMutex(hPROJMutex); return FALSE; } + if( !bIdentityTransform && pjctx == NULL ) + CPLReleaseMutex(hPROJMutex); + /* -------------------------------------------------------------------- */ /* Potentially transform back to degrees. */ /* -------------------------------------------------------------------- */ @@ -863,6 +1040,8 @@ int CPL_STDCALL OCTTransformEx( OGRCoordinateTransformationH hTransform, int *pabSuccess ) { + VALIDATE_POINTER1( hTransform, "OCTTransformEx", FALSE ); + return ((OGRCoordinateTransformation*) hTransform)-> TransformEx( nCount, x, y, z, pabSuccess ); } diff --git a/ogr/ogrdatasource.cpp b/ogr/ogrdatasource.cpp index ccf8b6a..f9be5a1 100644 --- a/ogr/ogrdatasource.cpp +++ b/ogr/ogrdatasource.cpp @@ -1,12 +1,13 @@ /****************************************************************************** - * $Id: ogrdatasource.cpp 16933 2009-05-03 19:49:41Z rouault $ + * $Id: ogrdatasource.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation - * Purpose: The generic portions of the OGRDataSource class. + * Purpose: The generic portions of the GDALDataset class. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,12 +30,8 @@ #include "ogrsf_frmts.h" #include "ogr_api.h" -#include "ogr_p.h" -#include "ogr_gensql.h" -#include "ogr_attrind.h" -#include "cpl_multiproc.h" -CPL_CVSID("$Id: ogrdatasource.cpp 16933 2009-05-03 19:49:41Z rouault $"); +CPL_CVSID("$Id: ogrdatasource.cpp 27384 2014-05-24 12:28:12Z rouault $"); /************************************************************************/ /* ~OGRDataSource() */ @@ -43,27 +40,6 @@ CPL_CVSID("$Id: ogrdatasource.cpp 16933 2009-05-03 19:49:41Z rouault $"); OGRDataSource::OGRDataSource() { - m_poStyleTable = NULL; - m_nRefCount = 0; - m_poDriver = NULL; - m_hMutex = NULL; -} - -/************************************************************************/ -/* ~OGRDataSource() */ -/************************************************************************/ - -OGRDataSource::~OGRDataSource() - -{ - if ( m_poStyleTable ) - { - delete m_poStyleTable; - m_poStyleTable = NULL; - } - - if( m_hMutex != NULL ) - CPLDestroyMutex( m_hMutex ); } /************************************************************************/ @@ -84,27 +60,7 @@ void OGR_DS_Destroy( OGRDataSourceH hDS ) { VALIDATE_POINTER0( hDS, "OGR_DS_Destroy" ); - delete (OGRDataSource *) hDS; -} - -/************************************************************************/ -/* Release() */ -/************************************************************************/ - -OGRErr OGRDataSource::Release() - -{ - return OGRSFDriverRegistrar::GetRegistrar()->ReleaseDataSource( this ); -} - -/************************************************************************/ -/* Reference() */ -/************************************************************************/ - -int OGRDataSource::Reference() - -{ - return ++m_nRefCount; + delete (GDALDataset *) hDS; } /************************************************************************/ @@ -116,17 +72,7 @@ int OGR_DS_Reference( OGRDataSourceH hDataSource ) { VALIDATE_POINTER1( hDataSource, "OGR_DS_Reference", 0 ); - return ((OGRDataSource *) hDataSource)->Reference(); -} - -/************************************************************************/ -/* Dereference() */ -/************************************************************************/ - -int OGRDataSource::Dereference() - -{ - return --m_nRefCount; + return ((GDALDataset *) hDataSource)->Reference(); } /************************************************************************/ @@ -138,17 +84,7 @@ int OGR_DS_Dereference( OGRDataSourceH hDataSource ) { VALIDATE_POINTER1( hDataSource, "OGR_DS_Dereference", 0 ); - return ((OGRDataSource *) hDataSource)->Dereference(); -} - -/************************************************************************/ -/* GetRefCount() */ -/************************************************************************/ - -int OGRDataSource::GetRefCount() const - -{ - return m_nRefCount; + return ((GDALDataset *) hDataSource)->Dereference(); } /************************************************************************/ @@ -160,25 +96,7 @@ int OGR_DS_GetRefCount( OGRDataSourceH hDataSource ) { VALIDATE_POINTER1( hDataSource, "OGR_DS_GetRefCount", 0 ); - return ((OGRDataSource *) hDataSource)->GetRefCount(); -} - -/************************************************************************/ -/* GetSummaryRefCount() */ -/************************************************************************/ - -int OGRDataSource::GetSummaryRefCount() const - -{ - CPLMutexHolderD( (void **) &m_hMutex ); - int nSummaryCount = m_nRefCount; - int iLayer; - OGRDataSource *poUseThis = (OGRDataSource *) this; - - for( iLayer=0; iLayer < poUseThis->GetLayerCount(); iLayer++ ) - nSummaryCount += poUseThis->GetLayer( iLayer )->GetRefCount(); - - return nSummaryCount; + return ((GDALDataset *) hDataSource)->GetRefCount(); } /************************************************************************/ @@ -190,28 +108,7 @@ int OGR_DS_GetSummaryRefCount( OGRDataSourceH hDataSource ) { VALIDATE_POINTER1( hDataSource, "OGR_DS_GetSummaryRefCount", 0 ); - return ((OGRDataSource *) hDataSource)->GetSummaryRefCount(); -} - -/************************************************************************/ -/* CreateLayer() */ -/************************************************************************/ - -OGRLayer *OGRDataSource::CreateLayer( const char * pszName, - OGRSpatialReference * poSpatialRef, - OGRwkbGeometryType eGType, - char **papszOptions ) - -{ - (void) eGType; - (void) poSpatialRef; - (void) pszName; - (void) papszOptions; - - CPLError( CE_Failure, CPLE_NotSupported, - "CreateLayer() not supported by this data source." ); - - return NULL; + return ((GDALDataset *) hDataSource)->GetSummaryRefCount(); } /************************************************************************/ @@ -232,95 +129,10 @@ OGRLayerH OGR_DS_CreateLayer( OGRDataSourceH hDS, CPLError ( CE_Failure, CPLE_ObjectNull, "Name was NULL in OGR_DS_CreateLayer"); return 0; } - return (OGRLayerH) ((OGRDataSource *)hDS)->CreateLayer( + return (OGRLayerH) ((GDALDataset *)hDS)->CreateLayer( pszName, (OGRSpatialReference *) hSpatialRef, eType, papszOptions ); } -/************************************************************************/ -/* CopyLayer() */ -/************************************************************************/ - -OGRLayer *OGRDataSource::CopyLayer( OGRLayer *poSrcLayer, - const char *pszNewName, - char **papszOptions ) - -{ - OGRFeatureDefn *poSrcDefn = poSrcLayer->GetLayerDefn(); - OGRLayer *poDstLayer = NULL; - -/* -------------------------------------------------------------------- */ -/* Create the layer. */ -/* -------------------------------------------------------------------- */ - if( !TestCapability( ODsCCreateLayer ) ) - { - CPLError( CE_Failure, CPLE_NotSupported, - "This datasource does not support creation of layers." ); - return NULL; - } - - CPLErrorReset(); - poDstLayer = CreateLayer( pszNewName, poSrcLayer->GetSpatialRef(), - poSrcDefn->GetGeomType(), papszOptions ); - - if( poDstLayer == NULL ) - return NULL; - -/* -------------------------------------------------------------------- */ -/* Add fields. Default to copy all field. */ -/* If only a subset of all fields requested, then output only */ -/* the selected fields, and in the order that they were */ -/* selected. */ -/* -------------------------------------------------------------------- */ - int iField; - - for( iField = 0; iField < poSrcDefn->GetFieldCount(); iField++ ) - poDstLayer->CreateField( poSrcDefn->GetFieldDefn(iField) ); - -/* -------------------------------------------------------------------- */ -/* Transfer features. */ -/* -------------------------------------------------------------------- */ - OGRFeature *poFeature; - - poSrcLayer->ResetReading(); - - while( TRUE ) - { - OGRFeature *poDstFeature = NULL; - - poFeature = poSrcLayer->GetNextFeature(); - - if( poFeature == NULL ) - break; - - CPLErrorReset(); - poDstFeature = OGRFeature::CreateFeature( poDstLayer->GetLayerDefn() ); - - if( poDstFeature->SetFrom( poFeature, TRUE ) != OGRERR_NONE ) - { - delete poFeature; - CPLError( CE_Failure, CPLE_AppDefined, - "Unable to translate feature %ld from layer %s.\n", - poFeature->GetFID(), poSrcDefn->GetName() ); - return poDstLayer; - } - - poDstFeature->SetFID( poFeature->GetFID() ); - - OGRFeature::DestroyFeature( poFeature ); - - CPLErrorReset(); - if( poDstLayer->CreateFeature( poDstFeature ) != OGRERR_NONE ) - { - OGRFeature::DestroyFeature( poDstFeature ); - return poDstLayer; - } - - OGRFeature::DestroyFeature( poDstFeature ); - } - - return poDstLayer; -} - /************************************************************************/ /* OGR_DS_CopyLayer() */ /************************************************************************/ @@ -332,26 +144,13 @@ OGRLayerH OGR_DS_CopyLayer( OGRDataSourceH hDS, { VALIDATE_POINTER1( hDS, "OGR_DS_CopyLayer", NULL ); VALIDATE_POINTER1( hSrcLayer, "OGR_DS_CopyLayer", NULL ); + VALIDATE_POINTER1( pszNewName, "OGR_DS_CopyLayer", NULL ); return (OGRLayerH) - ((OGRDataSource *) hDS)->CopyLayer( (OGRLayer *) hSrcLayer, + ((GDALDataset *) hDS)->CopyLayer( (OGRLayer *) hSrcLayer, pszNewName, papszOptions ); } -/************************************************************************/ -/* DeleteLayer() */ -/************************************************************************/ - -OGRErr OGRDataSource::DeleteLayer( int iLayer ) - -{ - (void) iLayer; - CPLError( CE_Failure, CPLE_NotSupported, - "DeleteLayer() not supported by this data source." ); - - return OGRERR_UNSUPPORTED_OPERATION; -} - /************************************************************************/ /* OGR_DS_DeleteLayer() */ /************************************************************************/ @@ -361,42 +160,7 @@ OGRErr OGR_DS_DeleteLayer( OGRDataSourceH hDS, int iLayer ) { VALIDATE_POINTER1( hDS, "OGR_DS_DeleteLayer", OGRERR_INVALID_HANDLE ); - return ((OGRDataSource *) hDS)->DeleteLayer( iLayer ); -} - -/************************************************************************/ -/* GetLayerByName() */ -/************************************************************************/ - -OGRLayer *OGRDataSource::GetLayerByName( const char *pszName ) - -{ - CPLMutexHolderD( &m_hMutex ); - - if ( ! pszName ) - return NULL; - - int i; - - /* first a case sensitive check */ - for( i = 0; i < GetLayerCount(); i++ ) - { - OGRLayer *poLayer = GetLayer(i); - - if( strcmp( pszName, poLayer->GetLayerDefn()->GetName() ) == 0 ) - return poLayer; - } - - /* then case insensitive */ - for( i = 0; i < GetLayerCount(); i++ ) - { - OGRLayer *poLayer = GetLayer(i); - - if( EQUAL( pszName, poLayer->GetLayerDefn()->GetName() ) ) - return poLayer; - } - - return NULL; + return ((GDALDataset *) hDS)->DeleteLayer( iLayer ); } /************************************************************************/ @@ -408,451 +172,7 @@ OGRLayerH OGR_DS_GetLayerByName( OGRDataSourceH hDS, const char *pszName ) { VALIDATE_POINTER1( hDS, "OGR_DS_GetLayerByName", NULL ); - return (OGRLayerH) ((OGRDataSource *) hDS)->GetLayerByName( pszName ); -} - -/************************************************************************/ -/* ProcessSQLCreateIndex() */ -/* */ -/* The correct syntax for creating an index in our dialect of */ -/* SQL is: */ -/* */ -/* CREATE INDEX ON USING */ -/************************************************************************/ - -OGRErr OGRDataSource::ProcessSQLCreateIndex( const char *pszSQLCommand ) - -{ - char **papszTokens = CSLTokenizeString( pszSQLCommand ); - -/* -------------------------------------------------------------------- */ -/* Do some general syntax checking. */ -/* -------------------------------------------------------------------- */ - if( CSLCount(papszTokens) != 6 - || !EQUAL(papszTokens[0],"CREATE") - || !EQUAL(papszTokens[1],"INDEX") - || !EQUAL(papszTokens[2],"ON") - || !EQUAL(papszTokens[4],"USING") ) - { - CSLDestroy( papszTokens ); - CPLError( CE_Failure, CPLE_AppDefined, - "Syntax error in CREATE INDEX command.\n" - "Was '%s'\n" - "Should be of form 'CREATE INDEX ON USING '", - pszSQLCommand ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* Find the named layer. */ -/* -------------------------------------------------------------------- */ - int i; - OGRLayer *poLayer = NULL; - - { - CPLMutexHolderD( &m_hMutex ); - - for( i = 0; i < GetLayerCount(); i++ ) - { - poLayer = GetLayer(i); - - if( EQUAL(poLayer->GetLayerDefn()->GetName(),papszTokens[3]) ) - break; - } - - if( i >= GetLayerCount() ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "CREATE INDEX ON failed, no such layer as `%s'.", - papszTokens[3] ); - CSLDestroy( papszTokens ); - return OGRERR_FAILURE; - } - } - -/* -------------------------------------------------------------------- */ -/* Does this layer even support attribute indexes? */ -/* -------------------------------------------------------------------- */ - if( poLayer->GetIndex() == NULL ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "CREATE INDEX ON not supported by this driver." ); - CSLDestroy( papszTokens ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* Find the named field. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) - { - if( EQUAL(papszTokens[5], - poLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()) ) - break; - } - - CSLDestroy( papszTokens ); - - if( i >= poLayer->GetLayerDefn()->GetFieldCount() ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "`%s' failed, field not found.", - pszSQLCommand ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* Attempt to create the index. */ -/* -------------------------------------------------------------------- */ - OGRErr eErr; - - eErr = poLayer->GetIndex()->CreateIndex( i ); - if( eErr == OGRERR_NONE ) - eErr = poLayer->GetIndex()->IndexAllFeatures( i ); - - return eErr; -} - -/************************************************************************/ -/* ProcessSQLDropIndex() */ -/* */ -/* The correct syntax for droping one or more indexes in */ -/* the OGR SQL dialect is: */ -/* */ -/* DROP INDEX ON [USING ] */ -/************************************************************************/ - -OGRErr OGRDataSource::ProcessSQLDropIndex( const char *pszSQLCommand ) - -{ - char **papszTokens = CSLTokenizeString( pszSQLCommand ); - -/* -------------------------------------------------------------------- */ -/* Do some general syntax checking. */ -/* -------------------------------------------------------------------- */ - if( (CSLCount(papszTokens) != 4 && CSLCount(papszTokens) != 6) - || !EQUAL(papszTokens[0],"DROP") - || !EQUAL(papszTokens[1],"INDEX") - || !EQUAL(papszTokens[2],"ON") - || (CSLCount(papszTokens) == 6 && !EQUAL(papszTokens[4],"USING")) ) - { - CSLDestroy( papszTokens ); - CPLError( CE_Failure, CPLE_AppDefined, - "Syntax error in DROP INDEX command.\n" - "Was '%s'\n" - "Should be of form 'DROP INDEX ON
    [USING ]'", - pszSQLCommand ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* Find the named layer. */ -/* -------------------------------------------------------------------- */ - int i; - OGRLayer *poLayer=NULL; - - { - CPLMutexHolderD( &m_hMutex ); - - for( i = 0; i < GetLayerCount(); i++ ) - { - poLayer = GetLayer(i); - - if( EQUAL(poLayer->GetLayerDefn()->GetName(),papszTokens[3]) ) - break; - } - - if( i >= GetLayerCount() ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "CREATE INDEX ON failed, no such layer as `%s'.", - papszTokens[3] ); - CSLDestroy( papszTokens ); - return OGRERR_FAILURE; - } - } - -/* -------------------------------------------------------------------- */ -/* Does this layer even support attribute indexes? */ -/* -------------------------------------------------------------------- */ - if( poLayer->GetIndex() == NULL ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "Indexes not supported by this driver." ); - CSLDestroy( papszTokens ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* If we weren't given a field name, drop all indexes. */ -/* -------------------------------------------------------------------- */ - OGRErr eErr; - - if( CSLCount(papszTokens) == 4 ) - { - for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) - { - OGRAttrIndex *poAttrIndex; - - poAttrIndex = poLayer->GetIndex()->GetFieldIndex(i); - if( poAttrIndex != NULL ) - { - eErr = poLayer->GetIndex()->DropIndex( i ); - if( eErr != OGRERR_NONE ) - return eErr; - } - } - - CSLDestroy(papszTokens); - return OGRERR_NONE; - } - -/* -------------------------------------------------------------------- */ -/* Find the named field. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) - { - if( EQUAL(papszTokens[5], - poLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()) ) - break; - } - - CSLDestroy( papszTokens ); - - if( i >= poLayer->GetLayerDefn()->GetFieldCount() ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "`%s' failed, field not found.", - pszSQLCommand ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* Attempt to drop the index. */ -/* -------------------------------------------------------------------- */ - eErr = poLayer->GetIndex()->DropIndex( i ); - - return eErr; -} - -/************************************************************************/ -/* ExecuteSQL() */ -/************************************************************************/ - -OGRLayer * OGRDataSource::ExecuteSQL( const char *pszStatement, - OGRGeometry *poSpatialFilter, - const char *pszDialect ) - -{ - const char *pszError; - swq_select *psSelectInfo = NULL; - - (void) pszDialect; - - swq_field_list sFieldList; - int nFIDIndex = 0; - OGRGenSQLResultsLayer *poResults = NULL; - - memset( &sFieldList, 0, sizeof(sFieldList) ); - -/* -------------------------------------------------------------------- */ -/* Handle CREATE INDEX statements specially. */ -/* -------------------------------------------------------------------- */ - if( EQUALN(pszStatement,"CREATE INDEX",12) ) - { - ProcessSQLCreateIndex( pszStatement ); - return NULL; - } - -/* -------------------------------------------------------------------- */ -/* Handle DROP INDEX statements specially. */ -/* -------------------------------------------------------------------- */ - if( EQUALN(pszStatement,"DROP INDEX",10) ) - { - ProcessSQLDropIndex( pszStatement ); - return NULL; - } - -/* -------------------------------------------------------------------- */ -/* Preparse the SQL statement. */ -/* -------------------------------------------------------------------- */ - pszError = swq_select_preparse( pszStatement, &psSelectInfo ); - if( pszError != NULL ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "SQL: %s", pszError ); - return NULL; - } - -/* -------------------------------------------------------------------- */ -/* Validate that all the source tables are recognised, count */ -/* fields. */ -/* -------------------------------------------------------------------- */ - int nFieldCount = 0, iTable, iField; - int iEDS; - int nExtraDSCount = 0; - OGRDataSource** papoExtraDS = NULL; - OGRSFDriverRegistrar *poReg=OGRSFDriverRegistrar::GetRegistrar(); - - for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) - { - swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; - OGRLayer *poSrcLayer; - OGRDataSource *poTableDS = this; - - if( psTableDef->data_source != NULL ) - { - poTableDS = (OGRDataSource *) - OGROpenShared( psTableDef->data_source, FALSE, NULL ); - if( poTableDS == NULL ) - { - if( strlen(CPLGetLastErrorMsg()) == 0 ) - CPLError( CE_Failure, CPLE_AppDefined, - "Unable to open secondary datasource\n" - "`%s' required by JOIN.", - psTableDef->data_source ); - - swq_select_free( psSelectInfo ); - goto end; - } - - /* Keep in an array to release at the end of this function */ - papoExtraDS = (OGRDataSource** )CPLRealloc(papoExtraDS, - sizeof(OGRDataSource*) * (nExtraDSCount + 1)); - papoExtraDS[nExtraDSCount++] = poTableDS; - } - - poSrcLayer = poTableDS->GetLayerByName( psTableDef->table_name ); - - if( poSrcLayer == NULL ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "SELECT from table %s failed, no such table/featureclass.", - psTableDef->table_name ); - swq_select_free( psSelectInfo ); - goto end; - } - - nFieldCount += poSrcLayer->GetLayerDefn()->GetFieldCount(); - } - -/* -------------------------------------------------------------------- */ -/* Build the field list for all indicated tables. */ -/* -------------------------------------------------------------------- */ - - sFieldList.table_count = psSelectInfo->table_count; - sFieldList.table_defs = psSelectInfo->table_defs; - - sFieldList.count = 0; - sFieldList.names = (char **) CPLMalloc( sizeof(char *) * (nFieldCount+SPECIAL_FIELD_COUNT) ); - sFieldList.types = (swq_field_type *) - CPLMalloc( sizeof(swq_field_type) * (nFieldCount+SPECIAL_FIELD_COUNT) ); - sFieldList.table_ids = (int *) - CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); - sFieldList.ids = (int *) - CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); - - for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) - { - swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; - OGRDataSource *poTableDS = this; - OGRLayer *poSrcLayer; - - if( psTableDef->data_source != NULL ) - { - poTableDS = (OGRDataSource *) - OGROpenShared( psTableDef->data_source, FALSE, NULL ); - CPLAssert( poTableDS != NULL ); - poTableDS->Dereference(); - } - - poSrcLayer = poTableDS->GetLayerByName( psTableDef->table_name ); - - for( iField = 0; - iField < poSrcLayer->GetLayerDefn()->GetFieldCount(); - iField++ ) - { - OGRFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetFieldDefn(iField); - int iOutField = sFieldList.count++; - sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef(); - if( poFDefn->GetType() == OFTInteger ) - sFieldList.types[iOutField] = SWQ_INTEGER; - else if( poFDefn->GetType() == OFTReal ) - sFieldList.types[iOutField] = SWQ_FLOAT; - else if( poFDefn->GetType() == OFTString ) - sFieldList.types[iOutField] = SWQ_STRING; - else - sFieldList.types[iOutField] = SWQ_OTHER; - - sFieldList.table_ids[iOutField] = iTable; - sFieldList.ids[iOutField] = iField; - } - - if( iTable == 0 ) - nFIDIndex = poSrcLayer->GetLayerDefn()->GetFieldCount(); - } - -/* -------------------------------------------------------------------- */ -/* Expand '*' in 'SELECT *' now before we add the pseudo fields */ -/* -------------------------------------------------------------------- */ - pszError = - swq_select_expand_wildcard( psSelectInfo, &sFieldList ); - - if( pszError != NULL ) - { - swq_select_free( psSelectInfo ); - CPLError( CE_Failure, CPLE_AppDefined, - "SQL: %s", pszError ); - goto end; - } - - for (iField = 0; iField < SPECIAL_FIELD_COUNT; iField++) - { - sFieldList.names[sFieldList.count] = (char*) SpecialFieldNames[iField]; - sFieldList.types[sFieldList.count] = SpecialFieldTypes[iField]; - sFieldList.table_ids[sFieldList.count] = 0; - sFieldList.ids[sFieldList.count] = nFIDIndex + iField; - sFieldList.count++; - } - -/* -------------------------------------------------------------------- */ -/* Finish the parse operation. */ -/* -------------------------------------------------------------------- */ - - pszError = swq_select_parse( psSelectInfo, &sFieldList, 0 ); - - if( pszError != NULL ) - { - swq_select_free( psSelectInfo ); - CPLError( CE_Failure, CPLE_AppDefined, - "SQL: %s", pszError ); - goto end; - } - -/* -------------------------------------------------------------------- */ -/* Everything seems OK, try to instantiate a results layer. */ -/* -------------------------------------------------------------------- */ - - poResults = new OGRGenSQLResultsLayer( this, psSelectInfo, - poSpatialFilter ); - - // Eventually, we should keep track of layers to cleanup. - -end: - CPLFree( sFieldList.names ); - CPLFree( sFieldList.types ); - CPLFree( sFieldList.table_ids ); - CPLFree( sFieldList.ids ); - - /* Release the datasets we have opened with OGROpenShared() */ - /* It is safe to do that as the 'new OGRGenSQLResultsLayer' itself */ - /* has taken a reference on them, which it will release in its */ - /* destructor */ - for(iEDS = 0; iEDS < nExtraDSCount; iEDS++) - poReg->ReleaseDataSource( papoExtraDS[iEDS] ); - CPLFree(papoExtraDS); - - return poResults; + return (OGRLayerH) ((GDALDataset *) hDS)->GetLayerByName( pszName ); } /************************************************************************/ @@ -868,21 +188,11 @@ OGRLayerH OGR_DS_ExecuteSQL( OGRDataSourceH hDS, VALIDATE_POINTER1( hDS, "OGR_DS_ExecuteSQL", NULL ); return (OGRLayerH) - ((OGRDataSource *)hDS)->ExecuteSQL( pszStatement, + ((GDALDataset *)hDS)->ExecuteSQL( pszStatement, (OGRGeometry *) hSpatialFilter, pszDialect ); } -/************************************************************************/ -/* ReleaseResultSet() */ -/************************************************************************/ - -void OGRDataSource::ReleaseResultSet( OGRLayer * poResultsSet ) - -{ - delete poResultsSet; -} - /************************************************************************/ /* OGR_DS_ReleaseResultSet() */ /************************************************************************/ @@ -892,7 +202,7 @@ void OGR_DS_ReleaseResultSet( OGRDataSourceH hDS, OGRLayerH hLayer ) { VALIDATE_POINTER0( hDS, "OGR_DS_ReleaseResultSet" ); - ((OGRDataSource *) hDS)->ReleaseResultSet( (OGRLayer *) hLayer ); + ((GDALDataset *) hDS)->ReleaseResultSet( (OGRLayer *) hLayer ); } /************************************************************************/ @@ -905,7 +215,7 @@ int OGR_DS_TestCapability( OGRDataSourceH hDS, const char *pszCap ) VALIDATE_POINTER1( hDS, "OGR_DS_TestCapability", 0 ); VALIDATE_POINTER1( pszCap, "OGR_DS_TestCapability", 0 ); - return ((OGRDataSource *) hDS)->TestCapability( pszCap ); + return ((GDALDataset *) hDS)->TestCapability( pszCap ); } /************************************************************************/ @@ -917,7 +227,7 @@ int OGR_DS_GetLayerCount( OGRDataSourceH hDS ) { VALIDATE_POINTER1( hDS, "OGR_DS_GetLayerCount", 0 ); - return ((OGRDataSource *)hDS)->GetLayerCount(); + return ((GDALDataset *)hDS)->GetLayerCount(); } /************************************************************************/ @@ -929,7 +239,7 @@ OGRLayerH OGR_DS_GetLayer( OGRDataSourceH hDS, int iLayer ) { VALIDATE_POINTER1( hDS, "OGR_DS_GetLayer", NULL ); - return (OGRLayerH) ((OGRDataSource*)hDS)->GetLayer( iLayer ); + return (OGRLayerH) ((GDALDataset*)hDS)->GetLayer( iLayer ); } /************************************************************************/ @@ -941,33 +251,7 @@ const char *OGR_DS_GetName( OGRDataSourceH hDS ) { VALIDATE_POINTER1( hDS, "OGR_DS_GetName", NULL ); - return ((OGRDataSource*)hDS)->GetName(); -} - -/************************************************************************/ -/* SyncToDisk() */ -/************************************************************************/ - -OGRErr OGRDataSource::SyncToDisk() - -{ - CPLMutexHolderD( &m_hMutex ); - int i; - OGRErr eErr; - - for( i = 0; i < GetLayerCount(); i++ ) - { - OGRLayer *poLayer = GetLayer(i); - - if( poLayer ) - { - eErr = poLayer->SyncToDisk(); - if( eErr != OGRERR_NONE ) - return eErr; - } - } - - return OGRERR_NONE; + return ((GDALDataset*)hDS)->GetDescription(); } /************************************************************************/ @@ -979,17 +263,11 @@ OGRErr OGR_DS_SyncToDisk( OGRDataSourceH hDS ) { VALIDATE_POINTER1( hDS, "OGR_DS_SyncToDisk", OGRERR_INVALID_HANDLE ); - return ((OGRDataSource *) hDS)->SyncToDisk(); -} - -/************************************************************************/ -/* GetDriver() */ -/************************************************************************/ - -OGRSFDriver *OGRDataSource::GetDriver() const - -{ - return m_poDriver; + ((GDALDataset *) hDS)->FlushCache(); + if( CPLGetLastErrorType() != 0 ) + return OGRERR_FAILURE; + else + return OGRERR_NONE; } /************************************************************************/ @@ -1004,16 +282,6 @@ OGRSFDriverH OGR_DS_GetDriver( OGRDataSourceH hDS ) return (OGRSFDriverH) ((OGRDataSource *) hDS)->GetDriver(); } -/************************************************************************/ -/* SetDriver() */ -/************************************************************************/ - -void OGRDataSource::SetDriver( OGRSFDriver *poDriver ) - -{ - m_poDriver = poDriver; -} - /************************************************************************/ /* OGR_DS_GetStyleTable() */ /************************************************************************/ @@ -1023,7 +291,7 @@ OGRStyleTableH OGR_DS_GetStyleTable( OGRDataSourceH hDS ) { VALIDATE_POINTER1( hDS, "OGR_DS_GetStyleTable", NULL ); - return (OGRStyleTableH) ((OGRDataSource *) hDS)->GetStyleTable( ); + return (OGRStyleTableH) ((GDALDataset *) hDS)->GetStyleTable( ); } /************************************************************************/ @@ -1036,7 +304,7 @@ void OGR_DS_SetStyleTableDirectly( OGRDataSourceH hDS, { VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTableDirectly" ); - ((OGRDataSource *) hDS)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); + ((GDALDataset *) hDS)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); } /************************************************************************/ @@ -1049,5 +317,5 @@ void OGR_DS_SetStyleTable( OGRDataSourceH hDS, OGRStyleTableH hStyleTable ) VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTable" ); VALIDATE_POINTER0( hStyleTable, "OGR_DS_SetStyleTable" ); - ((OGRDataSource *) hDS)->SetStyleTable( (OGRStyleTable *) hStyleTable); + ((GDALDataset *) hDS)->SetStyleTable( (OGRStyleTable *) hStyleTable); } diff --git a/ogr/ogrfeature.cpp b/ogr/ogrfeature.cpp index 0582311..0b75e0e 100644 --- a/ogr/ogrfeature.cpp +++ b/ogr/ogrfeature.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrfeature.cpp 18479 2010-01-08 22:35:20Z rouault $ + * $Id: ogrfeature.cpp 27390 2014-05-24 13:51:47Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRFeature class implementation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,8 +31,9 @@ #include "ogr_feature.h" #include "ogr_api.h" #include "ogr_p.h" +#include -CPL_CVSID("$Id: ogrfeature.cpp 18479 2010-01-08 22:35:20Z rouault $"); +CPL_CVSID("$Id: ogrfeature.cpp 27390 2014-05-24 13:51:47Z rouault $"); /************************************************************************/ /* OGRFeature() */ @@ -61,14 +63,14 @@ OGRFeature::OGRFeature( OGRFeatureDefn * poDefnIn ) poDefn = poDefnIn; nFID = OGRNullFID; - - poGeometry = NULL; - // we should likely be initializing from the defaults, but this will - // usually be a waste. - pauFields = (OGRField *) CPLCalloc( poDefn->GetFieldCount(), + // Allocate array of fields and initialize them to the unset special value + pauFields = (OGRField *) CPLMalloc( poDefn->GetFieldCount() * sizeof(OGRField) ); + papoGeometries = (OGRGeometry **) CPLCalloc( poDefn->GetGeomFieldCount(), + sizeof(OGRGeometry*) ); + for( int i = 0; i < poDefn->GetFieldCount(); i++ ) { pauFields[i].Set.nMarker1 = OGRUnsetMarker; @@ -111,10 +113,10 @@ OGRFeatureH OGR_F_Create( OGRFeatureDefnH hDefn ) OGRFeature::~OGRFeature() { - if( poGeometry != NULL ) - delete poGeometry; + int i; - for( int i = 0; i < poDefn->GetFieldCount(); i++ ) + int nFieldcount = poDefn->GetFieldCount(); + for( i = 0; i < nFieldcount; i++ ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn(i); @@ -148,9 +150,16 @@ OGRFeature::~OGRFeature() } } + int nGeomFieldCount = poDefn->GetGeomFieldCount(); + for( i = 0; i < nGeomFieldCount; i++ ) + { + delete papoGeometries[i]; + } + poDefn->Release(); CPLFree( pauFields ); + CPLFree( papoGeometries ); CPLFree(m_pszStyleString); CPLFree(m_pszTmpFieldValue); } @@ -289,12 +298,10 @@ OGRFeatureDefnH OGR_F_GetDefnRef( OGRFeatureH hFeat ) OGRErr OGRFeature::SetGeometryDirectly( OGRGeometry * poGeomIn ) { - delete poGeometry; - poGeometry = poGeomIn; - - // I should be verifying that the geometry matches the defn's type. - - return OGRERR_NONE; + if( GetGeomFieldCount() > 0 ) + return SetGeomFieldDirectly(0, poGeomIn); + else + return OGRERR_FAILURE; } /************************************************************************/ @@ -352,16 +359,10 @@ OGRErr OGR_F_SetGeometryDirectly( OGRFeatureH hFeat, OGRGeometryH hGeom ) OGRErr OGRFeature::SetGeometry( OGRGeometry * poGeomIn ) { - delete poGeometry; - - if( poGeomIn != NULL ) - poGeometry = poGeomIn->clone(); + if( GetGeomFieldCount() > 0 ) + return SetGeomField(0, poGeomIn); else - poGeometry = NULL; - - // I should be verifying that the geometry matches the defn's type. - - return OGRERR_NONE; + return OGRERR_FAILURE; } /************************************************************************/ @@ -413,9 +414,52 @@ OGRErr OGR_F_SetGeometry( OGRFeatureH hFeat, OGRGeometryH hGeom ) OGRGeometry *OGRFeature::StealGeometry() { - OGRGeometry *poReturn = poGeometry; - poGeometry = NULL; - return poReturn; + if( GetGeomFieldCount() > 0 ) + { + OGRGeometry *poReturn = papoGeometries[0]; + papoGeometries[0] = NULL; + return poReturn; + } + else + return NULL; +} + +OGRGeometry *OGRFeature::StealGeometry(int iGeomField) + +{ + if( iGeomField >= 0 && iGeomField < GetGeomFieldCount() ) + { + OGRGeometry *poReturn = papoGeometries[iGeomField]; + papoGeometries[iGeomField] = NULL; + return poReturn; + } + else + return NULL; +} + +/************************************************************************/ +/* OGR_F_StealGeometry() */ +/************************************************************************/ + +/** + * \brief Take away ownership of geometry. + * + * Fetch the geometry from this feature, and clear the reference to the + * geometry on the feature. This is a mechanism for the application to + * take over ownship of the geometry from the feature without copying. + * Sort of an inverse to OGR_FSetGeometryDirectly(). + * + * After this call the OGRFeature will have a NULL geometry. + * + * @return the pointer to the geometry. + */ + +OGRGeometryH OGR_F_StealGeometry( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_StealGeometry", NULL ); + + return (OGRGeometryH) ((OGRFeature *) hFeat)->StealGeometry(); } /************************************************************************/ @@ -428,10 +472,21 @@ OGRGeometry *OGRFeature::StealGeometry() * \brief Fetch pointer to feature geometry. * * This method is the same as the C function OGR_F_GetGeometryRef(). + * + * Starting with GDAL 1.11, this is equivalent to calling + * OGRFeature::GetGeomFieldRef(0). * * @return pointer to internal feature geometry. This object should * not be modified. */ +OGRGeometry *OGRFeature::GetGeometryRef() + +{ + if( GetGeomFieldCount() > 0 ) + return GetGeomFieldRef(0); + else + return NULL; +} /************************************************************************/ /* OGR_F_GetGeometryRef() */ @@ -455,6 +510,228 @@ OGRGeometryH OGR_F_GetGeometryRef( OGRFeatureH hFeat ) return (OGRGeometryH) ((OGRFeature *) hFeat)->GetGeometryRef(); } + +/************************************************************************/ +/* GetGeomFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch pointer to feature geometry. + * + * This method is the same as the C function OGR_F_GetGeomFieldRef(). + * + * @param iField geometry field to get. + * + * @return pointer to internal feature geometry. This object should + * not be modified. + * + * @since GDAL 1.11 + */ +OGRGeometry *OGRFeature::GetGeomFieldRef(int iField) + +{ + if( iField < 0 || iField >= GetGeomFieldCount() ) + return NULL; + else + return papoGeometries[iField]; +} + +/************************************************************************/ +/* GetGeomFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch pointer to feature geometry. + * + * @param pszFName name of geometry field to get. + * + * @return pointer to internal feature geometry. This object should + * not be modified. + * + * @since GDAL 1.11 + */ +OGRGeometry *OGRFeature::GetGeomFieldRef(const char* pszFName) + +{ + int iField = GetGeomFieldIndex(pszFName); + if( iField < 0 ) + return NULL; + else + return papoGeometries[iField]; +} + +/************************************************************************/ +/* OGR_F_GetGeomFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch an handle to feature geometry. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldRef(). + * + * @param hFeat handle to the feature to get geometry from. + * @param iField geometry field to get. + * @return an handle to internal feature geometry. This object should + * not be modified. + * + * @since GDAL 1.11 + */ + +OGRGeometryH OGR_F_GetGeomFieldRef( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldRef", NULL ); + + return (OGRGeometryH) ((OGRFeature *) hFeat)->GetGeomFieldRef(iField); +} + +/************************************************************************/ +/* SetGeomFieldDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This method updates the features geometry, and operate exactly as + * SetGeomField(), except that this method assumes ownership of the + * passed geometry. + * + * This method is the same as the C function OGR_F_SetGeomFieldDirectly(). + * + * @param iField geometry field to set. + * @param poGeomIn new geometry to apply to feature. Passing NULL value here + * is correct and it will result in deallocation of currently assigned geometry + * without assigning new one. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is invalid, + * or OGR_UNSUPPORTED_GEOMETRY_TYPE if the geometry type is illegal for the + * OGRFeatureDefn (checking not yet implemented). + * + * @since GDAL 1.11 + */ + +OGRErr OGRFeature::SetGeomFieldDirectly( int iField, OGRGeometry * poGeomIn ) + +{ + if( iField < 0 || iField >= GetGeomFieldCount() ) + return OGRERR_FAILURE; + + delete papoGeometries[iField]; + papoGeometries[iField] = poGeomIn; + + // I should be verifying that the geometry matches the defn's type. + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetGeomFieldDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This function updates the features geometry, and operate exactly as + * SetGeomField(), except that this function assumes ownership of the + * passed geometry. + * + * This function is the same as the C++ method + * OGRFeature::SetGeomFieldDirectly. + * + * @param hFeat handle to the feature on which to apply the geometry. + * @param iField geometry field to set. + * @param hGeom handle to the new geometry to apply to feature. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is invalid, + * or OGR_UNSUPPORTED_GEOMETRY_TYPE if the geometry type is illegal for the + * OGRFeatureDefn (checking not yet implemented). + * + * @since GDAL 1.11 + */ + +OGRErr OGR_F_SetGeomFieldDirectly( OGRFeatureH hFeat, int iField, + OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetGeomFieldDirectly", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetGeomFieldDirectly(iField, + (OGRGeometry *) hGeom); +} + +/************************************************************************/ +/* SetGeomField() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This method updates the features geometry, and operate exactly as + * SetGeomFieldDirectly(), except that this method does not assume ownership + * of the passed geometry, but instead makes a copy of it. + * + * This method is the same as the C function OGR_F_SetGeomField(). + * + * @param iField geometry field to set. + * @param poGeomIn new geometry to apply to feature. Passing NULL value here + * is correct and it will result in deallocation of currently assigned geometry + * without assigning new one. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is invalid, + * or OGR_UNSUPPORTED_GEOMETRY_TYPE if the geometry type is illegal for the + * OGRFeatureDefn (checking not yet implemented). + * + * @since GDAL 1.11 + */ + +OGRErr OGRFeature::SetGeomField( int iField, OGRGeometry * poGeomIn ) + +{ + if( iField < 0 || iField >= GetGeomFieldCount() ) + return OGRERR_FAILURE; + + delete papoGeometries[iField]; + + if( poGeomIn != NULL ) + papoGeometries[iField] = poGeomIn->clone(); + else + papoGeometries[iField] = NULL; + + // I should be verifying that the geometry matches the defn's type. + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetGeomField() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This function updates the features geometry, and operate exactly as + * SetGeometryDirectly(), except that this function does not assume ownership + * of the passed geometry, but instead makes a copy of it. + * + * This function is the same as the C++ OGRFeature::SetGeomField(). + * + * @param hFeat handle to the feature on which new geometry is applied to. + * @param iField geometry field to set. + * @param hGeom handle to the new geometry to apply to feature. + * + * @return OGRERR_NONE if successful, or OGR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the OGRFeatureDefn (checking not yet + * implemented). + */ + +OGRErr OGR_F_SetGeomField( OGRFeatureH hFeat, int iField, OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetGeomField", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetGeomField(iField, (OGRGeometry *) hGeom); +} + /************************************************************************/ /* Clone() */ /************************************************************************/ @@ -473,14 +750,17 @@ OGRGeometryH OGR_F_GetGeometryRef( OGRFeatureH hFeat ) OGRFeature *OGRFeature::Clone() { + int i; OGRFeature *poNew = new OGRFeature( poDefn ); - poNew->SetGeometry( poGeometry ); - - for( int i = 0; i < poDefn->GetFieldCount(); i++ ) + for( i = 0; i < poDefn->GetFieldCount(); i++ ) { poNew->SetField( i, pauFields + i ); } + for( i = 0; i < poDefn->GetGeomFieldCount(); i++ ) + { + poNew->SetGeomField( i, papoGeometries[i] ); + } if( GetStyleString() != NULL ) poNew->SetStyleString(GetStyleString()); @@ -637,6 +917,143 @@ int OGR_F_GetFieldIndex( OGRFeatureH hFeat, const char *pszName ) return ((OGRFeature *) hFeat)->GetFieldIndex( pszName ); } + +/************************************************************************/ +/* GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::GetGeomFieldCount(); + * + * \brief Fetch number of geometry fields on this feature. + * This will always be the same + * as the geometry field count for the OGRFeatureDefn. + * + * This method is the same as the C function OGR_F_GetGeomFieldCount(). + * + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_F_GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \brief Fetch number of geometry fields on this feature + * This will always be the same + * as the geometry field count for the OGRFeatureDefn. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldCount(). + * + * @param hFeat handle to the feature to get the geometry fields count from. + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ + +int OGR_F_GetGeomFieldCount( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldCount", 0 ); + + return ((OGRFeature *) hFeat)->GetGeomFieldCount(); +} + +/************************************************************************/ +/* GetGeomFieldDefnRef() */ +/************************************************************************/ + +/** + * \fn OGRGeomFieldDefn *OGRFeature::GetGeomFieldDefnRef( int iGeomField ); + * + * \brief Fetch definition for this geometry field. + * + * This method is the same as the C function OGR_F_GetGeomFieldDefnRef(). + * + * @param iGeomField the field to fetch, from 0 to GetGeomFieldCount()-1. + * + * @return the field definition (from the OGRFeatureDefn). This is an + * internal reference, and should not be deleted or modified. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_F_GetGeomFieldDefnRef() */ +/************************************************************************/ + +/** + * \brief Fetch definition for this geometry field. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldDefnRef(). + * + * @param hFeat handle to the feature on which the field is found. + * @param i the field to fetch, from 0 to GetGeomFieldCount()-1. + * + * @return an handle to the field definition (from the OGRFeatureDefn). + * This is an internal reference, and should not be deleted or modified. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefnH OGR_F_GetGeomFieldDefnRef( OGRFeatureH hFeat, int i ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldDefnRef", NULL ); + + return (OGRGeomFieldDefnH) ((OGRFeature *) hFeat)->GetGeomFieldDefnRef(i); +} + +/************************************************************************/ +/* GetGeomFieldIndex() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::GetGeomFieldIndex( const char * pszName ); + * + * \brief Fetch the geometry field index given geometry field name. + * + * This is a cover for the OGRFeatureDefn::GetGeomFieldIndex() method. + * + * This method is the same as the C function OGR_F_GetGeomFieldIndex(). + * + * @param pszName the name of the geometry field to search for. + * + * @return the geometry field index, or -1 if no matching geometry field is found. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_F_GetGeomFieldIndex() */ +/************************************************************************/ + +/** + * \brief Fetch the geometry field index given geometry field name. + * + * This is a cover for the OGRFeatureDefn::GetGeomFieldIndex() method. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldIndex(). + * + * @param hFeat handle to the feature on which the geometry field is found. + * @param pszName the name of the geometry field to search for. + * + * @return the geometry field index, or -1 if no matching geometry field is found. + * + * @since GDAL 1.11 + */ + +int OGR_F_GetGeomFieldIndex( OGRFeatureH hFeat, const char *pszName ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldIndex", 0 ); + + return ((OGRFeature *) hFeat)->GetGeomFieldIndex( pszName ); +} + + /************************************************************************/ /* IsFieldSet() */ /************************************************************************/ @@ -653,6 +1070,42 @@ int OGR_F_GetFieldIndex( OGRFeatureH hFeat, const char *pszName ) * @return TRUE if the field has been set, otherwise false. */ +int OGRFeature::IsFieldSet( int iField ) + +{ + int iSpecialField = iField - poDefn->GetFieldCount(); + if (iSpecialField >= 0) + { + // special field value accessors + switch (iSpecialField) + { + case SPF_FID: + return ((OGRFeature *)this)->GetFID() != OGRNullFID; + + case SPF_OGR_GEOM_WKT: + case SPF_OGR_GEOMETRY: + return GetGeomFieldCount() > 0 && papoGeometries[0] != NULL; + + case SPF_OGR_STYLE: + return ((OGRFeature *)this)->GetStyleString() != NULL; + + case SPF_OGR_GEOM_AREA: + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return FALSE; + + return OGR_G_Area((OGRGeometryH)papoGeometries[0]) != 0.0; + + default: + return FALSE; + } + } + else + { + return pauFields[iField].Set.nMarker1 != OGRUnsetMarker + || pauFields[iField].Set.nMarker2 != OGRUnsetMarker; + } +} + /************************************************************************/ /* OGR_F_IsFieldSet() */ /************************************************************************/ @@ -672,8 +1125,16 @@ int OGR_F_IsFieldSet( OGRFeatureH hFeat, int iField ) { VALIDATE_POINTER1( hFeat, "OGR_F_IsFieldSet", 0 ); + + OGRFeature* poFeature = (OGRFeature* )hFeat; + + if (iField < 0 || iField >= poFeature->GetFieldCount()) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", iField); + return FALSE; + } - return ((OGRFeature *)hFeat)->IsFieldSet( iField ); + return poFeature->IsFieldSet( iField ); } /************************************************************************/ @@ -693,7 +1154,6 @@ void OGRFeature::UnsetField( int iField ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL || !IsFieldSet(iField) ) return; @@ -817,9 +1277,9 @@ int OGRFeature::GetFieldAsInteger( int iField ) return GetFID(); case SPF_OGR_GEOM_AREA: - if( poGeometry == NULL ) + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) return 0; - return (int)OGR_G_GetArea((OGRGeometryH)poGeometry); + return (int)OGR_G_Area((OGRGeometryH)papoGeometries[0]); default: return 0; @@ -828,7 +1288,6 @@ int OGRFeature::GetFieldAsInteger( int iField ) OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return 0; @@ -908,9 +1367,9 @@ double OGRFeature::GetFieldAsDouble( int iField ) return GetFID(); case SPF_OGR_GEOM_AREA: - if( poGeometry == NULL ) + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) return 0.0; - return OGR_G_GetArea((OGRGeometryH)poGeometry); + return OGR_G_Area((OGRGeometryH)papoGeometries[0]); default: return 0.0; @@ -919,7 +1378,6 @@ double OGRFeature::GetFieldAsDouble( int iField ) OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return 0.0; @@ -984,7 +1442,7 @@ double OGR_F_GetFieldAsDouble( OGRFeatureH hFeat, int iField ) * @param iField the field to fetch, from 0 to GetFieldCount()-1. * * @return the field value. This string is internal, and should not be - * modified, or freed. It's lifetime may be very brief. + * modified, or freed. Its lifetime may be very brief. */ const char *OGRFeature::GetFieldAsString( int iField ) @@ -1007,8 +1465,8 @@ const char *OGRFeature::GetFieldAsString( int iField ) return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); case SPF_OGR_GEOMETRY: - if( poGeometry ) - return poGeometry->getGeometryName(); + if( GetGeomFieldCount() > 0 && papoGeometries[0] != NULL ) + return papoGeometries[0]->getGeometryName(); else return ""; @@ -1020,21 +1478,21 @@ const char *OGRFeature::GetFieldAsString( int iField ) case SPF_OGR_GEOM_WKT: { - if( poGeometry == NULL ) + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) return ""; - if (poGeometry->exportToWkt( &m_pszTmpFieldValue ) == OGRERR_NONE ) + if (papoGeometries[0]->exportToWkt( &m_pszTmpFieldValue ) == OGRERR_NONE ) return m_pszTmpFieldValue; else return ""; } case SPF_OGR_GEOM_AREA: - if( poGeometry == NULL ) + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) return ""; snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "%.16g", - OGR_G_GetArea((OGRGeometryH)poGeometry) ); + OGR_G_Area((OGRGeometryH)papoGeometries[0]) ); return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); default: @@ -1044,7 +1502,6 @@ const char *OGRFeature::GetFieldAsString( int iField ) OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return ""; @@ -1070,8 +1527,8 @@ const char *OGRFeature::GetFieldAsString( int iField ) if( poFDefn->GetWidth() != 0 ) { - snprintf( szFormat, sizeof(szFormat), "%%%d.%df", - poFDefn->GetWidth(), poFDefn->GetPrecision() ); + snprintf( szFormat, sizeof(szFormat), "%%.%df", + poFDefn->GetPrecision() ); } else strcpy( szFormat, "%.15g" ); @@ -1084,7 +1541,7 @@ const char *OGRFeature::GetFieldAsString( int iField ) else if( poFDefn->GetType() == OFTDateTime ) { snprintf( szTempBuffer, TEMP_BUFFER_SIZE, - "%04d/%02d/%02d %2d:%02d:%02d", + "%04d/%02d/%02d %02d:%02d:%02d", pauFields[iField].Date.Year, pauFields[iField].Date.Month, pauFields[iField].Date.Day, @@ -1270,7 +1727,7 @@ const char *OGRFeature::GetFieldAsString( int iField ) * @param iField the field to fetch, from 0 to GetFieldCount()-1. * * @return the field value. This string is internal, and should not be - * modified, or freed. It's lifetime may be very brief. + * modified, or freed. Its lifetime may be very brief. */ const char *OGR_F_GetFieldAsString( OGRFeatureH hFeat, int iField ) @@ -1296,7 +1753,7 @@ const char *OGR_F_GetFieldAsString( OGRFeatureH hFeat, int iField ) * @param pnCount an integer to put the list count (number of integers) into. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. If *pnCount is zero + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero * on return the returned pointer may be NULL or non-NULL. */ @@ -1305,14 +1762,8 @@ const int *OGRFeature::GetFieldAsIntegerList( int iField, int *pnCount ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); - if( poFDefn == NULL ) - return NULL; - - if( !IsFieldSet(iField) ) - return NULL; - - if( poFDefn->GetType() == OFTIntegerList ) + if( poFDefn != NULL && IsFieldSet(iField) && + poFDefn->GetType() == OFTIntegerList ) { if( pnCount != NULL ) *pnCount = pauFields[iField].IntegerList.nCount; @@ -1345,7 +1796,7 @@ const int *OGRFeature::GetFieldAsIntegerList( int iField, int *pnCount ) * @param pnCount an integer to put the list count (number of integers) into. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. If *pnCount is zero + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero * on return the returned pointer may be NULL or non-NULL. */ @@ -1373,7 +1824,7 @@ const int *OGR_F_GetFieldAsIntegerList( OGRFeatureH hFeat, int iField, * @param pnCount an integer to put the list count (number of doubles) into. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. If *pnCount is zero + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero * on return the returned pointer may be NULL or non-NULL. */ @@ -1382,14 +1833,8 @@ const double *OGRFeature::GetFieldAsDoubleList( int iField, int *pnCount ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); - if( poFDefn == NULL ) - return NULL; - - if( !IsFieldSet(iField) ) - return NULL; - - if( poFDefn->GetType() == OFTRealList ) + if( poFDefn != NULL && IsFieldSet(iField) && + poFDefn->GetType() == OFTRealList ) { if( pnCount != NULL ) *pnCount = pauFields[iField].RealList.nCount; @@ -1422,7 +1867,7 @@ const double *OGRFeature::GetFieldAsDoubleList( int iField, int *pnCount ) * @param pnCount an integer to put the list count (number of doubles) into. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. If *pnCount is zero + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero * on return the returned pointer may be NULL or non-NULL. */ @@ -1452,15 +1897,14 @@ const double *OGR_F_GetFieldAsDoubleList( OGRFeatureH hFeat, int iField, * @param iField the field to fetch, from 0 to GetFieldCount()-1. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. + * modified, or freed. Its lifetime may be very brief. */ -char **OGRFeature::GetFieldAsStringList( int iField ) const +char **OGRFeature::GetFieldAsStringList( int iField ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return NULL; @@ -1496,7 +1940,7 @@ char **OGRFeature::GetFieldAsStringList( int iField ) const * @param iField the field to fetch, from 0 to GetFieldCount()-1. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. + * modified, or freed. Its lifetime may be very brief. */ char **OGR_F_GetFieldAsStringList( OGRFeatureH hFeat, int iField ) @@ -1522,7 +1966,7 @@ char **OGR_F_GetFieldAsStringList( OGRFeatureH hFeat, int iField ) * @param pnBytes location to put the number of bytes returned. * * @return the field value. This data is internal, and should not be - * modified, or freed. It's lifetime may be very brief. + * modified, or freed. Its lifetime may be very brief. */ GByte *OGRFeature::GetFieldAsBinary( int iField, int *pnBytes ) @@ -1532,7 +1976,6 @@ GByte *OGRFeature::GetFieldAsBinary( int iField, int *pnBytes ) *pnBytes = 0; - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return NULL; @@ -1567,13 +2010,14 @@ GByte *OGRFeature::GetFieldAsBinary( int iField, int *pnBytes ) * @param pnBytes location to place count of bytes returned. * * @return the field value. This list is internal, and should not be - * modified, or freed. It's lifetime may be very brief. + * modified, or freed. Its lifetime may be very brief. */ GByte *OGR_F_GetFieldAsBinary( OGRFeatureH hFeat, int iField, int *pnBytes ) { VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsBinary", NULL ); + VALIDATE_POINTER1( pnBytes, "OGR_F_GetFieldAsBinary", NULL ); return ((OGRFeature *)hFeat)->GetFieldAsBinary(iField,pnBytes); } @@ -1609,7 +2053,6 @@ int OGRFeature::GetFieldAsDateTime( int iField, { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return FALSE; @@ -1706,10 +2149,9 @@ void OGRFeature::SetField( int iField, int nValue ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; - + if( poFDefn->GetType() == OFTInteger ) { pauFields[iField].Integer = nValue; @@ -1719,6 +2161,15 @@ void OGRFeature::SetField( int iField, int nValue ) { pauFields[iField].Real = nValue; } + else if( poFDefn->GetType() == OFTIntegerList ) + { + SetField( iField, 1, &nValue ); + } + else if( poFDefn->GetType() == OFTRealList ) + { + double dfValue = nValue; + SetField( iField, 1, &dfValue ); + } else if( poFDefn->GetType() == OFTString ) { char szTempBuffer[64]; @@ -1784,7 +2235,6 @@ void OGRFeature::SetField( int iField, double dfValue ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -1797,6 +2247,15 @@ void OGRFeature::SetField( int iField, double dfValue ) pauFields[iField].Integer = (int) dfValue; pauFields[iField].Set.nMarker2 = 0; } + else if( poFDefn->GetType() == OFTRealList ) + { + SetField( iField, 1, &dfValue ); + } + else if( poFDefn->GetType() == OFTIntegerList ) + { + int nValue = (int) dfValue; + SetField( iField, 1, &nValue ); + } else if( poFDefn->GetType() == OFTString ) { char szTempBuffer[128]; @@ -1859,9 +2318,13 @@ void OGR_F_SetFieldDouble( OGRFeatureH hFeat, int iField, double dfValue ) void OGRFeature::SetField( int iField, const char * pszValue ) { - OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + static int bWarn = -1; + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + char *pszLast; + + if( bWarn < 0 ) + bWarn = CSLTestBoolean( CPLGetConfigOption( "OGR_SETFIELD_NUMERIC_WARNING", "NO" ) ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -1874,12 +2337,21 @@ void OGRFeature::SetField( int iField, const char * pszValue ) } else if( poFDefn->GetType() == OFTInteger ) { - pauFields[iField].Integer = atoi(pszValue); + long nVal = strtol(pszValue, &pszLast, 10); + pauFields[iField].Integer = (nVal > INT_MAX) ? INT_MAX : (nVal < INT_MIN) ? INT_MIN : (int) nVal; + if( bWarn && (nVal != (long)pauFields[iField].Integer || !pszLast || *pszLast ) ) + CPLError(CE_Warning, CPLE_AppDefined, + "Value '%s' of field %s.%s parsed incompletely to integer %d.", + pszValue, poDefn->GetName(), poFDefn->GetNameRef(), pauFields[iField].Integer ); pauFields[iField].Set.nMarker2 = OGRUnsetMarker; } else if( poFDefn->GetType() == OFTReal ) { - pauFields[iField].Real = atof(pszValue); + pauFields[iField].Real = CPLStrtod(pszValue, &pszLast); + if( bWarn && ( !pszLast || *pszLast ) ) + CPLError(CE_Warning, CPLE_AppDefined, + "Value '%s' of field %s.%s parsed incompletely to real %.16g.", + pszValue, poDefn->GetName(), poFDefn->GetNameRef(), pauFields[iField].Real ); } else if( poFDefn->GetType() == OFTDate || poFDefn->GetType() == OFTTime @@ -1890,6 +2362,51 @@ void OGRFeature::SetField( int iField, const char * pszValue ) if( OGRParseDate( pszValue, &sWrkField, 0 ) ) memcpy( pauFields+iField, &sWrkField, sizeof(sWrkField)); } + else if( poFDefn->GetType() == OFTIntegerList + || poFDefn->GetType() == OFTRealList ) + { + char **papszValueList = NULL; + + if( pszValue[0] == '(' && strchr(pszValue,':') != NULL ) + { + papszValueList = CSLTokenizeString2( + pszValue, ",:()", 0 ); + } + + if( CSLCount(papszValueList) == 0 + || atoi(papszValueList[0]) != CSLCount(papszValueList)-1 ) + { + /* do nothing - the count does not match entries */ + } + else if( poFDefn->GetType() == OFTIntegerList ) + { + int i, nCount = atoi(papszValueList[0]); + std::vector anValues; + + for( i=0; i < nCount; i++ ) + anValues.push_back( atoi(papszValueList[i+1]) ); + SetField( iField, nCount, &(anValues[0]) ); + } + else if( poFDefn->GetType() == OFTRealList ) + { + int i, nCount = atoi(papszValueList[0]); + std::vector adfValues; + + for( i=0; i < nCount; i++ ) + adfValues.push_back( atof(papszValueList[i+1]) ); + SetField( iField, nCount, &(adfValues[0]) ); + } + + CSLDestroy(papszValueList); + } + else if ( poFDefn->GetType() == OFTStringList ) + { + if( pszValue && *pszValue ) + { + const char *papszValues[2] = { pszValue, 0 }; + SetField( iField, (char **) papszValues ); + } + } else /* do nothing for other field types */; } @@ -1941,7 +2458,6 @@ void OGRFeature::SetField( int iField, int nCount, int *panValues ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -1950,10 +2466,25 @@ void OGRFeature::SetField( int iField, int nCount, int *panValues ) OGRField uField; uField.IntegerList.nCount = nCount; + uField.Set.nMarker2 = 0; uField.IntegerList.paList = panValues; SetField( iField, &uField ); } + else if( poFDefn->GetType() == OFTRealList ) + { + std::vector adfValues; + + for( int i=0; i < nCount; i++ ) + adfValues.push_back( (double) panValues[i] ); + + SetField( iField, nCount, &adfValues[0] ); + } + else if( (poFDefn->GetType() == OFTInteger || poFDefn->GetType() == OFTReal) + && nCount == 1 ) + { + SetField( iField, panValues[0] ); + } } /************************************************************************/ @@ -2003,7 +2534,6 @@ void OGRFeature::SetField( int iField, int nCount, double * padfValues ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -2012,10 +2542,25 @@ void OGRFeature::SetField( int iField, int nCount, double * padfValues ) OGRField uField; uField.RealList.nCount = nCount; + uField.Set.nMarker2 = 0; uField.RealList.paList = padfValues; SetField( iField, &uField ); } + else if( poFDefn->GetType() == OFTIntegerList ) + { + std::vector anValues; + + for( int i=0; i < nCount; i++ ) + anValues.push_back( (int) padfValues[i] ); + + SetField( iField, nCount, &anValues[0] ); + } + else if( (poFDefn->GetType() == OFTInteger || poFDefn->GetType() == OFTReal) + && nCount == 1 ) + { + SetField( iField, padfValues[0] ); + } } /************************************************************************/ @@ -2064,7 +2609,6 @@ void OGRFeature::SetField( int iField, char ** papszValues ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -2073,8 +2617,9 @@ void OGRFeature::SetField( int iField, char ** papszValues ) OGRField uField; uField.StringList.nCount = CSLCount(papszValues); + uField.Set.nMarker2 = 0; uField.StringList.paList = papszValues; - + SetField( iField, &uField ); } } @@ -2125,7 +2670,6 @@ void OGRFeature::SetField( int iField, int nBytes, GByte *pabyData ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -2134,7 +2678,9 @@ void OGRFeature::SetField( int iField, int nBytes, GByte *pabyData ) OGRField uField; uField.Binary.nCount = nBytes; + uField.Set.nMarker2 = 0; uField.Binary.paData = pabyData; + SetField( iField, &uField ); } } @@ -2194,7 +2740,6 @@ void OGRFeature::SetField( int iField, int nYear, int nMonth, int nDay, { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -2202,6 +2747,13 @@ void OGRFeature::SetField( int iField, int nYear, int nMonth, int nDay, || poFDefn->GetType() == OFTTime || poFDefn->GetType() == OFTDateTime ) { + if( (GInt16)nYear != nYear ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Years < -32768 or > 32767 are not supported"); + return; + } + pauFields[iField].Date.Year = (GInt16)nYear; pauFields[iField].Date.Month = (GByte)nMonth; pauFields[iField].Date.Day = (GByte)nDay; @@ -2269,7 +2821,6 @@ void OGRFeature::SetField( int iField, OGRField * puValue ) { OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); - CPLAssert( poFDefn != NULL || iField == -1 ); if( poFDefn == NULL ) return; @@ -2480,12 +3031,26 @@ void OGRFeature::DumpReadable( FILE * fpOut, char** papszOptions ) } } - if( poGeometry != NULL ) + int nGeomFieldCount = GetGeomFieldCount(); + if( nGeomFieldCount > 0 ) { const char* pszDisplayGeometry = CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY"); if ( ! (pszDisplayGeometry != NULL && EQUAL(pszDisplayGeometry, "NO") ) ) - poGeometry->dumpReadable( fpOut, " ", papszOptions ); + { + for( int iField = 0; iField < nGeomFieldCount; iField++ ) + { + OGRGeomFieldDefn *poFDefn = poDefn->GetGeomFieldDefn(iField); + + if( papoGeometries[iField] != NULL ) + { + fprintf( fpOut, " " ); + if( strlen(poFDefn->GetNameRef()) > 0 && GetGeomFieldCount() > 1 ) + fprintf( fpOut, "%s = ", poFDefn->GetNameRef() ); + papoGeometries[iField]->dumpReadable( fpOut, "", papszOptions ); + } + } + } } fprintf( fpOut, "\n" ); @@ -2761,15 +3326,22 @@ OGRBoolean OGRFeature::Equal( OGRFeature * poFeature ) } } - if( GetGeometryRef() == NULL && poFeature->GetGeometryRef() != NULL ) - return FALSE; + int nGeomFieldCount = GetGeomFieldCount(); + for( i = 0; i < nGeomFieldCount; i ++ ) + { + OGRGeometry* poThisGeom = GetGeomFieldRef(i); + OGRGeometry* poOtherGeom = poFeature->GetGeomFieldRef(i); - if( GetGeometryRef() != NULL && poFeature->GetGeometryRef() == NULL ) - return FALSE; + if( poThisGeom == NULL && poOtherGeom != NULL ) + return FALSE; - if( GetGeometryRef() != NULL && poFeature->GetGeometryRef() != NULL - && (!GetGeometryRef()->Equals( poFeature->GetGeometryRef() ) ) ) - return FALSE; + if( poThisGeom != NULL && poOtherGeom == NULL ) + return FALSE; + + if( poThisGeom != NULL && poOtherGeom != NULL + && (!poThisGeom->Equals( poOtherGeom ) ) ) + return FALSE; + } return TRUE; } @@ -2937,14 +3509,41 @@ OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int *panMap , { OGRErr eErr; + if( poSrcFeature == this ) + return OGRERR_FAILURE; + SetFID( OGRNullFID ); /* -------------------------------------------------------------------- */ /* Set the geometry. */ /* -------------------------------------------------------------------- */ - eErr = SetGeometry( poSrcFeature->GetGeometryRef() ); - if( eErr != OGRERR_NONE ) - return eErr; + if( GetGeomFieldCount() == 1 ) + { + OGRGeomFieldDefn* poGFieldDefn = GetGeomFieldDefnRef(0); + + int iSrc = poSrcFeature->GetGeomFieldIndex( + poGFieldDefn->GetNameRef()); + if( iSrc >= 0 ) + SetGeomField( 0, poSrcFeature->GetGeomFieldRef(iSrc) ); + else + /* whatever the geometry field names are. For backward compatibility */ + SetGeomField( 0, poSrcFeature->GetGeomFieldRef(0) ); + } + else + { + int i; + for(i = 0; i < GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poGFieldDefn = GetGeomFieldDefnRef(i); + + int iSrc = poSrcFeature->GetGeomFieldIndex( + poGFieldDefn->GetNameRef()); + if( iSrc >= 0 ) + SetGeomField( i, poSrcFeature->GetGeomFieldRef(iSrc) ); + else + SetGeomField( i, NULL ); + } + } /* -------------------------------------------------------------------- */ /* Copy feature style string. */ @@ -2954,6 +3553,94 @@ OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int *panMap , /* -------------------------------------------------------------------- */ /* Set the fields by name. */ /* -------------------------------------------------------------------- */ + + eErr = SetFieldsFrom( poSrcFeature, panMap, bForgiving ); + if( eErr != OGRERR_NONE ) + return eErr; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetFromWithMap() */ +/************************************************************************/ + +/** + * \brief Set one feature from another. + * + * Overwrite the contents of this feature from the geometry and attributes + * of another. The hOtherFeature does not need to have the same + * OGRFeatureDefn. Field values are copied according to the provided indices + * map. Field types do not have to exactly match. OGR_F_SetField*() function + * conversion rules will be applied as needed. This is more efficient than + * OGR_F_SetFrom() in that this doesn't lookup the fields by their names. + * Particularly useful when the field names don't match. + * + * This function is the same as the C++ method OGRFeature::SetFrom(). + * + * @param hFeat handle to the feature to set to. + * @param hOtherFeat handle to the feature from which geometry, + * and field values will be copied. + * + * @param panMap Array of the indices of the destination feature's fields + * stored at the corresponding index of the source feature's fields. A value of + * -1 should be used to ignore the source's field. The array should not be NULL + * and be as long as the number of fields in the source feature. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGR_F_SetFromWithMap( OGRFeatureH hFeat, OGRFeatureH hOtherFeat, + int bForgiving, int *panMap ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetFrom", CE_Failure ); + VALIDATE_POINTER1( hOtherFeat, "OGR_F_SetFrom", CE_Failure ); + VALIDATE_POINTER1( panMap, "OGR_F_SetFrom", CE_Failure); + + return ((OGRFeature *) hFeat)->SetFrom( (OGRFeature *) hOtherFeat, + panMap, bForgiving ); +} + +/************************************************************************/ +/* SetFieldsFrom() */ +/************************************************************************/ + +/** + * \brief Set fields from another feature. + * + * Overwrite the fields of this feature from the attributes of + * another. The FID and the style string are not set. The poSrcFeature + * does not need to have the same OGRFeatureDefn. Field values are + * copied according to the provided indices map. Field types do not + * have to exactly match. SetField() method conversion rules will be + * applied as needed. This is more efficient than OGR_F_SetFrom() in + * that this doesn't lookup the fields by their names. Particularly + * useful when the field names don't match. + * + * @param poSrcFeature the feature from which geometry, and field values will + * be copied. + * + * @param panMap Array of the indices of the feature's fields + * stored at the corresponding index of the source feature's fields. A value of + * -1 should be used to ignore the source's field. The array should not be NULL + * and be as long as the number of fields in the source feature. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGRFeature::SetFieldsFrom( OGRFeature * poSrcFeature, int *panMap , + int bForgiving ) + +{ int iField, iDstField; for( iField = 0; iField < poSrcFeature->GetFieldCount(); iField++ ) @@ -2962,7 +3649,7 @@ OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int *panMap , if( iDstField < 0 ) continue; - + if( GetFieldCount() <= iDstField ) return OGRERR_FAILURE; @@ -2986,6 +3673,36 @@ OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int *panMap , SetField( iDstField, poSrcFeature->GetFieldAsString( iField ) ); break; + case OFTIntegerList: + { + if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString(iField) ); + } + else + { + int nCount; + const int *panValues = poSrcFeature->GetFieldAsIntegerList( iField, &nCount); + SetField( iDstField, nCount, (int*) panValues ); + } + } + break; + + case OFTRealList: + { + if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString(iField) ); + } + else + { + int nCount; + const double *padfValues = poSrcFeature->GetFieldAsDoubleList( iField, &nCount); + SetField( iDstField, nCount, (double*) padfValues ); + } + } + break; + case OFTDate: case OFTDateTime: case OFTTime: @@ -3022,51 +3739,6 @@ OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int *panMap , return OGRERR_NONE; } -/************************************************************************/ -/* OGR_F_SetFromWithMap() */ -/************************************************************************/ - -/** - * \brief Set one feature from another. - * - * Overwrite the contents of this feature from the geometry and attributes - * of another. The hOtherFeature does not need to have the same - * OGRFeatureDefn. Field values are copied according to the provided indices - * map. Field types do not have to exactly match. OGR_F_SetField*() function - * conversion rules will be applied as needed. This is more efficient than - * OGR_F_SetFrom() in that this doesn't lookup the fields by their names. - * Particularly useful when the field names don't match. - * - * This function is the same as the C++ method OGRFeature::SetFrom(). - * - * @param hFeat handle to the feature to set to. - * @param hOtherFeat handle to the feature from which geometry, - * and field values will be copied. - * - * @param panMap Array of the indices of the destination feature's fields - * stored at the corresponding index of the source feature's fields. A value of - * -1 should be used to ignore the source's field. The array should not be NULL - * and be as long as the number of fields in the source feature. - * - * @param bForgiving TRUE if the operation should continue despite lacking - * output fields matching some of the source fields. - * - * @return OGRERR_NONE if the operation succeeds, even if some values are - * not transferred, otherwise an error code. - */ - -OGRErr OGR_F_SetFromWithMap( OGRFeatureH hFeat, OGRFeatureH hOtherFeat, - int bForgiving, int *panMap ) - -{ - VALIDATE_POINTER1( hFeat, "OGR_F_SetFrom", CE_Failure ); - VALIDATE_POINTER1( hOtherFeat, "OGR_F_SetFrom", CE_Failure ); - VALIDATE_POINTER1( panMap, "OGR_F_SetFrom", CE_Failure); - - return ((OGRFeature *) hFeat)->SetFrom( (OGRFeature *) hOtherFeat, - panMap, bForgiving ); -} - /************************************************************************/ /* GetStyleString() */ /************************************************************************/ @@ -3085,16 +3757,16 @@ OGRErr OGR_F_SetFromWithMap( OGRFeatureH hFeat, OGRFeatureH hOtherFeat, const char *OGRFeature::GetStyleString() { - int iStyleFieldIndex; + int iStyleFieldIndex; - if (m_pszStyleString) - return m_pszStyleString; + if (m_pszStyleString) + return m_pszStyleString; - iStyleFieldIndex = GetFieldIndex("OGR_STYLE"); - if (iStyleFieldIndex >= 0) - return GetFieldAsString(iStyleFieldIndex); + iStyleFieldIndex = GetFieldIndex("OGR_STYLE"); + if (iStyleFieldIndex >= 0) + return GetFieldAsString(iStyleFieldIndex); - return NULL; + return NULL; } /************************************************************************/ @@ -3229,6 +3901,17 @@ void OGRFeature::SetStyleTable(OGRStyleTable *poStyleTable) m_poStyleTable = ( poStyleTable ) ? poStyleTable->Clone() : NULL; } +//************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +void OGRFeature::SetStyleTableDirectly(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = poStyleTable; +} + /************************************************************************/ /* RemapFields() */ /* */ @@ -3281,6 +3964,56 @@ OGRErr OGRFeature::RemapFields( OGRFeatureDefn *poNewDefn, return OGRERR_NONE; } +/************************************************************************/ +/* RemapGeomFields() */ +/* */ +/* This is used to transform a feature "in place" from one */ +/* feature defn to another with minimum work. */ +/************************************************************************/ + +OGRErr OGRFeature::RemapGeomFields( OGRFeatureDefn *poNewDefn, + int *panRemapSource ) + +{ + int iDstField; + OGRGeometry** papoNewGeomFields; + + if( poNewDefn == NULL ) + poNewDefn = poDefn; + + papoNewGeomFields = (OGRGeometry **) CPLCalloc( poNewDefn->GetGeomFieldCount(), + sizeof(OGRGeometry*) ); + + for( iDstField = 0; iDstField < poDefn->GetGeomFieldCount(); iDstField++ ) + { + if( panRemapSource[iDstField] == -1 ) + { + papoNewGeomFields[iDstField] = NULL; + } + else + { + papoNewGeomFields[iDstField] = + papoGeometries[panRemapSource[iDstField]]; + } + } + + /* + ** We really should be freeing memory for old columns that + ** are no longer present. We don't for now because it is a bit messy + ** and would take too long to test. + */ + +/* -------------------------------------------------------------------- */ +/* Apply new definition and fields. */ +/* -------------------------------------------------------------------- */ + CPLFree( papoGeometries ); + papoGeometries = papoNewGeomFields; + + poDefn = poNewDefn; + + return OGRERR_NONE; +} + /************************************************************************/ /* OGR_F_GetStyleTable() */ /************************************************************************/ diff --git a/ogr/ogrfeaturedefn.cpp b/ogr/ogrfeaturedefn.cpp index e4684b0..72f4f1a 100644 --- a/ogr/ogrfeaturedefn.cpp +++ b/ogr/ogrfeaturedefn.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrfeaturedefn.cpp 17587 2009-08-27 17:56:01Z warmerdam $ + * $Id: ogrfeaturedefn.cpp 27110 2014-03-28 21:29:20Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRFeatureDefn class implementation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2009-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,7 +32,7 @@ #include "ogr_api.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrfeaturedefn.cpp 17587 2009-08-27 17:56:01Z warmerdam $"); +CPL_CVSID("$Id: ogrfeaturedefn.cpp 27110 2014-03-28 21:29:20Z rouault $"); /************************************************************************/ /* OGRFeatureDefn() */ @@ -57,7 +58,10 @@ OGRFeatureDefn::OGRFeatureDefn( const char * pszName ) nRefCount = 0; nFieldCount = 0; papoFieldDefn = NULL; - eGeomType = wkbUnknown; + nGeomFieldCount = 1; + papoGeomFieldDefn = (OGRGeomFieldDefn**) CPLMalloc(sizeof(OGRGeomFieldDefn*)); + papoGeomFieldDefn[0] = new OGRGeomFieldDefn("", wkbUnknown); + bIgnoreStyle = FALSE; } /************************************************************************/ @@ -106,6 +110,13 @@ OGRFeatureDefn::~OGRFeatureDefn() } CPLFree( papoFieldDefn ); + + for( int i = 0; i < nGeomFieldCount; i++ ) + { + delete papoGeomFieldDefn[i]; + } + + CPLFree( papoGeomFieldDefn ); } /************************************************************************/ @@ -180,15 +191,21 @@ void OGR_FD_Release( OGRFeatureDefnH hDefn ) OGRFeatureDefn *OGRFeatureDefn::Clone() { + int i; OGRFeatureDefn *poCopy; poCopy = new OGRFeatureDefn( GetName() ); - poCopy->SetGeomType( GetGeomType() ); - - for( int i = 0; i < GetFieldCount(); i++ ) + GetFieldCount(); + for( i = 0; i < nFieldCount; i++ ) poCopy->AddFieldDefn( GetFieldDefn( i ) ); + /* There is a default geometry field created at OGRFeatureDefn instanciation */ + poCopy->DeleteGeomFieldDefn(0); + GetGeomFieldCount(); + for( i = 0; i < nGeomFieldCount; i++ ) + poCopy->AddGeomFieldDefn( GetGeomFieldDefn( i ) ); + return poCopy; } @@ -206,6 +223,10 @@ OGRFeatureDefn *OGRFeatureDefn::Clone() * @return the name. This name is internal and should not be modified, or * freed. */ +const char * OGRFeatureDefn::GetName() +{ + return pszFeatureClassName; +} /************************************************************************/ /* OGR_FD_GetName() */ @@ -239,6 +260,11 @@ const char *OGR_FD_GetName( OGRFeatureDefnH hDefn ) * @return count of fields. */ +int OGRFeatureDefn::GetFieldCount() +{ + return nFieldCount; +} + /************************************************************************/ /* OGR_FD_GetFieldCount() */ /************************************************************************/ @@ -279,7 +305,7 @@ int OGR_FD_GetFieldCount( OGRFeatureDefnH hDefn ) OGRFieldDefn *OGRFeatureDefn::GetFieldDefn( int iField ) { - if( iField < 0 || iField >= nFieldCount ) + if( iField < 0 || iField >= GetFieldCount() ) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", iField); return NULL; @@ -322,6 +348,9 @@ OGRFieldDefnH OGR_FD_GetFieldDefn( OGRFeatureDefnH hDefn, int iField ) /** * \brief Add a new field definition. * + * To add a new field definition to a layer definition, do not use this + * function directly, but use OGRLayer::CreateField() instead. + * * This method should only be called while there are no OGRFeature * objects in existance based on this OGRFeatureDefn. The OGRFieldDefn * passed in is copied, and remains the responsibility of the caller. @@ -334,6 +363,7 @@ OGRFieldDefnH OGR_FD_GetFieldDefn( OGRFeatureDefnH hDefn, int iField ) void OGRFeatureDefn::AddFieldDefn( OGRFieldDefn * poNewDefn ) { + GetFieldCount(); papoFieldDefn = (OGRFieldDefn **) CPLRealloc( papoFieldDefn, sizeof(void*)*(nFieldCount+1) ); @@ -348,11 +378,14 @@ void OGRFeatureDefn::AddFieldDefn( OGRFieldDefn * poNewDefn ) /** * \brief Add a new field definition to the passed feature definition. * + * To add a new field definition to a layer definition, do not use this + * function directly, but use OGR_L_CreateField() instead. + * * This function should only be called while there are no OGRFeature * objects in existance based on this OGRFeatureDefn. The OGRFieldDefn * passed in is copied, and remains the responsibility of the caller. * - * This function is the same as the C++ method OGRFeatureDefn::AddFieldDefn. + * This function is the same as the C++ method OGRFeatureDefn::AddFieldDefn(). * * @param hDefn handle to the feature definition to add the field definition * to. @@ -365,6 +398,441 @@ void OGR_FD_AddFieldDefn( OGRFeatureDefnH hDefn, OGRFieldDefnH hNewField ) ((OGRFeatureDefn *) hDefn)->AddFieldDefn( (OGRFieldDefn *) hNewField ); } +/************************************************************************/ +/* DeleteFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing field definition. + * + * To delete an existing field definition from a layer definition, do not use this + * function directly, but use OGRLayer::DeleteField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_DeleteFieldDefn(). + * + * @param iField the index of the field defintion. + * @return OGRERR_NONE in case of success. + * @since OGR 1.9.0 + */ + +OGRErr OGRFeatureDefn::DeleteFieldDefn( int iField ) + +{ + if (iField < 0 || iField >= GetFieldCount()) + return OGRERR_FAILURE; + + delete papoFieldDefn[iField]; + papoFieldDefn[iField] = NULL; + + if (iField < nFieldCount - 1) + { + memmove(papoFieldDefn + iField, + papoFieldDefn + iField + 1, + (nFieldCount - 1 - iField) * sizeof(void*)); + } + + nFieldCount--; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_FD_DeleteFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing field definition. + * + * To delete an existing field definition from a layer definition, do not use this + * function directly, but use OGR_L_DeleteField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C++ method OGRFeatureDefn::DeleteFieldDefn(). + * + * @param hDefn handle to the feature definition. + * @param iField the index of the field defintion. + * @return OGRERR_NONE in case of success. + * @since OGR 1.9.0 + */ + +OGRErr OGR_FD_DeleteFieldDefn( OGRFeatureDefnH hDefn, int iField ) + +{ + return ((OGRFeatureDefn *) hDefn)->DeleteFieldDefn( iField ); +} + +/************************************************************************/ +/* ReorderFieldDefns() */ +/************************************************************************/ + +/** + * \brief Reorder the field definitions in the array of the feature definition + * + * To reorder the field definitions in a layer definition, do not use this + * function directly, but use OGR_L_ReorderFields() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_ReorderFieldDefns(). + * + * @param panMap an array of GetFieldCount() elements which + * is a permutation of [0, GetFieldCount()-1]. panMap is such that, + * for each field definition at position i after reordering, + * its position before reordering was panMap[i]. + * @return OGRERR_NONE in case of success. + * @since OGR 1.9.0 + */ + +OGRErr OGRFeatureDefn::ReorderFieldDefns( int* panMap ) + +{ + if (GetFieldCount() == 0) + return OGRERR_NONE; + + OGRErr eErr = OGRCheckPermutation(panMap, nFieldCount); + if (eErr != OGRERR_NONE) + return eErr; + + OGRFieldDefn** papoFieldDefnNew = (OGRFieldDefn**) + CPLMalloc(sizeof(OGRFieldDefn*) * nFieldCount); + + for(int i=0;iReorderFieldDefns( panMap ); +} + + +/************************************************************************/ +/* GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::GetGeomFieldCount(); + * + * \brief Fetch number of geometry fields on this feature. + * + * This method is the same as the C function OGR_FD_GetGeomFieldCount(). + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ +int OGRFeatureDefn::GetGeomFieldCount() +{ + return nGeomFieldCount; +} + +/************************************************************************/ +/* OGR_FD_GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \brief Fetch number of geometry fields on the passed feature definition. + * + * This function is the same as the C++ OGRFeatureDefn::GetGeomFieldCount(). + * + * @param hDefn handle to the feature definition to get the fields count from. + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ + +int OGR_FD_GetGeomFieldCount( OGRFeatureDefnH hDefn ) + +{ + return ((OGRFeatureDefn *) hDefn)->GetGeomFieldCount(); +} + +/************************************************************************/ +/* GetGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Fetch geometry field definition. + * + * This method is the same as the C function OGR_FD_GetGeomFieldDefn(). + * + * @param iGeomField the geometry field to fetch, between 0 and GetGeomFieldCount()-1. + * + * @return a pointer to an internal field definition object or NULL if invalid index. + * This object should not be modified or freed by the application. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefn *OGRFeatureDefn::GetGeomFieldDefn( int iGeomField ) + +{ + if( iGeomField < 0 || iGeomField >= GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", iGeomField); + return NULL; + } + + return papoGeomFieldDefn[iGeomField]; +} + +/************************************************************************/ +/* OGR_FD_GetGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Fetch geometry field definition of the passed feature definition. + * + * This function is the same as the C++ method + * OGRFeatureDefn::GetGeomFieldDefn(). + * + * @param hDefn handle to the feature definition to get the field definition + * from. + * @param iGeomField the geometry field to fetch, between 0 and GetGeomFieldCount()-1. + * + * @return an handle to an internal field definition object or NULL if invalid index. + * This object should not be modified or freed by the application. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefnH OGR_FD_GetGeomFieldDefn( OGRFeatureDefnH hDefn, int iGeomField ) + +{ + return (OGRGeomFieldDefnH) ((OGRFeatureDefn *) hDefn)->GetGeomFieldDefn( iGeomField ); +} + +/************************************************************************/ +/* AddGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Add a new geometry field definition. + * + * To add a new geometry field definition to a layer definition, do not use this + * function directly, but use OGRLayer::CreateGeomField() instead. + * + * This method does an internal copy of the passed geometry field definition, + * unless bCopy is set to FALSE (in which case it takes ownership of the + * field definition. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. The OGRGeomFieldDefn + * passed in is copied, and remains the responsibility of the caller. + * + * This method is the same as the C function OGR_FD_AddGeomFieldDefn(). + * + * @param poNewDefn the definition of the new geometry field. + * @param bCopy whether poNewDefn should be copied. + * + * @since GDAL 1.11 + */ + +void OGRFeatureDefn::AddGeomFieldDefn( OGRGeomFieldDefn * poNewDefn, + int bCopy ) +{ + GetGeomFieldCount(); + papoGeomFieldDefn = (OGRGeomFieldDefn **) + CPLRealloc( papoGeomFieldDefn, sizeof(void*)*(nGeomFieldCount+1) ); + + papoGeomFieldDefn[nGeomFieldCount] = (bCopy) ? + new OGRGeomFieldDefn( poNewDefn ) : poNewDefn; + nGeomFieldCount++; +} + +/************************************************************************/ +/* OGR_FD_AddGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Add a new field definition to the passed feature definition. + * + * To add a new field definition to a layer definition, do not use this + * function directly, but use OGR_L_CreateGeomField() instead. + * + * This function should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. The OGRGeomFieldDefn + * passed in is copied, and remains the responsibility of the caller. + * + * This function is the same as the C++ method OGRFeatureDefn::AddGeomFieldDefn(). + * + * @param hDefn handle to the feature definition to add the geometry field definition + * to. + * @param hNewGeomField handle to the new field definition. + * + * @since GDAL 1.11 + */ + +void OGR_FD_AddGeomFieldDefn( OGRFeatureDefnH hDefn, OGRGeomFieldDefnH hNewGeomField ) + +{ + ((OGRFeatureDefn *) hDefn)->AddGeomFieldDefn( (OGRGeomFieldDefn *) hNewGeomField ); +} + +/************************************************************************/ +/* DeleteGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing geometry field definition. + * + * To delete an existing field definition from a layer definition, do not use this + * function directly, but use OGRLayer::DeleteGeomField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_DeleteGeomFieldDefn(). + * + * @param iGeomField the index of the geometry field defintion. + * @return OGRERR_NONE in case of success. + * + * @since GDAL 1.11 + */ + +OGRErr OGRFeatureDefn::DeleteGeomFieldDefn( int iGeomField ) + +{ + if (iGeomField < 0 || iGeomField >= GetGeomFieldCount()) + return OGRERR_FAILURE; + + delete papoGeomFieldDefn[iGeomField]; + papoGeomFieldDefn[iGeomField] = NULL; + + if (iGeomField < nGeomFieldCount - 1) + { + memmove(papoGeomFieldDefn + iGeomField, + papoGeomFieldDefn + iGeomField + 1, + (nGeomFieldCount - 1 - iGeomField) * sizeof(void*)); + } + + nGeomFieldCount--; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_FD_DeleteGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing geometry field definition. + * + * To delete an existing geometry field definition from a layer definition, do not use this + * function directly, but use OGR_L_DeleteGeomField() instead (*not implemented yet*) + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C++ method OGRFeatureDefn::DeleteGeomFieldDefn(). + * + * @param hDefn handle to the feature definition. + * @param iGeomField the index of the geometry field defintion. + * @return OGRERR_NONE in case of success. + * + * @since GDAL 1.11 + */ + +OGRErr OGR_FD_DeleteGeomFieldDefn( OGRFeatureDefnH hDefn, int iGeomField ) + +{ + return ((OGRFeatureDefn *) hDefn)->DeleteGeomFieldDefn( iGeomField ); +} + + +/************************************************************************/ +/* GetGeomFieldIndex() */ +/************************************************************************/ + +/** + * \brief Find geometry field by name. + * + * The geometry field index of the first geometry field matching the passed + * field name (case insensitively) is returned. + * + * This method is the same as the C function OGR_FD_GetGeomFieldIndex(). + * + * @param pszGeomFieldName the geometry field name to search for. + * + * @return the geometry field index, or -1 if no match found. + */ + + +int OGRFeatureDefn::GetGeomFieldIndex( const char * pszGeomFieldName ) + +{ + GetGeomFieldCount(); + for( int i = 0; i < nGeomFieldCount; i++ ) + { + if( EQUAL(pszGeomFieldName, GetGeomFieldDefn(i)->GetNameRef() ) ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* OGR_FD_GetGeomFieldIndex() */ +/************************************************************************/ +/** + * \brief Find geometry field by name. + * + * The geometry field index of the first geometry field matching the passed + * field name (case insensitively) is returned. + * + * This function is the same as the C++ method OGRFeatureDefn::GetGeomFieldIndex. + * + * @param hDefn handle to the feature definition to get field index from. + * @param pszGeomFieldName the geometry field name to search for. + * + * @return the geometry field index, or -1 if no match found. + */ + +int OGR_FD_GetGeomFieldIndex( OGRFeatureDefnH hDefn, + const char *pszGeomFieldName ) + +{ + return ((OGRFeatureDefn *)hDefn)->GetGeomFieldIndex( pszGeomFieldName ); +} + + /************************************************************************/ /* GetGeomType() */ /************************************************************************/ @@ -381,10 +849,21 @@ void OGR_FD_AddFieldDefn( OGRFeatureDefnH hDefn, OGRFieldDefnH hNewField ) * type as 25D even if some or all geometries are in fact 25D. A few (broken) * drivers return wkbPolygon for layers that also include wkbMultiPolygon. * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->GetType(). + * * This method is the same as the C function OGR_FD_GetGeomType(). * * @return the base type for all geometry related to this definition. */ +OGRwkbGeometryType OGRFeatureDefn::GetGeomType() +{ + if( GetGeomFieldCount() == 0 ) + return wkbNone; + OGRwkbGeometryType eType = GetGeomFieldDefn(0)->GetType(); + if( eType == (wkbUnknown | wkb25DBit) && CSLTestBoolean(CPLGetConfigOption("QGIS_HACK", "NO")) ) + eType = wkbUnknown; + return eType; +} /************************************************************************/ /* OGR_FD_GetGeomType() */ @@ -394,6 +873,8 @@ void OGR_FD_AddFieldDefn( OGRFeatureDefnH hDefn, OGRFieldDefnH hNewField ) * * This function is the same as the C++ method OGRFeatureDefn::GetGeomType(). * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->GetType(). + * * @param hDefn handle to the feature definition to get the geometry type from. * @return the base type for all geometry related to this definition. */ @@ -418,13 +899,26 @@ OGRwkbGeometryType OGR_FD_GetGeomType( OGRFeatureDefnH hDefn ) * * This method is the same as the C function OGR_FD_SetGeomType(). * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetType(). + * * @param eNewType the new type to assign. */ void OGRFeatureDefn::SetGeomType( OGRwkbGeometryType eNewType ) { - eGeomType = eNewType; + if( GetGeomFieldCount() > 0 ) + { + if( GetGeomFieldCount() == 1 && eNewType == wkbNone ) + DeleteGeomFieldDefn(0); + else + GetGeomFieldDefn(0)->SetType(eNewType); + } + else if( eNewType != wkbNone ) + { + OGRGeomFieldDefn oGeomFieldDefn( "", eNewType ); + AddGeomFieldDefn(&oGeomFieldDefn); + } } /************************************************************************/ @@ -441,6 +935,8 @@ void OGRFeatureDefn::SetGeomType( OGRwkbGeometryType eNewType ) * * This function is the same as the C++ method OGRFeatureDefn::SetGeomType(). * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetType(). + * * @param hDefn handle to the layer or feature definition to set the geometry * type to. * @param eType the new type to assign. @@ -452,7 +948,6 @@ void OGR_FD_SetGeomType( OGRFeatureDefnH hDefn, OGRwkbGeometryType eType ) ((OGRFeatureDefn *) hDefn)->SetGeomType( eType ); } - /************************************************************************/ /* Reference() */ /************************************************************************/ @@ -550,7 +1045,7 @@ int OGR_FD_Dereference( OGRFeatureDefnH hDefn ) * This function is the same as the C++ method * OGRFeatureDefn::GetReferenceCount(). * - * @param hDefn hanlde to the feature definition on witch OGRFeature are + * @param hDefn handle to the feature definition on witch OGRFeature are * based on. * @return the current reference count. */ @@ -582,9 +1077,10 @@ int OGR_FD_GetReferenceCount( OGRFeatureDefnH hDefn ) int OGRFeatureDefn::GetFieldIndex( const char * pszFieldName ) { + GetFieldCount(); for( int i = 0; i < nFieldCount; i++ ) { - if( EQUAL(pszFieldName, papoFieldDefn[i]->GetNameRef() ) ) + if( EQUAL(pszFieldName, GetFieldDefn(i)->GetNameRef() ) ) return i; } @@ -614,6 +1110,163 @@ int OGR_FD_GetFieldIndex( OGRFeatureDefnH hDefn, const char *pszFieldName ) return ((OGRFeatureDefn *)hDefn)->GetFieldIndex( pszFieldName ); } +/************************************************************************/ +/* IsGeometryIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::IsGeometryIgnored(); + * + * \brief Determine whether the geometry can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_IsGeometryIgnored(). + * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->IsIgnored(). + * + * @return ignore state + */ + +int OGRFeatureDefn::IsGeometryIgnored() +{ + if( GetGeomFieldCount() == 0 ) + return FALSE; + return GetGeomFieldDefn(0)->IsIgnored(); +} + +/************************************************************************/ +/* OGR_FD_IsGeometryIgnored() */ +/************************************************************************/ + +/** + * \brief Determine whether the geometry can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::IsGeometryIgnored(). + * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->IsIgnored(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @return ignore state + */ + +int OGR_FD_IsGeometryIgnored( OGRFeatureDefnH hDefn ) +{ + return ((OGRFeatureDefn *) hDefn)->IsGeometryIgnored(); +} + +/************************************************************************/ +/* SetGeometryIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRFeatureDefn::SetGeometryIgnored( int bIgnore ); + * + * \brief Set whether the geometry can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_SetGeometryIgnored(). + * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetIgnored(). + * + * @param bIgnore ignore state + */ + +void OGRFeatureDefn::SetGeometryIgnored( int bIgnore ) +{ + if( GetGeomFieldCount() > 0 ) + GetGeomFieldDefn(0)->SetIgnored(bIgnore); +} + +/************************************************************************/ +/* OGR_FD_SetGeometryIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether the geometry can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::SetGeometryIgnored(). + * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetIgnored(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @param bIgnore ignore state + */ + +void OGR_FD_SetGeometryIgnored( OGRFeatureDefnH hDefn, int bIgnore ) +{ + ((OGRFeatureDefn *) hDefn)->SetGeometryIgnored( bIgnore ); +} + +/************************************************************************/ +/* IsStyleIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::IsStyleIgnored(); + * + * \brief Determine whether the style can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_IsStyleIgnored(). + * + * @return ignore state + */ + +/************************************************************************/ +/* OGR_FD_IsStyleIgnored() */ +/************************************************************************/ + +/** + * \brief Determine whether the style can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::IsStyleIgnored(). + * + * @param hDefn handle to the feature definition on which OGRFeature are + * based on. + * @return ignore state + */ + +int OGR_FD_IsStyleIgnored( OGRFeatureDefnH hDefn ) +{ + return ((OGRFeatureDefn *) hDefn)->IsStyleIgnored(); +} + +/************************************************************************/ +/* SetStyleIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRFeatureDefn::SetStyleIgnored( int bIgnore ); + * + * \brief Set whether the style can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_SetStyleIgnored(). + * + * @param bIgnore ignore state + */ + +/************************************************************************/ +/* OGR_FD_SetStyleIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether the style can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::SetStyleIgnored(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @param bIgnore ignore state + */ + +void OGR_FD_SetStyleIgnored( OGRFeatureDefnH hDefn, int bIgnore ) +{ + ((OGRFeatureDefn *) hDefn)->SetStyleIgnored( bIgnore ); +} + /************************************************************************/ /* CreateFeatureDefn() */ /************************************************************************/ @@ -633,3 +1286,67 @@ void OGRFeatureDefn::DestroyFeatureDefn( OGRFeatureDefn *poDefn ) { delete poDefn; } + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the feature definition is identical to the other one. + * + * @param poOtherFeatureDefn the other feature definition to compare to. + * @return TRUE if the feature definition is identical to the other one. + */ + +int OGRFeatureDefn::IsSame( OGRFeatureDefn * poOtherFeatureDefn ) +{ + if (strcmp(GetName(), poOtherFeatureDefn->GetName()) == 0 && + GetFieldCount() == poOtherFeatureDefn->GetFieldCount() && + GetGeomFieldCount() == poOtherFeatureDefn->GetGeomFieldCount()) + { + int i; + for(i=0;iGetFieldDefn(i); + if (!poFldDefn->IsSame(poOtherFldDefn)) + { + return FALSE; + } + } + for(i=0;iGetGeomFieldDefn(i); + if (!poGFldDefn->IsSame(poOtherGFldDefn)) + { + return FALSE; + } + } + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* OGR_FD_IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the feature definition is identical to the other one. + * + * @param hFDefn handle to the feature definition on witch OGRFeature are + * based on. + * @param hOtherFDefn handle to the other feature definition to compare to. + * @return TRUE if the feature definition is identical to the other one. + * + * @since OGR 1.11 + */ + +int OGR_FD_IsSame( OGRFeatureDefnH hFDefn, OGRFeatureDefnH hOtherFDefn ) +{ + VALIDATE_POINTER1( hFDefn, "OGR_FD_IsSame", FALSE ); + VALIDATE_POINTER1( hOtherFDefn, "OGR_FD_IsSame", FALSE ); + return ((OGRFeatureDefn*)hFDefn)->IsSame((OGRFeatureDefn*)hOtherFDefn); +} diff --git a/ogr/ogrfeaturequery.cpp b/ogr/ogrfeaturequery.cpp index daafc2b..d08dd10 100644 --- a/ogr/ogrfeaturequery.cpp +++ b/ogr/ogrfeaturequery.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrfeaturequery.cpp 16787 2009-04-17 13:51:40Z dmorissette $ + * $Id: ogrfeaturequery.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implementation of simple SQL WHERE style attributes queries @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,11 +30,12 @@ ****************************************************************************/ #include +#include "swq.h" #include "ogr_feature.h" #include "ogr_p.h" #include "ogr_attrind.h" -CPL_CVSID("$Id: ogrfeaturequery.cpp 16787 2009-04-17 13:51:40Z dmorissette $"); +CPL_CVSID("$Id: ogrfeaturequery.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* Support for special attributes (feature query and selection) */ @@ -62,8 +64,7 @@ OGRFeatureQuery::OGRFeatureQuery() OGRFeatureQuery::~OGRFeatureQuery() { - if( pSWQExpr != NULL ) - swq_expr_free( (swq_expr *) pSWQExpr ); + delete (swq_expr_node *) pSWQExpr; } /************************************************************************/ @@ -78,7 +79,10 @@ OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn, /* Clear any existing expression. */ /* -------------------------------------------------------------------- */ if( pSWQExpr != NULL ) - swq_expr_free( (swq_expr *) pSWQExpr ); + { + delete (swq_expr_node *) pSWQExpr; + pSWQExpr = NULL; + } /* -------------------------------------------------------------------- */ /* Build list of fields. */ @@ -86,7 +90,8 @@ OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn, char **papszFieldNames; swq_field_type *paeFieldTypes; int iField; - int nFieldCount = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT; + int nFieldCount = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT + + poDefn->GetGeomFieldCount(); papszFieldNames = (char **) CPLMalloc(sizeof(char *) * nFieldCount ); @@ -113,6 +118,12 @@ OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn, paeFieldTypes[iField] = SWQ_STRING; break; + case OFTDate: + case OFTTime: + case OFTDateTime: + paeFieldTypes[iField] = SWQ_TIMESTAMP; + break; + default: paeFieldTypes[iField] = SWQ_OTHER; break; @@ -127,20 +138,29 @@ OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn, ++iField; } + for( iField = 0; iField < poDefn->GetGeomFieldCount(); iField++ ) + { + OGRGeomFieldDefn *poField = poDefn->GetGeomFieldDefn( iField ); + int iDstField = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT + iField; + + papszFieldNames[iDstField] = (char *) poField->GetNameRef(); + if( *papszFieldNames[iDstField] == '\0' ) + papszFieldNames[iDstField] = (char*) OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME; + paeFieldTypes[iDstField] = SWQ_GEOMETRY; + } + /* -------------------------------------------------------------------- */ /* Try to parse. */ /* -------------------------------------------------------------------- */ - const char *pszError; OGRErr eErr = OGRERR_NONE; + CPLErr eCPLErr; poTargetDefn = poDefn; - pszError = swq_expr_compile( pszExpression, nFieldCount, - papszFieldNames, paeFieldTypes, - (swq_expr **) &pSWQExpr ); - if( pszError != NULL ) + eCPLErr = swq_expr_compile( pszExpression, nFieldCount, + papszFieldNames, paeFieldTypes, + (swq_expr_node **) &pSWQExpr ); + if( eCPLErr != CE_None ) { - CPLError( CE_Failure, CPLE_AppDefined, - "%s", pszError ); eErr = OGRERR_CORRUPT_DATA; pSWQExpr = NULL; } @@ -153,263 +173,128 @@ OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn, } /************************************************************************/ -/* OGRFeatureQueryEvaluator() */ +/* OGRFeatureFetcher() */ /************************************************************************/ -static int OGRFeatureQueryEvaluator( swq_field_op *op, OGRFeature *poFeature ) +static swq_expr_node *OGRFeatureFetcher( swq_expr_node *op, void *pFeatureIn ) { - OGRField sField; - OGRField *psField; + OGRFeature *poFeature = (OGRFeature *) pFeatureIn; + swq_expr_node *poRetNode = NULL; - int iSpecialField = op->field_index - poFeature->GetDefnRef()->GetFieldCount(); - if( iSpecialField >= 0 ) + if( op->field_type == SWQ_GEOMETRY ) { - if ( iSpecialField < SPECIAL_FIELD_COUNT ) - { - switch ( SpecialFieldTypes[iSpecialField] ) - { - case SWQ_INTEGER: - sField.Integer = poFeature->GetFieldAsInteger(op->field_index); - break; - - case SWQ_FLOAT: - sField.Real = poFeature->GetFieldAsDouble(op->field_index); - break; - - case SWQ_STRING: - sField.String = (char*) - poFeature->GetFieldAsString( op->field_index ); - break; - - default: - CPLAssert( FALSE ); - break; - } - } - else - { - CPLDebug( "OGRFeatureQuery", "Illegal special field index."); - return FALSE; - } - psField = &sField; + int iField = op->field_index - (poFeature->GetFieldCount() + SPECIAL_FIELD_COUNT); + poRetNode = new swq_expr_node( poFeature->GetGeomFieldRef(iField) ); + return poRetNode; } - else - psField = poFeature->GetRawFieldRef( op->field_index ); switch( op->field_type ) { case SWQ_INTEGER: - switch( op->operation ) - { - case SWQ_EQ: - return psField->Integer == op->int_value; - case SWQ_NE: - return psField->Integer != op->int_value; - case SWQ_LT: - return psField->Integer < op->int_value; - case SWQ_GT: - return psField->Integer > op->int_value; - case SWQ_LE: - return psField->Integer <= op->int_value; - case SWQ_GE: - return psField->Integer >= op->int_value; - case SWQ_ISNULL: - return !poFeature->IsFieldSet( op->field_index ); - - case SWQ_IN: - { - const char *pszSrc; - - pszSrc = op->string_value; - while( *pszSrc != '\0' ) - { - if( atoi(pszSrc) == psField->Integer ) - return TRUE; - pszSrc += strlen(pszSrc) + 1; - } - - return FALSE; - } - - default: - CPLDebug( "OGRFeatureQuery", - "Illegal operation (%d) on integer field.", - op->operation ); - return FALSE; - } + case SWQ_BOOLEAN: + poRetNode = new swq_expr_node( + poFeature->GetFieldAsInteger(op->field_index) ); + break; case SWQ_FLOAT: - switch( op->operation ) - { - case SWQ_EQ: - return psField->Real == op->float_value; - case SWQ_NE: - return psField->Real != op->float_value; - case SWQ_LT: - return psField->Real < op->float_value; - case SWQ_GT: - return psField->Real > op->float_value; - case SWQ_LE: - return psField->Real <= op->float_value; - case SWQ_GE: - return psField->Real >= op->float_value; - case SWQ_ISNULL: - return !poFeature->IsFieldSet( op->field_index ); - case SWQ_IN: - { - const char *pszSrc; - - pszSrc = op->string_value; - while( *pszSrc != '\0' ) - { - if( atof(pszSrc) == psField->Real ) - return TRUE; - pszSrc += strlen(pszSrc) + 1; - } - - return FALSE; - } + poRetNode = new swq_expr_node( + poFeature->GetFieldAsDouble(op->field_index) ); + break; - default: - CPLDebug( "OGRFeatureQuery", - "Illegal operation (%d) on float field.", - op->operation ); - return FALSE; - } + default: + poRetNode = new swq_expr_node( + poFeature->GetFieldAsString(op->field_index) ); + break; + } - case SWQ_STRING: - switch( op->operation ) - { - case SWQ_EQ: - if (psField->Set.nMarker1 == OGRUnsetMarker - && psField->Set.nMarker2 == OGRUnsetMarker ) - { - return (op->string_value[0] == '\0'); - } - else - { - return EQUAL(psField->String,op->string_value); - } - case SWQ_NE: - if (psField->Set.nMarker1 == OGRUnsetMarker - && psField->Set.nMarker2 == OGRUnsetMarker ) - { - return (op->string_value[0] != '\0'); - } - else - { - return !EQUAL(psField->String,op->string_value); - } + poRetNode->is_null = !(poFeature->IsFieldSet(op->field_index)); - case SWQ_LT: - if (psField->Set.nMarker1 == OGRUnsetMarker - && psField->Set.nMarker2 == OGRUnsetMarker ) - { - return (op->string_value[0] != '\0'); - } - else - { - return strcmp(psField->String,op->string_value) < 0; - } - case SWQ_GT: - if (psField->Set.nMarker1 == OGRUnsetMarker - && psField->Set.nMarker2 == OGRUnsetMarker ) - { - return (op->string_value[0] != '\0'); - } - else - { - return strcmp(psField->String,op->string_value) > 0; - } - case SWQ_LE: - if (psField->Set.nMarker1 == OGRUnsetMarker - && psField->Set.nMarker2 == OGRUnsetMarker ) - { - return (op->string_value[0] != '\0'); - } - else - { - return strcmp(psField->String,op->string_value) <= 0; - } - case SWQ_GE: - if (psField->Set.nMarker1 == OGRUnsetMarker - && psField->Set.nMarker2 == OGRUnsetMarker ) - { - return (op->string_value[0] != '\0'); - } - else - { - return strcmp(psField->String,op->string_value) >= 0; - } + return poRetNode; +} - case SWQ_ISNULL: - return !poFeature->IsFieldSet( op->field_index ); +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ - case SWQ_LIKE: - if (psField->Set.nMarker1 != OGRUnsetMarker - || psField->Set.nMarker2 != OGRUnsetMarker ) - return swq_test_like(psField->String, op->string_value); - else - return FALSE; - - case SWQ_IN: - { - const char *pszSrc; - - if( !poFeature->IsFieldSet(op->field_index) ) - return FALSE; - - pszSrc = op->string_value; - while( *pszSrc != '\0' ) - { - if( EQUAL(pszSrc,psField->String) ) - return TRUE; - pszSrc += strlen(pszSrc) + 1; - } - - return FALSE; - } +int OGRFeatureQuery::Evaluate( OGRFeature *poFeature ) - default: - CPLDebug( "OGRFeatureQuery", - "Illegal operation (%d) on string field.", - op->operation ); - return FALSE; - } +{ + if( pSWQExpr == NULL ) + return FALSE; - case SWQ_OTHER: - switch( op->operation ) - { - case SWQ_ISNULL: - return !poFeature->IsFieldSet( op->field_index ); + swq_expr_node *poResult; - default: - CPLDebug( "OGRFeatureQuery", - "Illegal operation (%d) on list or binary field.", - op->operation ); - return FALSE; - } + poResult = ((swq_expr_node *) pSWQExpr)->Evaluate( OGRFeatureFetcher, + (void *) poFeature ); - default: - assert( FALSE ); + if( poResult == NULL ) return FALSE; - } + + CPLAssert( poResult->field_type == SWQ_BOOLEAN ); + + int bLogicalResult = poResult->int_value; + + delete poResult; + + return bLogicalResult; } /************************************************************************/ -/* Evaluate() */ +/* CanUseIndex() */ /************************************************************************/ -int OGRFeatureQuery::Evaluate( OGRFeature *poFeature ) +int OGRFeatureQuery::CanUseIndex( OGRLayer *poLayer ) +{ + swq_expr_node *psExpr = (swq_expr_node *) pSWQExpr; + +/* -------------------------------------------------------------------- */ +/* Do we have an index on the targetted layer? */ +/* -------------------------------------------------------------------- */ + if ( poLayer->GetIndex() == FALSE ) + return FALSE; + + return CanUseIndex( psExpr, poLayer ); +} +int OGRFeatureQuery::CanUseIndex( swq_expr_node *psExpr, + OGRLayer *poLayer ) { - if( pSWQExpr == NULL ) + OGRAttrIndex *poIndex; + +/* -------------------------------------------------------------------- */ +/* Does the expression meet our requirements? */ +/* -------------------------------------------------------------------- */ + if( psExpr == NULL || + psExpr->eNodeType != SNT_OPERATION ) + return FALSE; + + if ((psExpr->nOperation == SWQ_OR || psExpr->nOperation == SWQ_AND) && + psExpr->nSubExprCount == 2) + { + return CanUseIndex( psExpr->papoSubExpr[0], poLayer ) && + CanUseIndex( psExpr->papoSubExpr[1], poLayer ); + } + + if( !(psExpr->nOperation == SWQ_EQ || psExpr->nOperation == SWQ_IN) + || psExpr->nSubExprCount < 2 ) + return FALSE; + + swq_expr_node *poColumn = psExpr->papoSubExpr[0]; + swq_expr_node *poValue = psExpr->papoSubExpr[1]; + + if( poColumn->eNodeType != SNT_COLUMN + || poValue->eNodeType != SNT_CONSTANT ) return FALSE; - return swq_expr_evaluate( (swq_expr *) pSWQExpr, - (swq_op_evaluator) OGRFeatureQueryEvaluator, - (void *) poFeature ); + poIndex = poLayer->GetIndex()->GetFieldIndex( poColumn->field_index ); + if( poIndex == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* OK, we have an index */ +/* -------------------------------------------------------------------- */ + return TRUE; } /************************************************************************/ @@ -426,25 +311,201 @@ int OGRFeatureQuery::Evaluate( OGRFeature *poFeature ) /* multi-part queries with ranges. */ /************************************************************************/ +static int CompareLong(const void *a, const void *b) +{ + return (*(const long *)a) - (*(const long *)b); +} + long *OGRFeatureQuery::EvaluateAgainstIndices( OGRLayer *poLayer, OGRErr *peErr ) { - swq_expr *psExpr = (swq_expr *) pSWQExpr; - OGRAttrIndex *poIndex; + swq_expr_node *psExpr = (swq_expr_node *) pSWQExpr; if( peErr != NULL ) *peErr = OGRERR_NONE; /* -------------------------------------------------------------------- */ -/* Does the expression meet our requirements? Do we have an */ -/* index on the targetted field? */ +/* Do we have an index on the targetted layer? */ /* -------------------------------------------------------------------- */ - if( psExpr == NULL || psExpr->operation != SWQ_EQ - || poLayer->GetIndex() == NULL ) + if ( poLayer->GetIndex() == NULL ) return NULL; - poIndex = poLayer->GetIndex()->GetFieldIndex( psExpr->field_index ); + int nFIDCount = 0; + return EvaluateAgainstIndices(psExpr, poLayer, nFIDCount); +} + +/* The input arrays must be sorted ! */ +static +long* OGRORLongArray(long panFIDList1[], int nFIDCount1, + long panFIDList2[], int nFIDCount2, int& nFIDCount) +{ + int nMaxCount = nFIDCount1 + nFIDCount2; + long* panFIDList = (long*) CPLMalloc((nMaxCount+1) * sizeof(long)); + nFIDCount = 0; + + int i1 = 0, i2 =0; + for(;i1eNodeType != SNT_OPERATION ) + return NULL; + + if ((psExpr->nOperation == SWQ_OR || psExpr->nOperation == SWQ_AND) && + psExpr->nSubExprCount == 2) + { + int nFIDCount1 = 0, nFIDCount2 = 0; + long* panFIDList1 = EvaluateAgainstIndices( psExpr->papoSubExpr[0], poLayer, nFIDCount1 ); + long* panFIDList2 = panFIDList1 == NULL ? NULL : + EvaluateAgainstIndices( psExpr->papoSubExpr[1], poLayer, nFIDCount2 ); + long* panFIDList = NULL; + if (panFIDList1 != NULL && panFIDList2 != NULL) + { + if (psExpr->nOperation == SWQ_OR ) + panFIDList = OGRORLongArray(panFIDList1, nFIDCount1, + panFIDList2, nFIDCount2, nFIDCount); + else if (psExpr->nOperation == SWQ_AND ) + panFIDList = OGRANDLongArray(panFIDList1, nFIDCount1, + panFIDList2, nFIDCount2, nFIDCount); + + } + CPLFree(panFIDList1); + CPLFree(panFIDList2); + return panFIDList; + } + + if( !(psExpr->nOperation == SWQ_EQ || psExpr->nOperation == SWQ_IN) + || psExpr->nSubExprCount < 2 ) + return NULL; + + swq_expr_node *poColumn = psExpr->papoSubExpr[0]; + swq_expr_node *poValue = psExpr->papoSubExpr[1]; + + if( poColumn->eNodeType != SNT_COLUMN + || poValue->eNodeType != SNT_CONSTANT ) + return NULL; + + poIndex = poLayer->GetIndex()->GetFieldIndex( poColumn->field_index ); if( poIndex == NULL ) return NULL; @@ -454,20 +515,70 @@ long *OGRFeatureQuery::EvaluateAgainstIndices( OGRLayer *poLayer, OGRField sValue; OGRFieldDefn *poFieldDefn; - poFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(psExpr->field_index); + poFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(poColumn->field_index); + +/* -------------------------------------------------------------------- */ +/* Handle the case of an IN operation. */ +/* -------------------------------------------------------------------- */ + if (psExpr->nOperation == SWQ_IN) + { + int nLength; + long *panFIDs = NULL; + int iIN; + + for( iIN = 1; iIN < psExpr->nSubExprCount; iIN++ ) + { + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + if (psExpr->papoSubExpr[iIN]->field_type == SWQ_FLOAT) + sValue.Integer = (int) psExpr->papoSubExpr[iIN]->float_value; + else + sValue.Integer = psExpr->papoSubExpr[iIN]->int_value; + break; + + case OFTReal: + sValue.Real = psExpr->papoSubExpr[iIN]->float_value; + break; + + case OFTString: + sValue.String = psExpr->papoSubExpr[iIN]->string_value; + break; + + default: + CPLAssert( FALSE ); + return NULL; + } + + panFIDs = poIndex->GetAllMatches( &sValue, panFIDs, &nFIDCount, &nLength ); + } + + if (nFIDCount > 1) + { + /* the returned FIDs are expected to be in sorted order */ + qsort(panFIDs, nFIDCount, sizeof(long), CompareLong); + } + return panFIDs; + } +/* -------------------------------------------------------------------- */ +/* Handle equality test. */ +/* -------------------------------------------------------------------- */ switch( poFieldDefn->GetType() ) { case OFTInteger: - sValue.Integer = psExpr->int_value; + if (poValue->field_type == SWQ_FLOAT) + sValue.Integer = (int) poValue->float_value; + else + sValue.Integer = poValue->int_value; break; - + case OFTReal: - sValue.Real = psExpr->float_value; + sValue.Real = poValue->float_value; break; - + case OFTString: - sValue.String = psExpr->string_value; + sValue.String = poValue->string_value; break; default: @@ -475,7 +586,14 @@ long *OGRFeatureQuery::EvaluateAgainstIndices( OGRLayer *poLayer, return NULL; } - return poIndex->GetAllMatches( &sValue ); + int nLength = 0; + long *panFIDs = poIndex->GetAllMatches( &sValue, NULL, &nFIDCount, &nLength ); + if (nFIDCount > 1) + { + /* the returned FIDs are expected to be in sorted order */ + qsort(panFIDs, nFIDCount, sizeof(long), CompareLong); + } + return panFIDs; } /************************************************************************/ @@ -489,46 +607,52 @@ char **OGRFeatureQuery::FieldCollector( void *pBareOp, char **papszList ) { - swq_field_op *op = (swq_field_op *) pBareOp; + swq_expr_node *op = (swq_expr_node *) pBareOp; /* -------------------------------------------------------------------- */ /* References to tables other than the primarily are currently */ /* unsupported. Error out. */ /* -------------------------------------------------------------------- */ - if( op->table_index != 0 ) + if( op->eNodeType == SNT_COLUMN ) { - CSLDestroy( papszList ); - return NULL; - } + if( op->table_index != 0 ) + { + CSLDestroy( papszList ); + return NULL; + } /* -------------------------------------------------------------------- */ /* Add the field name into our list if it is not already there. */ /* -------------------------------------------------------------------- */ - const char *pszFieldName; - - if( op->field_index >= poTargetDefn->GetFieldCount() - && op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT) - pszFieldName = SpecialFieldNames[op->field_index]; - else if( op->field_index >= 0 - && op->field_index < poTargetDefn->GetFieldCount() ) - pszFieldName = - poTargetDefn->GetFieldDefn(op->field_index)->GetNameRef(); - else - { - CSLDestroy( papszList ); - return NULL; + const char *pszFieldName; + + if( op->field_index >= poTargetDefn->GetFieldCount() + && op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT) + pszFieldName = SpecialFieldNames[op->field_index - poTargetDefn->GetFieldCount()]; + else if( op->field_index >= 0 + && op->field_index < poTargetDefn->GetFieldCount() ) + pszFieldName = + poTargetDefn->GetFieldDefn(op->field_index)->GetNameRef(); + else + { + CSLDestroy( papszList ); + return NULL; + } + + if( CSLFindString( papszList, pszFieldName ) == -1 ) + papszList = CSLAddString( papszList, pszFieldName ); } - if( CSLFindString( papszList, pszFieldName ) == -1 ) - papszList = CSLAddString( papszList, pszFieldName ); - /* -------------------------------------------------------------------- */ /* Add in fields from subexpressions. */ /* -------------------------------------------------------------------- */ - if( op->first_sub_expr != NULL ) - papszList = FieldCollector( op->first_sub_expr, papszList ); - if( op->second_sub_expr != NULL ) - papszList = FieldCollector( op->second_sub_expr, papszList ); + if( op->eNodeType == SNT_OPERATION ) + { + for( int iSubExpr = 0; iSubExpr < op->nSubExprCount; iSubExpr++ ) + { + papszList = FieldCollector( op->papoSubExpr[iSubExpr], papszList ); + } + } return papszList; } diff --git a/ogr/ogrfeaturestyle.cpp b/ogr/ogrfeaturestyle.cpp index 98d3f5b..50468ad 100644 --- a/ogr/ogrfeaturestyle.cpp +++ b/ogr/ogrfeaturestyle.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrfeaturestyle.cpp 18572 2010-01-17 14:21:31Z rouault $ + * $Id: ogrfeaturestyle.cpp 27071 2014-03-21 21:52:46Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Feature Representation string API @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 2000-2001, Stephane Villeneuve + * Copyright (c) 2008-2010, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,7 +34,7 @@ #include "ogr_featurestyle.h" #include "ogr_api.h" -CPL_CVSID("$Id: ogrfeaturestyle.cpp 18572 2010-01-17 14:21:31Z rouault $"); +CPL_CVSID("$Id: ogrfeaturestyle.cpp 27071 2014-03-21 21:52:46Z rouault $"); CPL_C_START void OGRFeatureStylePuller() {} @@ -48,7 +49,7 @@ static const OGRStyleParamId asStylePen[] = { {OGRSTPenColor,"c",FALSE,OGRSTypeString}, {OGRSTPenWidth,"w",TRUE,OGRSTypeDouble}, - {OGRSTPenPattern,"p",TRUE,OGRSTypeString}, + {OGRSTPenPattern,"p",FALSE,OGRSTypeString}, // georefed,but multiple times. {OGRSTPenId,"id",FALSE,OGRSTypeString}, {OGRSTPenPerOffset,"dp",TRUE,OGRSTypeDouble}, {OGRSTPenCap,"cap",FALSE,OGRSTypeString}, @@ -376,6 +377,15 @@ const char *OGRStyleMgr::GetStyleName(const char *pszStyleString) /* const char *OGRStyleMgr::GetStyleByName(const char *pszStyleName) */ /* */ /****************************************************************************/ + +/** + * \brief find a style in the current style table. + * + * + * @param pszStyleName the name of the style to add. + * + * @return the style string matching the name or NULL if not found or error. + */ const char *OGRStyleMgr::GetStyleByName(const char *pszStyleName) { if (m_poDataSetStyleTable) @@ -390,6 +400,19 @@ const char *OGRStyleMgr::GetStyleByName(const char *pszStyleName) /* char *pszStyleString) */ /* */ /****************************************************************************/ + +/** + * \brief Add a style to the current style table. + * + * This method is the same as the C function OGR_SM_AddStyle(). + * + * @param pszStyleName the name of the style to add. + * @param pszStyleString the style string to use, or NULL to use the style + * stored in the manager. + * + * @return TRUE on success, FALSE on errors. + */ + GBool OGRStyleMgr::AddStyle(const char *pszStyleName, const char *pszStyleString) { @@ -413,7 +436,7 @@ GBool OGRStyleMgr::AddStyle(const char *pszStyleName, /************************************************************************/ /** - * Add a style to the current style table. + * \brief Add a style to the current style table. * * This function is the same as the C++ method OGRStyleMgr::AddStyle(). * @@ -440,6 +463,19 @@ int OGR_SM_AddStyle(OGRStyleMgrH hSM, const char *pszStyleName, /* */ /****************************************************************************/ +/** + * \brief Get the style string from the style manager. + * + * @param poFeature feature object from which to read the style or NULL to + * get the style string stored in the manager. + * + * @return the style string stored in the feature or the style string stored + * in the style manager if poFeature is NULL + * + * NOTE: this method will call OGRStyleMgr::InitFromFeature() if poFeature is + * not NULL and replace the style string stored in the style manager + */ + const char *OGRStyleMgr::GetStyleString(OGRFeature *poFeature) { if (poFeature == NULL) @@ -448,6 +484,19 @@ const char *OGRStyleMgr::GetStyleString(OGRFeature *poFeature) return InitFromFeature(poFeature); } +/****************************************************************************/ +/* GBool OGRStyleMgr::AddPart(const char *pszPart) */ +/* Add a new part in the current style */ +/****************************************************************************/ + +/** + * \brief Add a part (style string) to the current style. + * + * @param pszPart the style string defining the part to add. + * + * @return TRUE on success, FALSE on errors. + */ + GBool OGRStyleMgr::AddPart(const char *pszPart) { char *pszTmp; @@ -490,7 +539,7 @@ GBool OGRStyleMgr::AddPart(const char *pszPart) GBool OGRStyleMgr::AddPart(OGRStyleTool *poStyleTool) { char *pszTmp; - if (poStyleTool) + if (poStyleTool && poStyleTool->GetStyleString()) { if (m_pszStyleString) { @@ -618,6 +667,8 @@ int OGR_SM_GetPartCount(OGRStyleMgrH hSM, const char *pszStyleString) * * This method is the same as the C function OGR_SM_GetPart(). * + * This method instanciates a new object that should be freed with OGR_ST_Destroy(). + * * @param nPartId the part number (0-based index). * @param pszStyleString (optional) the style string on which to operate. * If NULL then the current style string stored in the style manager is used. @@ -648,7 +699,7 @@ OGRStyleTool *OGRStyleMgr::GetPart(int nPartId, pszString = CSLGetField( papszStyleString, nPartId ); - if ( pszString || strlen(pszString) > 0 ) + if ( strlen(pszString) > 0 ) { poStyleTool = CreateStyleToolFromStyleString(pszString); if ( poStyleTool ) @@ -669,6 +720,8 @@ OGRStyleTool *OGRStyleMgr::GetPart(int nPartId, * * This function is the same as the C++ method OGRStyleMgr::GetPart(). * + * This function instanciates a new object that should be freed with OGR_ST_Destroy(). + * * @param hSM handle to the style manager. * @param nPartId the part number (0-based index). * @param pszStyleString (optional) the style string on which to operate. @@ -733,6 +786,7 @@ OGRStyleTool *OGRStyleMgr::CreateStyleToolFromStyleString(const char * OGRStyleTable::OGRStyleTable() { m_papszStyleTable = NULL; + iNextStyle = 0; } /************************************************************************/ @@ -871,6 +925,31 @@ GBool OGRStyleTable::AddStyle(const char *pszName, const char *pszStyleString) return FALSE; } +/************************************************************************/ +/* OGR_STBL_AddStyle() */ +/************************************************************************/ + +/** + * \brief Add a new style in the table. + * No comparison will be done on the + * Style string, only on the name. + * This function is the same as the C++ method OGRStyleTable::AddStyle(). + * + * @param hStyleTable handle to the style table. + * @param pszName the name the style to add. + * @param pszStyleString the style string to add. + * + * @return TRUE on success, FALSE on error + */ + +int OGR_STBL_AddStyle( OGRStyleTableH hStyleTable, + const char *pszName, const char *pszStyleString) +{ + VALIDATE_POINTER1( hStyleTable, "OGR_STBL_AddStyle", FALSE ); + + return ((OGRStyleTable *) hStyleTable)->AddStyle( pszName, pszStyleString ); +} + /****************************************************************************/ /* GBool OGRStyleTable::RemoveStyle(char *pszName) */ /* */ @@ -1729,9 +1808,12 @@ GBool OGRStyleTool::Parse(const OGRStyleParamId *pasStyle, for ( i = 0; i < nElements; i++ ) { char **papszStylePair = - CSLTokenizeString2( papszToken2[i], ":", CSLT_HONOURSTRINGS - | CSLT_STRIPLEADSPACES - | CSLT_STRIPENDSPACES ); + CSLTokenizeString2( papszToken2[i], ":", + CSLT_HONOURSTRINGS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES + | CSLT_ALLOWEMPTYTOKENS ); + int j, nTokens = CSLCount(papszStylePair); if ( nTokens < 1 || nTokens > 2 ) diff --git a/ogr/ogrfielddefn.cpp b/ogr/ogrfielddefn.cpp index 977cae0..19de781 100644 --- a/ogr/ogrfielddefn.cpp +++ b/ogr/ogrfielddefn.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrfielddefn.cpp 16574 2009-03-14 13:09:10Z rouault $ + * $Id: ogrfielddefn.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRFieldDefn class implementation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2009-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,7 +32,7 @@ #include "ogr_api.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrfielddefn.cpp 16574 2009-03-14 13:09:10Z rouault $"); +CPL_CVSID("$Id: ogrfielddefn.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRFieldDefn() */ @@ -107,6 +108,7 @@ void OGRFieldDefn::Initialize( const char * pszNameIn, OGRFieldType eTypeIn ) nPrecision = 0; // for numbers? memset( &uDefault, 0, sizeof(OGRField) ); + bIgnore = FALSE; } /************************************************************************/ @@ -640,3 +642,86 @@ void OGR_Fld_Set( OGRFieldDefnH hDefn, const char *pszNameIn, ((OGRFieldDefn *) hDefn)->Set( pszNameIn, eTypeIn, nWidthIn, nPrecisionIn, eJustifyIn ); } + +/************************************************************************/ +/* IsIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRFieldDefn::IsIgnored(); + * + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_Fld_IsIgnored(). + * + * @return ignore state + */ + +/************************************************************************/ +/* OGR_Fld_IsIgnored() */ +/************************************************************************/ + +/** + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRFieldDefn::IsIgnored(). + * + * @param hDefn handle to the field definition + * @return ignore state + */ + +int OGR_Fld_IsIgnored( OGRFieldDefnH hDefn ) +{ + return ((OGRFieldDefn *) hDefn)->IsIgnored(); +} + +/************************************************************************/ +/* SetIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRFieldDefn::SetIgnored( int ignore ); + * + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_Fld_SetIgnored(). + * + * @param ignore ignore state + */ + +/************************************************************************/ +/* OGR_Fld_SetIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRFieldDefn::SetIgnored(). + * + * @param hDefn handle to the field definition + * @param ignore ignore state + */ + +void OGR_Fld_SetIgnored( OGRFieldDefnH hDefn, int ignore ) +{ + ((OGRFieldDefn *) hDefn)->SetIgnored( ignore ); +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the field definition is identical to the other one. + * + * @param poOtherFieldDefn the other field definition to compare to. + * @return TRUE if the field definition is identical to the other one. + */ + +int OGRFieldDefn::IsSame( const OGRFieldDefn * poOtherFieldDefn ) const +{ + return (strcmp(pszName, poOtherFieldDefn->pszName) == 0 && + eType == poOtherFieldDefn->eType && + nWidth == poOtherFieldDefn->nWidth && + nPrecision == poOtherFieldDefn->nPrecision); +} diff --git a/ogr/ogrgeometry.cpp b/ogr/ogrgeometry.cpp index 942172a..632bf8e 100644 --- a/ogr/ogrgeometry.cpp +++ b/ogr/ogrgeometry.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrgeometry.cpp 17010 2009-05-13 20:33:01Z warmerdam $ + * $Id: ogrgeometry.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Implements a few base methods on OGRGeometry. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -34,12 +35,12 @@ #include "cpl_multiproc.h" #include -CPL_CVSID("$Id: ogrgeometry.cpp 17010 2009-05-13 20:33:01Z warmerdam $"); +CPL_CVSID("$Id: ogrgeometry.cpp 27044 2014-03-16 23:41:27Z rouault $"); int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE; #ifdef HAVE_GEOS -static void _GEOSErrorHandler(const char *fmt, ...) +static void OGRGEOSErrorHandler(const char *fmt, ...) { va_list args; @@ -48,7 +49,7 @@ static void _GEOSErrorHandler(const char *fmt, ...) va_end(args); } -static void _GEOSWarningHandler(const char *fmt, ...) +static void OGRGEOSWarningHandler(const char *fmt, ...) { va_list args; @@ -80,7 +81,6 @@ OGRGeometry::~OGRGeometry() poSRS->Release(); } - /************************************************************************/ /* dumpReadable() */ /************************************************************************/ @@ -126,9 +126,9 @@ void OGRGeometry::dumpReadable( FILE * fp, const char * pszPrefix, char** papszO { case wkbUnknown: case wkbNone: - break; case wkbPoint: case wkbPoint25D: + fprintf( fp, "\n"); break; case wkbLineString: case wkbLineString25D: @@ -143,18 +143,23 @@ void OGRGeometry::dumpReadable( FILE * fp, const char * pszPrefix, char** papszO poPoly = (OGRPolygon*)this; poRing = poPoly->getExteriorRing(); nRings = poPoly->getNumInteriorRings(); - fprintf( fp, "%d points", poRing->getNumPoints() ); - if (nRings) + if (poRing == NULL) + fprintf( fp, "empty"); + else { - fprintf( fp, ", %d inner rings (", nRings); - for( ir = 0; ir < nRings; ir++) + fprintf( fp, "%d points", poRing->getNumPoints() ); + if (nRings) { - if (ir) - fprintf( fp, ", "); - fprintf( fp, "%d points", - poPoly->getInteriorRing(ir)->getNumPoints() ); + fprintf( fp, ", %d inner rings (", nRings); + for( ir = 0; ir < nRings; ir++) + { + if (ir) + fprintf( fp, ", "); + fprintf( fp, "%d points", + poPoly->getInteriorRing(ir)->getNumPoints() ); + } + fprintf( fp, ")"); } - fprintf( fp, ")"); } fprintf( fp, "\n"); break; @@ -330,27 +335,24 @@ OGRBoolean OGRGeometry::Intersects( OGRGeometry *poOtherGeom ) const GEOSGeom hThisGeosGeom = NULL; GEOSGeom hOtherGeosGeom = NULL; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + OGRBoolean bResult = FALSE; if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - OGRBoolean bResult; - - if( GEOSIntersects( hThisGeosGeom, hOtherGeosGeom ) != 0 ) + if( GEOSIntersects_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ) != 0 ) bResult = TRUE; else bResult = FALSE; - - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); - - return bResult; - } - else - { - return TRUE; } + + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; #endif /* HAVE_GEOS */ } @@ -565,6 +567,15 @@ OGRErr OGR_G_Transform( OGRGeometryH hGeom, * @return 0 for points, 1 for lines and 2 for surfaces. */ +int OGRGeometry::getIsoGeometryType() const +{ + int nGType = wkbFlatten(getGeometryType()); + + if ( getCoordinateDimension() == 3 ) + nGType += 1000; + + return nGType; +} /************************************************************************/ /* OGRGeometry::segmentize() */ @@ -652,8 +663,8 @@ int OGR_G_GetDimension( OGRGeometryH hGeom ) * * This method is the same as the C function OGR_G_GetCoordinateDimension(). * - * @return in practice this always returns 2 indicating that coordinates are - * specified within a two dimensional space. + * @return in practice this will return 2 or 3. It can also return 0 in the + * case of an empty point. */ int OGRGeometry::getCoordinateDimension() const @@ -676,8 +687,9 @@ int OGRGeometry::getCoordinateDimension() const * * @param hGeom handle on the geometry to get the dimension of the * coordinates from. - * @return in practice this always returns 2 indicating that coordinates are - * specified within a two dimensional space. + * + * @return in practice this will return 2 or 3. It can also return 0 in the + * case of an empty point. */ int OGR_G_GetCoordinateDimension( OGRGeometryH hGeom ) @@ -713,6 +725,19 @@ void OGRGeometry::setCoordinateDimension( int nNewDimension ) /* OGR_G_SetCoordinateDimension() */ /************************************************************************/ +/** + * \brief Set the coordinate dimension. + * + * This method sets the explicit coordinate dimension. Setting the coordinate + * dimension of a geometry to 2 should zero out any existing Z values. Setting + * the dimension of a geometry collection will not necessarily affect the + * children geometries. + * + * @param hGeom handle on the geometry to set the dimension of the + * coordinates. + * @param nNewDimension New coordinate dimension value, either 2 or 3. + */ + void OGR_G_SetCoordinateDimension( OGRGeometryH hGeom, int nNewDimension) { @@ -860,6 +885,40 @@ void OGR_G_GetEnvelope( OGRGeometryH hGeom, OGREnvelope *psEnvelope ) ((OGRGeometry *) hGeom)->getEnvelope( psEnvelope ); } +/** + * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const; + * + * \brief Computes and returns the bounding envelope (3D) for this geometry in the passed psEnvelope structure. + * + * This method is the same as the C function OGR_G_GetEnvelope3D(). + * + * @param psEnvelope the structure in which to place the results. + * + * @since OGR 1.9.0 + */ + +/************************************************************************/ +/* OGR_G_GetEnvelope3D() */ +/************************************************************************/ +/** + * \brief Computes and returns the bounding envelope (3D) for this geometry in the passed psEnvelope structure. + * + * This function is the same as the CPP method OGRGeometry::getEnvelope(). + * + * @param hGeom handle of the geometry to get envelope from. + * @param psEnvelope the structure in which to place the results. + * + * @since OGR 1.9.0 + */ + +void OGR_G_GetEnvelope3D( OGRGeometryH hGeom, OGREnvelope3D *psEnvelope ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_GetEnvelope3D" ); + + ((OGRGeometry *) hGeom)->getEnvelope( psEnvelope ); +} + /** * \fn OGRErr OGRGeometry::importFromWkb( unsigned char * pabyData, int nSize); * @@ -915,7 +974,8 @@ OGRErr OGR_G_ImportFromWkb( OGRGeometryH hGeom, /** * \fn OGRErr OGRGeometry::exportToWkb( OGRwkbByteOrder eByteOrder, - unsigned char * pabyData ) const; + unsigned char * pabyData, + OGRwkbVariant eWkbVariant=wkbVariantOgc ) const * * \brief Convert a geometry into well known binary format. * @@ -928,6 +988,11 @@ OGRErr OGR_G_ImportFromWkb( OGRGeometryH hGeom, * @param pabyData a buffer into which the binary representation is * written. This buffer must be at least * OGRGeometry::WkbSize() byte in size. + * @param eWkbVariant What standard to use when exporting geometries with + * three dimensions (or more). The default wkbVariantOgc is + * the historical OGR variant. wkbVariantIso is the + * variant defined in ISO SQL/MM and adopted by OGC + * for SFSQL 1.2. * * @return Currently OGRERR_NONE is always returned. */ @@ -1024,7 +1089,8 @@ OGRErr OGR_G_ImportFromWkt( OGRGeometryH hGeom, char ** ppszSrcText ) * This method is the same as the C function OGR_G_ExportToWkt(). * * @param ppszDstText a text buffer is allocated by the program, and assigned - * to the passed pointer. + * to the passed pointer. After use, *ppszDstText should be + * freed with OGRFree(). * * @return Currently OGRERR_NONE is always returned. */ @@ -1041,7 +1107,8 @@ OGRErr OGR_G_ImportFromWkt( OGRGeometryH hGeom, char ** ppszSrcText ) * * @param hGeom handle on the geometry to convert to a text format from. * @param ppszSrcText a text buffer is allocated by the program, and assigned - to the passed pointer. + * to the passed pointer. After use, *ppszDstText should be + * freed with OGRFree(). * * @return Currently OGRERR_NONE is always returned. */ @@ -1303,13 +1370,15 @@ OGRGeometry::IsValid( ) const OGRBoolean bResult = FALSE; GEOSGeom hThisGeosGeom = NULL; - hThisGeosGeom = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL ) { - bResult = GEOSisValid( hThisGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); + bResult = GEOSisValid_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); } + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -1374,13 +1443,15 @@ OGRGeometry::IsSimple( ) const OGRBoolean bResult = FALSE; GEOSGeom hThisGeosGeom = NULL; - hThisGeosGeom = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL ) { - bResult = GEOSisSimple( hThisGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); + bResult = GEOSisSimple_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); } + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -1445,13 +1516,15 @@ OGRGeometry::IsRing( ) const OGRBoolean bResult = FALSE; GEOSGeom hThisGeosGeom = NULL; - hThisGeosGeom = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL ) { - bResult = GEOSisRing( hThisGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); + bResult = GEOSisRing_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); } + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -1485,6 +1558,72 @@ int OGR_G_IsRing( OGRGeometryH hGeom ) return ((OGRGeometry *) hGeom)->IsRing(); } +/************************************************************************/ +/* OGRFromOGCGeomType() */ +/* Map OGCgeometry format type to corresponding */ +/* OGR constants. */ +/************************************************************************/ + +#define EQUALN_CST(var, cst) EQUALN(var, cst, strlen(cst)) + +OGRwkbGeometryType OGRFromOGCGeomType( const char *pszGeomType ) +{ + unsigned int n25DBit = 0; + if( *pszGeomType != '\0' ) + { + char ch = pszGeomType[strlen(pszGeomType)-1]; + if( ch == 'z' || ch == 'Z' ) + n25DBit = wkb25DBit; + } + if ( EQUALN_CST(pszGeomType, "POINT") ) + return (OGRwkbGeometryType)(wkbPoint | n25DBit); + else if ( EQUALN_CST(pszGeomType, "LINESTRING") ) + return (OGRwkbGeometryType)(wkbLineString | n25DBit); + else if ( EQUALN_CST(pszGeomType, "POLYGON") ) + return (OGRwkbGeometryType)(wkbPolygon | n25DBit); + else if ( EQUALN_CST(pszGeomType, "MULTIPOINT") ) + return (OGRwkbGeometryType)(wkbMultiPoint | n25DBit); + else if ( EQUALN_CST(pszGeomType, "MULTILINESTRING") ) + return (OGRwkbGeometryType)(wkbMultiLineString | n25DBit); + else if ( EQUALN_CST(pszGeomType, "MULTIPOLYGON") ) + return (OGRwkbGeometryType)(wkbMultiPolygon | n25DBit); + else if ( EQUALN_CST(pszGeomType, "GEOMETRYCOLLECTION") ) + return (OGRwkbGeometryType)(wkbGeometryCollection | n25DBit); + else + return (OGRwkbGeometryType)(wkbUnknown | n25DBit); +} + +/************************************************************************/ +/* OGRToOGCGeomType() */ +/* Map OGR geometry format constants to corresponding */ +/* OGC geometry type */ +/************************************************************************/ + +const char * OGRToOGCGeomType( OGRwkbGeometryType eGeomType ) +{ + switch ( wkbFlatten(eGeomType) ) + { + case wkbUnknown: + return "GEOMETRY"; + case wkbPoint: + return "POINT"; + case wkbLineString: + return "LINESTRING"; + case wkbPolygon: + return "POLYGON"; + case wkbMultiPoint: + return "MULTIPOINT"; + case wkbMultiLineString: + return "MULTILINESTRING"; + case wkbMultiPolygon: + return "MULTIPOLYGON"; + case wkbGeometryCollection: + return "GEOMETRYCOLLECTION"; + default: + return ""; + } +} + /************************************************************************/ /* OGRGeometryTypeToName() */ /************************************************************************/ @@ -1503,52 +1642,57 @@ int OGR_G_IsRing( OGRGeometryH hGeom ) const char *OGRGeometryTypeToName( OGRwkbGeometryType eType ) { - switch( eType ) + bool b2D = wkbFlatten(eType) == eType; + + switch( wkbFlatten(eType) ) { case wkbUnknown: - return "Unknown (any)"; - - case wkbPoint: - return "Point"; + if( b2D ) + return "Unknown (any)"; + else + return "3D Unknown (any)"; - case wkbPoint25D: - return "3D Point"; + case wkbPoint: + if( b2D ) + return "Point"; + else + return "3D Point"; case wkbLineString: - return "Line String"; - - case wkbLineString25D: - return "3D Line String"; + if( b2D ) + return "Line String"; + else + return "3D Line String"; case wkbPolygon: - return "Polygon"; - - case wkbPolygon25D: - return "3D Polygon"; + if( b2D ) + return "Polygon"; + else + return "3D Polygon"; case wkbMultiPoint: - return "Multi Point"; - - case wkbMultiPoint25D: - return "3D Multi Point"; + if( b2D ) + return "Multi Point"; + else + return "3D Multi Point"; case wkbMultiLineString: - return "Multi Line String"; - - case wkbMultiLineString25D: - return "3D Multi Line String"; + if( b2D ) + return "Multi Line String"; + else + return "3D Multi Line String"; case wkbMultiPolygon: - return "Multi Polygon"; - - case wkbMultiPolygon25D: - return "3D Multi Polygon"; + if( b2D ) + return "Multi Polygon"; + else + return "3D Multi Polygon"; case wkbGeometryCollection: - return "Geometry Collection"; - - case wkbGeometryCollection25D: - return "3D Geometry Collection"; + if( b2D ) + return "Geometry Collection"; + else + return "3D Geometry Collection"; case wkbNone: return "None"; @@ -1665,7 +1809,7 @@ void OGR_G_FlattenTo2D( OGRGeometryH hGeom ) /************************************************************************/ /** - * \fn char *OGRGeometry::exportToGML() const; + * \fn char *OGRGeometry::exportToGML( const char* const * papszOptions = NULL ) const; * * \brief Convert a geometry into GML format. * @@ -1673,14 +1817,27 @@ void OGR_G_FlattenTo2D( OGRGeometryH hGeom ) * types assuming the this is available in the gml namespace. The returned * string should be freed with CPLFree() when no longer required. * - * This method is the same as the C function OGR_G_ExportToGML(). + * The supported options in OGR 1.8.0 are : + *
      + *
    • FORMAT=GML3. Otherwise it will default to GML 2.1.2 output. + *
    • GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3) To use gml:Curve element for linestrings. + * Otherwise gml:LineString will be used . + *
    • GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3) Default to YES. If YES, SRS with EPSG authority will + * be written with the "urn:ogc:def:crs:EPSG::" prefix. + * In the case, if the SRS is a geographic SRS without explicit AXIS order, but that the same SRS authority code + * imported with ImportFromEPSGA() should be treated as lat/long, then the function will take care of coordinate order swapping. + * If set to NO, SRS with EPSG authority will be written with the "EPSG:" prefix, even if they are in lat/long order. + *
    + * + * This method is the same as the C function OGR_G_ExportToGMLEx(). * + * @param papszOptions NULL-terminated list of options. * @return A GML fragment or NULL in case of error. */ -char *OGRGeometry::exportToGML() const +char *OGRGeometry::exportToGML( const char* const * papszOptions ) const { - return OGR_G_ExportToGML( (OGRGeometryH) this ); + return OGR_G_ExportToGMLEx( (OGRGeometryH) this, (char**)papszOptions ); } /************************************************************************/ @@ -1785,11 +1942,40 @@ int OGRGetGenerate_DB2_V72_BYTE_ORDER() return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER; } +/************************************************************************/ +/* createGEOSContext() */ +/************************************************************************/ + +GEOSContextHandle_t OGRGeometry::createGEOSContext() +{ +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; +#else + return initGEOS_r( OGRGEOSWarningHandler, OGRGEOSErrorHandler ); +#endif +} + +/************************************************************************/ +/* freeGEOSContext() */ +/************************************************************************/ + +void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt) +{ +#ifdef HAVE_GEOS + if( hGEOSCtxt != NULL ) + { + finishGEOS_r( hGEOSCtxt ); + } +#endif +} + /************************************************************************/ /* exportToGEOS() */ /************************************************************************/ -GEOSGeom OGRGeometry::exportToGEOS() const +GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt) const { #ifndef HAVE_GEOS @@ -1800,15 +1986,15 @@ GEOSGeom OGRGeometry::exportToGEOS() const #else - static void *hGEOSInitMutex = NULL; - static int bGEOSInitialized = FALSE; + if( hGEOSCtxt == NULL ) + return NULL; - CPLMutexHolderD( &hGEOSInitMutex ); - - if( !bGEOSInitialized ) + /* POINT EMPTY is exported to WKB as if it were POINT(0 0) */ + /* so that particular case is necessary */ + if (wkbFlatten(getGeometryType()) == wkbPoint && + nCoordDimension == 0) { - bGEOSInitialized = TRUE; - initGEOS( _GEOSWarningHandler, _GEOSErrorHandler ); + return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY"); } GEOSGeom hGeom = NULL; @@ -1818,7 +2004,7 @@ GEOSGeom OGRGeometry::exportToGEOS() const nDataSize = WkbSize(); pabyData = (unsigned char *) CPLMalloc(nDataSize); if( exportToWkb( wkbNDR, pabyData ) == OGRERR_NONE ) - hGeom = GEOSGeomFromWKB_buf( pabyData, nDataSize ); + hGeom = GEOSGeomFromWKB_buf_r( hGEOSCtxt, pabyData, nDataSize ); CPLFree( pabyData ); @@ -1835,7 +2021,8 @@ GEOSGeom OGRGeometry::exportToGEOS() const /** * \brief Compute distance between two geometries. * - * Returns the shortest distance between the two geometries. + * Returns the shortest distance between the two geometries. The distance is + * expressed into the same unit as the coordinates of the geometries. * * This method is the same as the C function OGR_G_Distance(). * @@ -1870,19 +2057,21 @@ double OGRGeometry::Distance( const OGRGeometry *poOtherGeom ) const GEOSGeom hThis = NULL; GEOSGeom hOther = NULL; - hOther = poOtherGeom->exportToGEOS(); - hThis = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hOther = poOtherGeom->exportToGEOS(hGEOSCtxt); + hThis = exportToGEOS(hGEOSCtxt); int bIsErr = 0; double dfDistance = 0.0; if( hThis != NULL && hOther != NULL ) { - bIsErr = GEOSDistance( hThis, hOther, &dfDistance ); + bIsErr = GEOSDistance_r( hGEOSCtxt, hThis, hOther, &dfDistance ); } - GEOSGeom_destroy( hThis ); - GEOSGeom_destroy( hOther ); + GEOSGeom_destroy_r( hGEOSCtxt, hThis ); + GEOSGeom_destroy_r( hGEOSCtxt, hOther ); + freeGEOSContext( hGEOSCtxt ); if ( bIsErr > 0 ) { @@ -1901,7 +2090,8 @@ double OGRGeometry::Distance( const OGRGeometry *poOtherGeom ) const /** * \brief Compute distance between two geometries. * - * Returns the shortest distance between the two geometries. + * Returns the shortest distance between the two geometries. The distance is + * expressed into the same unit as the coordinates of the geometries. * * This function is the same as the C++ method OGRGeometry::Distance(). * @@ -1960,18 +2150,22 @@ OGRGeometry *OGRGeometry::ConvexHull() const GEOSGeom hGeosHull = NULL; OGRGeometry *poHullOGRGeom = NULL; - hGeosGeom = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hGeosGeom = exportToGEOS(hGEOSCtxt); if( hGeosGeom != NULL ) { - hGeosHull = GEOSConvexHull( hGeosGeom ); - GEOSGeom_destroy( hGeosGeom ); + hGeosHull = GEOSConvexHull_r( hGEOSCtxt, hGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); if( hGeosHull != NULL ) { - poHullOGRGeom = OGRGeometryFactory::createFromGEOS(hGeosHull); - GEOSGeom_destroy( hGeosHull); + poHullOGRGeom = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosHull); + if( poHullOGRGeom != NULL && getSpatialReference() != NULL ) + poHullOGRGeom->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosHull); } } + freeGEOSContext( hGEOSCtxt ); return poHullOGRGeom; @@ -2009,7 +2203,7 @@ OGRGeometryH OGR_G_ConvexHull( OGRGeometryH hTarget ) } /************************************************************************/ -/* getBoundary() */ +/* Boundary() */ /************************************************************************/ /** @@ -2018,7 +2212,7 @@ OGRGeometryH OGR_G_ConvexHull( OGRGeometryH hTarget ) * A new geometry object is created and returned containing the boundary * of the geometry on which the method is invoked. * - * This method is the same as the C function OGR_G_GetBoundary(). + * This method is the same as the C function OGR_G_Boundary(). * * This method is built on the GEOS library, check it for the definition * of the geometry operation. @@ -2026,9 +2220,11 @@ OGRGeometryH OGR_G_ConvexHull( OGRGeometryH hTarget ) * issuing a CPLE_NotSupported error. * * @return a newly allocated geometry now owned by the caller, or NULL on failure. + * + * @since OGR 1.8.0 */ -OGRGeometry *OGRGeometry::getBoundary() const +OGRGeometry *OGRGeometry::Boundary() const { #ifndef HAVE_GEOS @@ -2043,26 +2239,44 @@ OGRGeometry *OGRGeometry::getBoundary() const GEOSGeom hGeosProduct = NULL; OGRGeometry *poOGRProduct = NULL; - hGeosGeom = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hGeosGeom = exportToGEOS(hGEOSCtxt); if( hGeosGeom != NULL ) { - hGeosProduct = GEOSBoundary( hGeosGeom ); - GEOSGeom_destroy( hGeosGeom ); + hGeosProduct = GEOSBoundary_r( hGEOSCtxt, hGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); if( hGeosProduct != NULL ) { - poOGRProduct = OGRGeometryFactory::createFromGEOS(hGeosProduct); - GEOSGeom_destroy( hGeosProduct ); + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); } } + freeGEOSContext( hGEOSCtxt ); return poOGRProduct; #endif /* HAVE_GEOS */ } + +/** + * \brief Compute boundary (deprecated) + * + * @deprecated + * + * @see Boundary() + */ +OGRGeometry *OGRGeometry::getBoundary() const + +{ + return Boundary(); +} + /************************************************************************/ -/* OGR_G_GetBoundary() */ +/* OGR_G_Boundary() */ /************************************************************************/ /** * \brief Compute boundary. @@ -2070,7 +2284,7 @@ OGRGeometry *OGRGeometry::getBoundary() const * A new geometry object is created and returned containing the boundary * of the geometry on which the method is invoked. * - * This function is the same as the C++ method OGR_G_GetBoundary(). + * This function is the same as the C++ method OGR_G_Boundary(). * * This function is built on the GEOS library, check it for the definition * of the geometry operation. @@ -2081,16 +2295,32 @@ OGRGeometry *OGRGeometry::getBoundary() const * * @return a handle to a newly allocated geometry now owned by the caller, * or NULL on failure. + * + * @since OGR 1.8.0 + */ +OGRGeometryH OGR_G_Boundary( OGRGeometryH hTarget ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_Boundary", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Boundary(); +} + +/** + * \brief Compute boundary (deprecated) + * + * @deprecated + * + * @see OGR_G_Boundary() */ OGRGeometryH OGR_G_GetBoundary( OGRGeometryH hTarget ) { VALIDATE_POINTER1( hTarget, "OGR_G_GetBoundary", NULL ); - return (OGRGeometryH) ((OGRGeometry *) hTarget)->getBoundary(); + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Boundary(); } - /************************************************************************/ /* Buffer() */ /************************************************************************/ @@ -2116,7 +2346,8 @@ OGRGeometryH OGR_G_GetBoundary( OGRGeometryH hTarget ) * If OGR is built without the GEOS library, this method will always fail, * issuing a CPLE_NotSupported error. * - * @param dfDist the buffer distance to be applied. + * @param dfDist the buffer distance to be applied. Should be expressed into + * the same unit as the coordinates of the geometry. * * @param nQuadSegs the number of segments used to approximate a 90 degree (quadrant) of * curvature. @@ -2139,18 +2370,22 @@ OGRGeometry *OGRGeometry::Buffer( double dfDist, int nQuadSegs ) const GEOSGeom hGeosProduct = NULL; OGRGeometry *poOGRProduct = NULL; - hGeosGeom = exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hGeosGeom = exportToGEOS(hGEOSCtxt); if( hGeosGeom != NULL ) { - hGeosProduct = GEOSBuffer( hGeosGeom, dfDist, nQuadSegs ); - GEOSGeom_destroy( hGeosGeom ); + hGeosProduct = GEOSBuffer_r( hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs ); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); if( hGeosProduct != NULL ) { - poOGRProduct = OGRGeometryFactory::createFromGEOS(hGeosProduct); - GEOSGeom_destroy( hGeosProduct ); + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); } } + freeGEOSContext(hGEOSCtxt); return poOGRProduct; @@ -2183,7 +2418,8 @@ OGRGeometry *OGRGeometry::Buffer( double dfDist, int nQuadSegs ) const * issuing a CPLE_NotSupported error. * * @param hTarget the geometry. - * @param dfDist the buffer distance to be applied. + * @param dfDist the buffer distance to be applied. Should be expressed into + * the same unit as the coordinates of the geometry. * * @param nQuadSegs the number of segments used to approximate a 90 degree * (quadrant) of curvature. @@ -2239,20 +2475,28 @@ OGRGeometry *OGRGeometry::Intersection( const OGRGeometry *poOtherGeom ) const GEOSGeom hGeosProduct = NULL; OGRGeometry *poOGRProduct = NULL; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - hGeosProduct = GEOSIntersection( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + hGeosProduct = GEOSIntersection_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); if( hGeosProduct != NULL ) { - poOGRProduct = OGRGeometryFactory::createFromGEOS(hGeosProduct); - GEOSGeom_destroy( hGeosProduct ); + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); } } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return poOGRProduct; @@ -2331,20 +2575,28 @@ OGRGeometry *OGRGeometry::Union( const OGRGeometry *poOtherGeom ) const GEOSGeom hGeosProduct = NULL; OGRGeometry *poOGRProduct = NULL; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - hGeosProduct = GEOSUnion( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + hGeosProduct = GEOSUnion_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); if( hGeosProduct != NULL ) { - poOGRProduct = OGRGeometryFactory::createFromGEOS(hGeosProduct); - GEOSGeom_destroy( hGeosProduct ); + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); } } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return poOGRProduct; @@ -2383,6 +2635,88 @@ OGRGeometryH OGR_G_Union( OGRGeometryH hThis, OGRGeometryH hOther ) ((OGRGeometry *) hThis)->Union( (OGRGeometry *) hOther ); } +/************************************************************************/ +/* UnionCascaded() */ +/************************************************************************/ + +/** + * \brief Compute union using cascading. + * + * This method is the same as the C function OGR_G_UnionCascaded(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @return a new geometry representing the union or NULL if an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometry *OGRGeometry::UnionCascaded() const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL ) + { + hGeosProduct = GEOSUnionCascaded_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_UnionCascaded() */ +/************************************************************************/ + +/** + * \brief Compute union using cascading. + * + * This function is the same as the C++ method OGRGeometry::UnionCascaded(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * + * @return a new geometry representing the union or NULL if an error occurs. + */ + +OGRGeometryH OGR_G_UnionCascaded( OGRGeometryH hThis ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_UnionCascaded", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->UnionCascaded(); +} + /************************************************************************/ /* Difference() */ /************************************************************************/ @@ -2422,20 +2756,28 @@ OGRGeometry *OGRGeometry::Difference( const OGRGeometry *poOtherGeom ) const GEOSGeom hGeosProduct = NULL; OGRGeometry *poOGRProduct = NULL; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - hGeosProduct = GEOSDifference( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + hGeosProduct = GEOSDifference_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); if( hGeosProduct != NULL ) { - poOGRProduct = OGRGeometryFactory::createFromGEOS(hGeosProduct); - GEOSGeom_destroy( hGeosProduct ); + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); } } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return poOGRProduct; @@ -2476,7 +2818,7 @@ OGRGeometryH OGR_G_Difference( OGRGeometryH hThis, OGRGeometryH hOther ) } /************************************************************************/ -/* SymmetricDifference() */ +/* SymDifference() */ /************************************************************************/ /** @@ -2485,7 +2827,7 @@ OGRGeometryH OGR_G_Difference( OGRGeometryH hThis, OGRGeometryH hOther ) * Generates a new geometry which is the symmetric difference of this * geometry and the second geometry passed into the method. * - * This method is the same as the C function OGR_G_SymmetricDifference(). + * This method is the same as the C function OGR_G_SymDifference(). * * This method is built on the GEOS library, check it for the definition * of the geometry operation. @@ -2496,10 +2838,12 @@ OGRGeometryH OGR_G_Difference( OGRGeometryH hThis, OGRGeometryH hOther ) * * @return a new geometry representing the symmetric difference or NULL if the * difference is empty or an error occurs. + * + * @since OGR 1.8.0 */ OGRGeometry * -OGRGeometry::SymmetricDifference( const OGRGeometry *poOtherGeom ) const +OGRGeometry::SymDifference( const OGRGeometry *poOtherGeom ) const { #ifndef HAVE_GEOS @@ -2515,28 +2859,51 @@ OGRGeometry::SymmetricDifference( const OGRGeometry *poOtherGeom ) const GEOSGeom hGeosProduct = NULL; OGRGeometry *poOGRProduct = NULL; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - hGeosProduct = GEOSSymDifference( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + hGeosProduct = GEOSSymDifference_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); if( hGeosProduct != NULL ) { - poOGRProduct = OGRGeometryFactory::createFromGEOS(hGeosProduct); - GEOSGeom_destroy( hGeosProduct ); + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); } } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return poOGRProduct; #endif /* HAVE_GEOS */ } + +/** + * \brief Compute symmetric difference (deprecated) + * + * @deprecated + * + * @see OGRGeometry::SymDifference() + */ +OGRGeometry * +OGRGeometry::SymmetricDifference( const OGRGeometry *poOtherGeom ) const + +{ + return SymDifference( poOtherGeom ); +} + /************************************************************************/ -/* OGR_G_SymmetricDifference() */ +/* OGR_G_SymDifference() */ /************************************************************************/ /** @@ -2557,15 +2924,33 @@ OGRGeometry::SymmetricDifference( const OGRGeometry *poOtherGeom ) const * * @return a new geometry representing the symmetric difference or NULL if the * difference is empty or an error occurs. + * + * @since OGR 1.8.0 */ +OGRGeometryH OGR_G_SymDifference( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_SymDifference", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->SymDifference( (OGRGeometry *) hOther ); +} + +/** + * \brief Compute symmetric difference (deprecated) + * + * @deprecated + * + * @see OGR_G_SymmetricDifference() + */ OGRGeometryH OGR_G_SymmetricDifference( OGRGeometryH hThis, OGRGeometryH hOther ) { VALIDATE_POINTER1( hThis, "OGR_G_SymmetricDifference", NULL ); return (OGRGeometryH) - ((OGRGeometry *) hThis)->SymmetricDifference( (OGRGeometry *) hOther ); + ((OGRGeometry *) hThis)->SymDifference( (OGRGeometry *) hOther ); } /************************************************************************/ @@ -2605,14 +2990,16 @@ OGRGeometry::Disjoint( const OGRGeometry *poOtherGeom ) const GEOSGeom hOtherGeosGeom = NULL; OGRBoolean bResult = FALSE; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - bResult = GEOSDisjoint( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + bResult = GEOSDisjoint_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -2685,15 +3072,17 @@ OGRGeometry::Touches( const OGRGeometry *poOtherGeom ) const GEOSGeom hOtherGeosGeom = NULL; OGRBoolean bResult = FALSE; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - bResult = GEOSTouches( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + bResult = GEOSTouches_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -2766,15 +3155,17 @@ OGRGeometry::Crosses( const OGRGeometry *poOtherGeom ) const GEOSGeom hOtherGeosGeom = NULL; OGRBoolean bResult = FALSE; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - bResult = GEOSCrosses( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + bResult = GEOSCrosses_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -2847,14 +3238,16 @@ OGRGeometry::Within( const OGRGeometry *poOtherGeom ) const GEOSGeom hOtherGeosGeom = NULL; OGRBoolean bResult = FALSE; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - bResult = GEOSWithin( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + bResult = GEOSWithin_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -2927,14 +3320,16 @@ OGRGeometry::Contains( const OGRGeometry *poOtherGeom ) const GEOSGeom hOtherGeosGeom = NULL; OGRBoolean bResult = FALSE; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - bResult = GEOSContains( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + bResult = GEOSContains_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -3008,14 +3403,16 @@ OGRGeometry::Overlaps( const OGRGeometry *poOtherGeom ) const GEOSGeom hOtherGeosGeom = NULL; OGRBoolean bResult = FALSE; - hThisGeosGeom = exportToGEOS(); - hOtherGeosGeom = poOtherGeom->exportToGEOS(); + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) { - bResult = GEOSOverlaps( hThisGeosGeom, hOtherGeosGeom ); - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); + bResult = GEOSOverlaps_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); return bResult; @@ -3073,6 +3470,16 @@ void OGRGeometry::closeRings() /* OGR_G_CloseRings() */ /************************************************************************/ +/** + * \brief Force rings to be closed. + * + * If this geometry, or any contained geometries has polygon rings that + * are not closed, they will be closed by adding the starting point at + * the end. + * + * @param hGeom handle to the geometry. + */ + void OGR_G_CloseRings( OGRGeometryH hGeom ) { @@ -3080,3 +3487,836 @@ void OGR_G_CloseRings( OGRGeometryH hGeom ) ((OGRGeometry *) hGeom)->closeRings(); } + +/************************************************************************/ +/* Centroid() */ +/************************************************************************/ + +/** + * \brief Compute the geometry centroid. + * + * The centroid location is applied to the passed in OGRPoint object. + * The centroid is not necessarily within the geometry. + * + * This method relates to the SFCOM ISurface::get_Centroid() method + * however the current implementation based on GEOS can operate on other + * geometry types such as multipoint, linestring, geometrycollection such as + * multipolygons. + * OGC SF SQL 1.1 defines the operation for surfaces (polygons). + * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces (multipolygons). + * + * This function is the same as the C function OGR_G_Centroid(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @return OGRERR_NONE on success or OGRERR_FAILURE on error. + * + * @since OGR 1.8.0 as a OGRGeometry method (previously was restricted to OGRPolygon) + */ + +int OGRGeometry::Centroid( OGRPoint *poPoint ) const + +{ + if( poPoint == NULL ) + return OGRERR_FAILURE; + +#ifndef HAVE_GEOS + // notdef ... not implemented yet. + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return OGRERR_FAILURE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + hOtherGeosGeom = GEOSGetCentroid_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + + if( hOtherGeosGeom == NULL ) + { + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + + OGRGeometry *poCentroidGeom = + OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom ); + + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + + if (poCentroidGeom == NULL) + { + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint) + { + delete poCentroidGeom; + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + + if( poCentroidGeom != NULL && getSpatialReference() != NULL ) + poCentroidGeom->assignSpatialReference(getSpatialReference()); + + OGRPoint *poCentroid = (OGRPoint *) poCentroidGeom; + if( !poCentroid->IsEmpty() ) + { + poPoint->setX( poCentroid->getX() ); + poPoint->setY( poCentroid->getY() ); + } + else + { + poPoint->empty(); + } + + delete poCentroidGeom; + + freeGEOSContext( hGEOSCtxt ); + return OGRERR_NONE; + } + else + { + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Centroid() */ +/************************************************************************/ + +/** + * \brief Compute the geometry centroid. + * + * The centroid location is applied to the passed in OGRPoint object. + * The centroid is not necessarily within the geometry. + * + * This method relates to the SFCOM ISurface::get_Centroid() method + * however the current implementation based on GEOS can operate on other + * geometry types such as multipoint, linestring, geometrycollection such as + * multipolygons. + * OGC SF SQL 1.1 defines the operation for surfaces (polygons). + * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces (multipolygons). + * + * This function is the same as the C++ method OGRGeometry::Centroid(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @return OGRERR_NONE on success or OGRERR_FAILURE on error. + */ + +int OGR_G_Centroid( OGRGeometryH hGeom, OGRGeometryH hCentroidPoint ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Centroid", OGRERR_FAILURE ); + + OGRGeometry *poGeom = ((OGRGeometry *) hGeom); + OGRPoint *poCentroid = ((OGRPoint *) hCentroidPoint); + + if( poCentroid == NULL ) + return OGRERR_FAILURE; + + if( wkbFlatten(poCentroid->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Passed wrong geometry type as centroid argument." ); + return OGRERR_FAILURE; + } + + return poGeom->Centroid( poCentroid ); +} + +/************************************************************************/ +/* OGR_G_PointOnSurface() */ +/************************************************************************/ + +/** + * \brief Returns a point guaranteed to lie on the surface. + * + * This method relates to the SFCOM ISurface::get_PointOnSurface() method + * however the current implementation based on GEOS can operate on other + * geometry types than the types that are supported by SQL/MM-Part 3 : + * surfaces (polygons) and multisurfaces (multipolygons). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hGeom the geometry to operate on. + * @return a point guaranteed to lie on the surface or NULL if an error + * occured. + * + * @since OGR 1.10 + */ + +OGRGeometryH OGR_G_PointOnSurface( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_PointOnSurface", NULL ); + +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRGeometry* poThis = (OGRGeometry*) hGeom; + + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + hOtherGeosGeom = GEOSPointOnSurface_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + + if( hOtherGeosGeom == NULL ) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + OGRGeometry *poInsidePointGeom = (OGRGeometry *) + OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom ); + + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + + if (poInsidePointGeom == NULL) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint) + { + delete poInsidePointGeom; + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + if( poInsidePointGeom != NULL && poThis->getSpatialReference() != NULL ) + poInsidePointGeom->assignSpatialReference(poThis->getSpatialReference()); + + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return (OGRGeometryH) poInsidePointGeom; + } + else + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } +#endif +} + +/************************************************************************/ +/* Simplify() */ +/************************************************************************/ + +/** + * \brief Simplify the geometry. + * + * This function is the same as the C function OGR_G_Simplify(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometry *OGRGeometry::Simplify(double dTolerance) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL ) + { + hGeosProduct = GEOSSimplify_r( hGEOSCtxt, hThisGeosGeom, dTolerance ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct ); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + return poOGRProduct; + +#endif /* HAVE_GEOS */ + +} + +/************************************************************************/ +/* OGR_G_Simplify() */ +/************************************************************************/ + +/** + * \brief Compute a simplified geometry. + * + * This function is the same as the C++ method OGRGeometry::Simplify(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometryH OGR_G_Simplify( OGRGeometryH hThis, double dTolerance ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Simplify", NULL ); + return (OGRGeometryH) ((OGRGeometry *) hThis)->Simplify( dTolerance ); +} + +/************************************************************************/ +/* SimplifyPreserveTopology() */ +/************************************************************************/ + +/** + * \brief Simplify the geometry while preserving topology. + * + * This function is the same as the C function OGR_G_SimplifyPreserveTopology(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.9.0 + */ + +OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL ) + { + hGeosProduct = GEOSTopologyPreserveSimplify_r( hGEOSCtxt, hThisGeosGeom, dTolerance ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct ); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + return poOGRProduct; + +#endif /* HAVE_GEOS */ + +} + +/************************************************************************/ +/* OGR_G_SimplifyPreserveTopology() */ +/************************************************************************/ + +/** + * \brief Simplify the geometry while preserving topology. + * + * This function is the same as the C++ method OGRGeometry::SimplifyPreserveTopology(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.9.0 + */ + +OGRGeometryH OGR_G_SimplifyPreserveTopology( OGRGeometryH hThis, double dTolerance ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_SimplifyPreserveTopology", NULL ); + return (OGRGeometryH) ((OGRGeometry *) hThis)->SimplifyPreserveTopology( dTolerance ); +} + +/************************************************************************/ +/* Polygonize() */ +/************************************************************************/ +/* Contributor: Alessandro Furieri, a.furieri@lqt.it */ +/* Developed for Faunalia (http://www.faunalia.it) with funding from */ +/* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */ +/* AMBIENTALE */ +/************************************************************************/ + +/** + * \brief Polygonizes a set of sparse edges. + * + * A new geometry object is created and returned containing a collection + * of reassembled Polygons: NULL will be returned if the input collection + * doesn't corresponds to a MultiLinestring, or when reassembling Edges + * into Polygons is impossible due to topogical inconsistencies. + * + * This method is the same as the C function OGR_G_Polygonize(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @return a newly allocated geometry now owned by the caller, or NULL on failure. + * + * @since OGR 1.9.0 + */ + +OGRGeometry *OGRGeometry::Polygonize() const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + OGRGeometryCollection *poColl = NULL; + if( wkbFlatten(getGeometryType()) == wkbGeometryCollection || + wkbFlatten(getGeometryType()) == wkbMultiLineString ) + poColl = (OGRGeometryCollection *)this; + else + return NULL; + + int iCount = poColl->getNumGeometries(); + + GEOSGeom *hGeosGeomList = NULL; + GEOSGeom hGeosPolygs = NULL; + OGRGeometry *poPolygsOGRGeom = NULL; + int bError = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + + hGeosGeomList = new GEOSGeom [iCount]; + for ( int ig = 0; ig < iCount; ig++) + { + GEOSGeom hGeosGeom = NULL; + OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig); + if( poChild == NULL || + wkbFlatten(poChild->getGeometryType()) != wkbLineString ) + bError = TRUE; + else + { + hGeosGeom = poChild->exportToGEOS(hGEOSCtxt); + if( hGeosGeom == NULL) + bError = TRUE; + } + *(hGeosGeomList + ig) = hGeosGeom; + } + + if( bError == FALSE ) + { + hGeosPolygs = GEOSPolygonize_r( hGEOSCtxt, hGeosGeomList, iCount ); + + if( hGeosPolygs != NULL ) + { + poPolygsOGRGeom = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosPolygs); + if( poPolygsOGRGeom != NULL && getSpatialReference() != NULL ) + poPolygsOGRGeom->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosPolygs); + } + } + + for ( int ig = 0; ig < iCount; ig++) + { + GEOSGeom hGeosGeom = *(hGeosGeomList + ig); + if( hGeosGeom != NULL) + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); + } + delete [] hGeosGeomList; + freeGEOSContext( hGEOSCtxt ); + + return poPolygsOGRGeom; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Polygonize() */ +/************************************************************************/ +/** + * \brief Polygonizes a set of sparse edges. + * + * A new geometry object is created and returned containing a collection + * of reassembled Polygons: NULL will be returned if the input collection + * doesn't corresponds to a MultiLinestring, or when reassembling Edges + * into Polygons is impossible due to topogical inconsistencies. + * + * This function is the same as the C++ method OGRGeometry::Polygonize(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hTarget The Geometry to be polygonized. + * + * @return a handle to a newly allocated geometry now owned by the caller, + * or NULL on failure. + * + * @since OGR 1.9.0 + */ + +OGRGeometryH OGR_G_Polygonize( OGRGeometryH hTarget ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_Polygonize", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Polygonize(); +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +/** + * \brief Swap x and y coordinates. + * + * @since OGR 1.8.0 + */ + +void OGRGeometry::swapXY() + +{ +} + +/************************************************************************/ +/* Prepared geometry API */ +/************************************************************************/ + +/* GEOS >= 3.1.0 for prepared geometries */ +#if defined(HAVE_GEOS) +#define HAVE_GEOS_PREPARED_GEOMETRY +#endif + +#ifdef HAVE_GEOS_PREPARED_GEOMETRY +struct _OGRPreparedGeometry +{ + GEOSContextHandle_t hGEOSCtxt; + GEOSGeom hGEOSGeom; + const GEOSPreparedGeometry* poPreparedGEOSGeom; +}; +#endif + +/************************************************************************/ +/* OGRHasPreparedGeometrySupport() */ +/************************************************************************/ + +int OGRHasPreparedGeometrySupport() +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + return TRUE; +#else + return FALSE; +#endif +} + +/************************************************************************/ +/* OGRCreatePreparedGeometry() */ +/************************************************************************/ + +OGRPreparedGeometry* OGRCreatePreparedGeometry( const OGRGeometry* poGeom ) +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt); + if( hGEOSGeom == NULL ) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + const GEOSPreparedGeometry* poPreparedGEOSGeom = GEOSPrepare_r(hGEOSCtxt, hGEOSGeom); + if( poPreparedGEOSGeom == NULL ) + { + GEOSGeom_destroy_r( hGEOSCtxt, hGEOSGeom ); + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + OGRPreparedGeometry* poPreparedGeom = new OGRPreparedGeometry; + poPreparedGeom->hGEOSCtxt = hGEOSCtxt; + poPreparedGeom->hGEOSGeom = hGEOSGeom; + poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom; + + return poPreparedGeom; +#else + return NULL; +#endif +} + +/************************************************************************/ +/* OGRDestroyPreparedGeometry() */ +/************************************************************************/ + +void OGRDestroyPreparedGeometry( OGRPreparedGeometry* poPreparedGeom ) +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + if( poPreparedGeom != NULL ) + { + GEOSPreparedGeom_destroy_r(poPreparedGeom->hGEOSCtxt, poPreparedGeom->poPreparedGEOSGeom); + GEOSGeom_destroy_r( poPreparedGeom->hGEOSCtxt, poPreparedGeom->hGEOSGeom ); + OGRGeometry::freeGEOSContext( poPreparedGeom->hGEOSCtxt ); + delete poPreparedGeom; + } +#endif +} + +/************************************************************************/ +/* OGRPreparedGeometryIntersects() */ +/************************************************************************/ + +int OGRPreparedGeometryIntersects( const OGRPreparedGeometry* poPreparedGeom, + const OGRGeometry* poOtherGeom ) +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + if( poPreparedGeom == NULL || poOtherGeom == NULL ) + return FALSE; + + GEOSGeom hGEOSOtherGeom = poOtherGeom->exportToGEOS(poPreparedGeom->hGEOSCtxt); + if( hGEOSOtherGeom == NULL ) + return FALSE; + + int bRet = GEOSPreparedIntersects_r(poPreparedGeom->hGEOSCtxt, + poPreparedGeom->poPreparedGEOSGeom, + hGEOSOtherGeom); + GEOSGeom_destroy_r( poPreparedGeom->hGEOSCtxt, hGEOSOtherGeom ); + + return bRet; +#else + return FALSE; +#endif +} + +/************************************************************************/ +/* OGRGeometryFromEWKB() */ +/************************************************************************/ + +/* Flags for creating WKB format for PostGIS */ +#define WKBZOFFSET 0x80000000 +#define WKBMOFFSET 0x40000000 +#define WKBSRIDFLAG 0x20000000 +#define WKBBBOXFLAG 0x10000000 + +OGRGeometry *OGRGeometryFromEWKB( GByte *pabyWKB, int nLength, int* pnSRID ) + +{ + OGRGeometry *poGeometry = NULL; + unsigned int ewkbFlags = 0; + + if (nLength < 5) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid EWKB content : %d bytes", nLength ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Detect XYZM variant of PostGIS EWKB */ +/* */ +/* OGR does not support parsing M coordinate, */ +/* so we return NULL geometry. */ +/* -------------------------------------------------------------------- */ + memcpy(&ewkbFlags, pabyWKB+1, 4); + OGRwkbByteOrder eByteOrder = (pabyWKB[0] == 0 ? wkbXDR : wkbNDR); + if( OGR_SWAP( eByteOrder ) ) + ewkbFlags= CPL_SWAP32(ewkbFlags); + + if (ewkbFlags & WKBMOFFSET) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Reading EWKB with 4-dimensional coordinates (XYZM) is not supported" ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* PostGIS EWKB format includes an SRID, but this won't be */ +/* understood by OGR, so if the SRID flag is set, we remove the */ +/* SRID (bytes at offset 5 to 8). */ +/* -------------------------------------------------------------------- */ + if( nLength > 9 && + ((pabyWKB[0] == 0 /* big endian */ && (pabyWKB[1] & 0x20) ) + || (pabyWKB[0] != 0 /* little endian */ && (pabyWKB[4] & 0x20))) ) + { + if( pnSRID ) + { + memcpy(pnSRID, pabyWKB+5, 4); + if( OGR_SWAP( eByteOrder ) ) + *pnSRID = CPL_SWAP32(*pnSRID); + } + memmove( pabyWKB+5, pabyWKB+9, nLength-9 ); + nLength -= 4; + if( pabyWKB[0] == 0 ) + pabyWKB[1] &= (~0x20); + else + pabyWKB[4] &= (~0x20); + } + +/* -------------------------------------------------------------------- */ +/* Try to ingest the geometry. */ +/* -------------------------------------------------------------------- */ + OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLength ); + + return poGeometry; +} + +/************************************************************************/ +/* OGRGeometryFromHexEWKB() */ +/************************************************************************/ + +OGRGeometry *OGRGeometryFromHexEWKB( const char *pszBytea, int* pnSRID ) + +{ + GByte *pabyWKB; + int nWKBLength=0; + OGRGeometry *poGeometry; + + if( pszBytea == NULL ) + return NULL; + + pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength); + + poGeometry = OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID); + + CPLFree(pabyWKB); + + return poGeometry; +} + +/************************************************************************/ +/* OGRGeometryToHexEWKB() */ +/************************************************************************/ + +char* OGRGeometryToHexEWKB( OGRGeometry * poGeometry, int nSRSId ) +{ + GByte *pabyWKB; + char *pszTextBuf; + char *pszTextBufCurrent; + char *pszHex; + + int nWkbSize = poGeometry->WkbSize(); + pabyWKB = (GByte *) CPLMalloc(nWkbSize); + + if( poGeometry->exportToWkb( wkbNDR, pabyWKB ) != OGRERR_NONE ) + { + CPLFree( pabyWKB ); + return CPLStrdup(""); + } + + /* When converting to hex, each byte takes 2 hex characters. In addition + we add in 8 characters to represent the SRID integer in hex, and + one for a null terminator */ + + int pszSize = nWkbSize*2 + 8 + 1; + pszTextBuf = (char *) CPLMalloc(pszSize); + pszTextBufCurrent = pszTextBuf; + + /* Convert the 1st byte, which is the endianess flag, to hex. */ + pszHex = CPLBinaryToHex( 1, pabyWKB ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + pszTextBufCurrent += 2; + + /* Next, get the geom type which is bytes 2 through 5 */ + GUInt32 geomType; + memcpy( &geomType, pabyWKB+1, 4 ); + + /* Now add the SRID flag if an SRID is provided */ + if (nSRSId > 0) + { + /* Change the flag to wkbNDR (little) endianess */ + GUInt32 nGSrsFlag = CPL_LSBWORD32( WKBSRIDFLAG ); + /* Apply the flag */ + geomType = geomType | nGSrsFlag; + } + + /* Now write the geom type which is 4 bytes */ + pszHex = CPLBinaryToHex( 4, (GByte*) &geomType ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + pszTextBufCurrent += 8; + + /* Now include SRID if provided */ + if (nSRSId > 0) + { + /* Force the srsid to wkbNDR (little) endianess */ + GUInt32 nGSRSId = CPL_LSBWORD32( nSRSId ); + pszHex = CPLBinaryToHex( sizeof(nGSRSId),(GByte*) &nGSRSId ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + pszTextBufCurrent += 8; + } + + /* Copy the rest of the data over - subtract + 5 since we already copied 5 bytes above */ + pszHex = CPLBinaryToHex( nWkbSize - 5, pabyWKB + 5 ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + + CPLFree( pabyWKB ); + + return pszTextBuf; +} diff --git a/ogr/ogrgeometrycollection.cpp b/ogr/ogrgeometrycollection.cpp index 2672f40..354dba2 100644 --- a/ogr/ogrgeometrycollection.cpp +++ b/ogr/ogrgeometrycollection.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrgeometrycollection.cpp 16898 2009-05-01 12:23:36Z rouault $ + * $Id: ogrgeometrycollection.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRGeometryCollection class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,7 @@ #include "ogr_geometry.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrgeometrycollection.cpp 16898 2009-05-01 12:23:36Z rouault $"); +CPL_CVSID("$Id: ogrgeometrycollection.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRGeometryCollection() */ @@ -119,7 +120,20 @@ OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const int OGRGeometryCollection::getDimension() const { - return 2; // This isn't strictly correct. It should be based on members. + int nDimension = 0; + /* FIXME? Not sure if it is really appropriate to take the max in case */ + /* of geometries of different dimension... */ + for( int i = 0; i < nGeomCount; i++ ) + { + int nSubGeomDimension = papoGeoms[i]->getDimension(); + if( nSubGeomDimension > nDimension ) + { + nDimension = nSubGeomDimension; + if( nDimension == 2 ) + break; + } + } + return nDimension; } /************************************************************************/ @@ -352,18 +366,24 @@ int OGRGeometryCollection::WkbSize() const } /************************************************************************/ -/* importFromWkb() */ -/* */ -/* Initialize from serialized stream in well known binary */ -/* format. */ +/* importFromWkbInternal() */ /************************************************************************/ -OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, - int nSize ) +OGRErr OGRGeometryCollection::importFromWkbInternal( unsigned char * pabyData, + int nSize, int nRecLevel ) { OGRwkbByteOrder eByteOrder; int nDataOffset; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing WKB geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } if( nSize < 9 && nSize != -1 ) return OGRERR_NOT_ENOUGH_DATA; @@ -380,24 +400,13 @@ OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, /* geometry type is between 0 and 255 so we only have to fetch */ /* one byte. */ /* -------------------------------------------------------------------- */ -#ifdef DEBUG - OGRwkbGeometryType eGeometryType; - if( eByteOrder == wkbNDR ) - { - eGeometryType = (OGRwkbGeometryType) pabyData[1]; - } - else - { - eGeometryType = (OGRwkbGeometryType) pabyData[4]; - } + OGRBoolean b3D; + OGRwkbGeometryType eGeometryType; + OGRErr err = OGRReadWKBGeometryType( pabyData, &eGeometryType, &b3D ); - if (! ( eGeometryType == wkbGeometryCollection - || eGeometryType == wkbMultiPolygon - || eGeometryType == wkbMultiLineString - || eGeometryType == wkbMultiPoint )) + if ( err != OGRERR_NONE || eGeometryType != wkbFlatten(getGeometryType()) ) return OGRERR_CORRUPT_DATA; -#endif /* -------------------------------------------------------------------- */ /* Clear existing Geoms. */ @@ -444,29 +453,90 @@ OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) { OGRErr eErr; + OGRGeometry* poSubGeom = NULL; + + /* Parses sub-geometry */ + unsigned char* pabySubData = pabyData + nDataOffset; + if( nSize < 9 && nSize != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + OGRwkbByteOrder eSubGeomByteOrder = + DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabySubData); + + if( eSubGeomByteOrder != wkbXDR && eSubGeomByteOrder != wkbNDR ) + return OGRERR_CORRUPT_DATA; + + OGRwkbGeometryType eSubGeomType; + OGRBoolean bIs3D; + if ( OGRReadWKBGeometryType( pabySubData, &eSubGeomType, &bIs3D ) != OGRERR_NONE ) + return OGRERR_FAILURE; - eErr = OGRGeometryFactory:: - createFromWkb( pabyData + nDataOffset, NULL, - papoGeoms + iGeom, nSize ); + if( eSubGeomType == wkbPoint || + eSubGeomType == wkbLineString || + eSubGeomType == wkbPolygon) + { + eErr = OGRGeometryFactory:: + createFromWkb( pabySubData, NULL, + &poSubGeom, nSize ); + } + else if (eSubGeomType == wkbGeometryCollection || + eSubGeomType == wkbMultiPolygon || + eSubGeomType == wkbMultiPoint || + eSubGeomType == wkbMultiLineString) + { + poSubGeom = OGRGeometryFactory::createGeometry( eSubGeomType ); + eErr = ((OGRGeometryCollection*)poSubGeom)-> + importFromWkbInternal( pabySubData, nSize, nRecLevel + 1 ); + } + else + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; if( eErr != OGRERR_NONE ) { nGeomCount = iGeom; + delete poSubGeom; return eErr; } + if( (eGeometryType == wkbMultiPoint && eSubGeomType != wkbPoint) || + (eGeometryType == wkbMultiLineString && eSubGeomType != wkbLineString) || + (eGeometryType == wkbMultiPolygon && eSubGeomType != wkbPolygon) ) + { + nGeomCount = iGeom; + CPLDebug("OGR", "Cannot add geometry of type (%d) to geometry of type (%d)", + eSubGeomType, eGeometryType); + delete poSubGeom; + return OGRERR_CORRUPT_DATA; + } + + papoGeoms[iGeom] = poSubGeom; + if (papoGeoms[iGeom]->getCoordinateDimension() == 3) nCoordDimension = 3; + int nSubGeomWkbSize = papoGeoms[iGeom]->WkbSize(); if( nSize != -1 ) - nSize -= papoGeoms[iGeom]->WkbSize(); + nSize -= nSubGeomWkbSize; - nDataOffset += papoGeoms[iGeom]->WkbSize(); + nDataOffset += nSubGeomWkbSize; } return OGRERR_NONE; } +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, + int nSize ) + +{ + return importFromWkbInternal(pabyData, nSize, 0); +} + /************************************************************************/ /* exportToWkb() */ /* */ @@ -474,7 +544,8 @@ OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, /************************************************************************/ OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder, - unsigned char * pabyData ) const + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const { int nOffset; @@ -490,6 +561,9 @@ OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder, /* -------------------------------------------------------------------- */ GUInt32 nGType = getGeometryType(); + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); + if( eByteOrder == wkbNDR ) nGType = CPL_LSBWORD32( nGType ); else @@ -528,16 +602,26 @@ OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder, } /************************************************************************/ -/* importFromWkt() */ +/* importFromWktInternal() */ /************************************************************************/ -OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) +OGRErr OGRGeometryCollection::importFromWktInternal( char ** ppszInput, int nRecLevel ) { char szToken[OGR_WKT_TOKEN_MAX]; const char *pszInput = *ppszInput; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing WKT geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } + /* -------------------------------------------------------------------- */ /* Clear existing Geoms. */ /* -------------------------------------------------------------------- */ @@ -553,38 +637,75 @@ OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ -/* The next character should be a ( indicating the start of the */ -/* list of objects. */ +/* Check for EMPTY ... */ /* -------------------------------------------------------------------- */ - pszInput = OGRWktReadToken( pszInput, szToken ); + const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; + pszPreScan = OGRWktReadToken( pszInput, szToken ); if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszInput; + *ppszInput = (char *) pszPreScan; + empty(); return OGRERR_NONE; } - if( szToken[0] != '(' ) - return OGRERR_CORRUPT_DATA; - /* -------------------------------------------------------------------- */ -/* If the next token is EMPTY, then verify that we have proper */ -/* EMPTY format will a trailing closing bracket. */ +/* Check for Z, M or ZM. Will ignore the Measure */ /* -------------------------------------------------------------------- */ - OGRWktReadToken( pszInput, szToken ); - if( EQUAL(szToken,"EMPTY") ) + else if( EQUAL(szToken,"Z") ) { - pszInput = OGRWktReadToken( pszInput, szToken ); - pszInput = OGRWktReadToken( pszInput, szToken ); - - *ppszInput = (char *) pszInput; + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ return OGRERR_NONE; + } + } + + if( !EQUAL(szToken,"(") ) + return OGRERR_CORRUPT_DATA; + + if ( !bHasZ && !bHasM ) + { + /* Test for old-style GEOMETRYCOLLECTION(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszInput = OGRWktReadToken( pszPreScan, szToken ); + + if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszInput; + empty(); + return OGRERR_NONE; + } + } } + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + /* ==================================================================== */ /* Read each subgeometry in turn. */ /* ==================================================================== */ @@ -593,8 +714,25 @@ OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) OGRGeometry *poGeom = NULL; OGRErr eErr; - eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput, - NULL, &poGeom ); + /* -------------------------------------------------------------------- */ + /* Get the first token, which should be the geometry type. */ + /* -------------------------------------------------------------------- */ + if( OGRWktReadToken( pszInput, szToken ) == NULL ) + return OGRERR_CORRUPT_DATA; + + /* -------------------------------------------------------------------- */ + /* Do the import. */ + /* -------------------------------------------------------------------- */ + if (EQUAL(szToken,"GEOMETRYCOLLECTION")) + { + poGeom = new OGRGeometryCollection(); + eErr = ((OGRGeometryCollection*)poGeom)-> + importFromWktInternal( (char **) &pszInput, nRecLevel + 1 ); + } + else + eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput, + NULL, &poGeom ); + if( eErr != OGRERR_NONE ) return eErr; @@ -619,6 +757,16 @@ OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) return OGRERR_NONE; } +/************************************************************************/ +/* importFromWkt() */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) + +{ + return importFromWktInternal(ppszInput, 0); +} + /************************************************************************/ /* exportToWkt() */ /* */ @@ -704,24 +852,84 @@ void OGRGeometryCollection::getEnvelope( OGREnvelope * psEnvelope ) const { OGREnvelope oGeomEnv; - - if( nGeomCount == 0 ) - return; + int bExtentSet = FALSE; + + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + if (!papoGeoms[iGeom]->IsEmpty()) + { + if (!bExtentSet) + { + papoGeoms[iGeom]->getEnvelope( psEnvelope ); + bExtentSet = TRUE; + } + else + { + papoGeoms[iGeom]->getEnvelope( &oGeomEnv ); + + if( psEnvelope->MinX > oGeomEnv.MinX ) + psEnvelope->MinX = oGeomEnv.MinX; + if( psEnvelope->MinY > oGeomEnv.MinY ) + psEnvelope->MinY = oGeomEnv.MinY; + if( psEnvelope->MaxX < oGeomEnv.MaxX ) + psEnvelope->MaxX = oGeomEnv.MaxX; + if( psEnvelope->MaxY < oGeomEnv.MaxY ) + psEnvelope->MaxY = oGeomEnv.MaxY; + } + } + } + + if (!bExtentSet) + { + psEnvelope->MinX = psEnvelope->MinY = 0; + psEnvelope->MaxX = psEnvelope->MaxY = 0; + } +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ - papoGeoms[0]->getEnvelope( psEnvelope ); +void OGRGeometryCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const - for( int iGeom = 1; iGeom < nGeomCount; iGeom++ ) +{ + OGREnvelope3D oGeomEnv; + int bExtentSet = FALSE; + + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) { - papoGeoms[iGeom]->getEnvelope( &oGeomEnv ); + if (!papoGeoms[iGeom]->IsEmpty()) + { + if (!bExtentSet) + { + papoGeoms[iGeom]->getEnvelope( psEnvelope ); + bExtentSet = TRUE; + } + else + { + papoGeoms[iGeom]->getEnvelope( &oGeomEnv ); + + if( psEnvelope->MinX > oGeomEnv.MinX ) + psEnvelope->MinX = oGeomEnv.MinX; + if( psEnvelope->MinY > oGeomEnv.MinY ) + psEnvelope->MinY = oGeomEnv.MinY; + if( psEnvelope->MaxX < oGeomEnv.MaxX ) + psEnvelope->MaxX = oGeomEnv.MaxX; + if( psEnvelope->MaxY < oGeomEnv.MaxY ) + psEnvelope->MaxY = oGeomEnv.MaxY; + + if( psEnvelope->MinZ > oGeomEnv.MinZ ) + psEnvelope->MinZ = oGeomEnv.MinZ; + if( psEnvelope->MaxZ < oGeomEnv.MaxZ ) + psEnvelope->MaxZ = oGeomEnv.MaxZ; + } + } + } - if( psEnvelope->MinX > oGeomEnv.MinX ) - psEnvelope->MinX = oGeomEnv.MinX; - if( psEnvelope->MinY > oGeomEnv.MinY ) - psEnvelope->MinY = oGeomEnv.MinY; - if( psEnvelope->MaxX < oGeomEnv.MaxX ) - psEnvelope->MaxX = oGeomEnv.MaxX; - if( psEnvelope->MaxY < oGeomEnv.MaxY ) - psEnvelope->MaxY = oGeomEnv.MaxY; + if (!bExtentSet) + { + psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0; + psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0; } } @@ -740,6 +948,9 @@ OGRBoolean OGRGeometryCollection::Equals( OGRGeometry * poOther ) const if( poOther->getGeometryType() != getGeometryType() ) return FALSE; + if ( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + if( getNumGeometries() != poOGC->getNumGeometries() ) return FALSE; @@ -821,6 +1032,47 @@ void OGRGeometryCollection::setCoordinateDimension( int nNewDimension ) } +/************************************************************************/ +/* get_Length() */ +/************************************************************************/ + +/** + * \brief Compute the length of a multicurve. + * + * The length is computed as the sum of the length of all members + * in this collection. + * + * @note No warning will be issued if a member of the collection does not + * support the get_Length method. + * + * @return computed area. + */ + +double OGRGeometryCollection::get_Length() const +{ + double dfLength = 0.0; + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRGeometry* geom = papoGeoms[iGeom]; + switch( wkbFlatten(geom->getGeometryType()) ) + { + case wkbLinearRing: + case wkbLineString: + dfLength += ((OGRCurve *) geom)->get_Length(); + break; + + case wkbGeometryCollection: + dfLength +=((OGRGeometryCollection *) geom)->get_Length(); + break; + + default: + break; + } + } + + return dfLength; +} + /************************************************************************/ /* get_Area() */ /************************************************************************/ @@ -897,3 +1149,13 @@ void OGRGeometryCollection::segmentize( double dfMaxLength ) for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) papoGeoms[iGeom]->segmentize(dfMaxLength); } + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRGeometryCollection::swapXY() +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + papoGeoms[iGeom]->swapXY(); +} diff --git a/ogr/ogrgeometryfactory.cpp b/ogr/ogrgeometryfactory.cpp index a36cbe5..dd283ae 100644 --- a/ogr/ogrgeometryfactory.cpp +++ b/ogr/ogrgeometryfactory.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrgeometryfactory.cpp 18320 2009-12-17 02:22:03Z warmerdam $ + * $Id: ogrgeometryfactory.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Factory for converting geometry to and from well known binary @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,7 +34,7 @@ #include "ogr_p.h" #include "ogr_geos.h" -CPL_CVSID("$Id: ogrgeometryfactory.cpp 18320 2009-12-17 02:22:03Z warmerdam $"); +CPL_CVSID("$Id: ogrgeometryfactory.cpp 27044 2014-03-16 23:41:27Z rouault $"); #ifndef PI #define PI 3.14159265358979323846 @@ -63,7 +64,8 @@ CPL_CVSID("$Id: ogrgeometryfactory.cpp 18320 2009-12-17 02:22:03Z warmerdam $"); * created geometry object. This may be NULL. * @param ppoReturn the newly created geometry object will be assigned to the * indicated pointer on return. This will be NULL in case - * of failure. + * of failure. If not NULL, *ppoReturn should be freed with + * OGRGeometryFactory::destroyGeometry() after use. * @param nBytes the number of bytes available in pabyData, or -1 if it isn't * known. * @@ -117,10 +119,13 @@ OGRErr OGRGeometryFactory::createFromWkb(unsigned char *pabyData, /* geometry type is between 0 and 255 so we only have to fetch */ /* one byte. */ /* -------------------------------------------------------------------- */ - if( eByteOrder == wkbNDR ) - eGeometryType = (OGRwkbGeometryType) pabyData[1]; - else - eGeometryType = (OGRwkbGeometryType) pabyData[4]; + + OGRBoolean bIs3D; + OGRErr err = OGRReadWKBGeometryType( pabyData, &eGeometryType, &bIs3D ); + + if( err != OGRERR_NONE ) + return err; + /* -------------------------------------------------------------------- */ /* Instantiate a geometry of the appropriate type, and */ @@ -173,7 +178,8 @@ OGRErr OGRGeometryFactory::createFromWkb(unsigned char *pabyData, * created geometry object. This may be NULL. * @param phGeometry the newly created geometry object will * be assigned to the indicated handle on return. This will be NULL in case - * of failure. + * of failure. If not NULL, *phGeometry should be freed with + * OGR_G_DestroyGeometry() after use. * @param nBytes the number of bytes of data available in pabyData, or -1 * if it is not known, but assumed to be sufficient. * @@ -210,7 +216,8 @@ OGRErr CPL_DLL OGR_G_CreateFromWkb( unsigned char *pabyData, * created geometry object. This may be NULL. * @param ppoReturn the newly created geometry object will be assigned to the * indicated pointer on return. This will be NULL if the - * method fails. + * method fails. If not NULL, *ppoReturn should be freed with + * OGRGeometryFactory::destroyGeometry() after use. * * Example: * @@ -218,7 +225,7 @@ OGRErr CPL_DLL OGR_G_CreateFromWkb( unsigned char *pabyData, * const char* wkt= "POINT(0 0)"; * * // cast because OGR_G_CreateFromWkt will move the pointer - * char* pszWkt = (char*) wkt.c_str(); + * char* pszWkt = (char*) wkt; * OGRSpatialReferenceH ref = OSRNewSpatialReference(NULL); * OGRGeometryH new_geom; * OGRErr err = OGR_G_CreateFromWkt(&pszWkt, ref, &new_geom); @@ -330,7 +337,8 @@ OGRErr OGRGeometryFactory::createFromWkt(char **ppszData, * created geometry object. This may be NULL. * @param phGeometry the newly created geometry object will be assigned to the * indicated handle on return. This will be NULL if the - * method fails. + * method fails. If not NULL, *phGeometry should be freed with + * OGR_G_DestroyGeometry() after use. * * @return OGRERR_NONE if all goes well, otherwise any of * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or @@ -354,7 +362,7 @@ OGRErr CPL_DLL OGR_G_CreateFromWkt( char **ppszData, /** * \brief Create an empty geometry of desired type. * - * This is equivelent to allocating the desired geometry with new, but + * This is equivalent to allocating the desired geometry with new, but * the allocation is guaranteed to take place in the context of the * GDAL/OGR heap. * @@ -362,7 +370,8 @@ OGRErr CPL_DLL OGR_G_CreateFromWkt( char **ppszData, * * @param eGeometryType the type code of the geometry class to be instantiated. * - * @return the newly create geometry or NULL on failure. + * @return the newly create geometry or NULL on failure. Should be freed with + * OGRGeometryFactory::destroyGeometry() after use. */ OGRGeometry * @@ -406,7 +415,7 @@ OGRGeometryFactory::createGeometry( OGRwkbGeometryType eGeometryType ) /** * \brief Create an empty geometry of desired type. * - * This is equivelent to allocating the desired geometry with new, but + * This is equivalent to allocating the desired geometry with new, but * the allocation is guaranteed to take place in the context of the * GDAL/OGR heap. * @@ -415,7 +424,8 @@ OGRGeometryFactory::createGeometry( OGRwkbGeometryType eGeometryType ) * * @param eGeometryType the type code of the geometry to be created. * - * @return handle to the newly create geometry or NULL on failure. + * @return handle to the newly create geometry or NULL on failure. Should be freed with + * OGR_G_DestroyGeometry() after use. */ OGRGeometryH OGR_G_CreateGeometry( OGRwkbGeometryType eGeometryType ) @@ -479,6 +489,7 @@ void OGR_G_DestroyGeometry( OGRGeometryH hGeom ) * this just effects a change on multipolygons. The passed in geometry is * consumed and a new one returned (or potentially the same one). * + * @param poGeom the input geometry - ownership is passed to the method. * @return new geometry. */ @@ -488,13 +499,17 @@ OGRGeometry *OGRGeometryFactory::forceToPolygon( OGRGeometry *poGeom ) if( poGeom == NULL ) return NULL; - if( wkbFlatten(poGeom->getGeometryType()) != wkbGeometryCollection - || wkbFlatten(poGeom->getGeometryType()) != wkbMultiPolygon ) + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + + if( eGeomType != wkbGeometryCollection + && eGeomType != wkbMultiPolygon ) return poGeom; // build an aggregated polygon from all the polygon rings in the container. OGRPolygon *poPolygon = new OGRPolygon(); OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + if( poGeom->getSpatialReference() != NULL ) + poPolygon->assignSpatialReference(poGeom->getSpatialReference()); int iGeom; for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) @@ -505,11 +520,14 @@ OGRGeometry *OGRGeometryFactory::forceToPolygon( OGRGeometry *poGeom ) OGRPolygon *poOldPoly = (OGRPolygon *) poGC->getGeometryRef(iGeom); int iRing; + + if( poOldPoly->getExteriorRing() == NULL ) + continue; - poPolygon->addRing( poOldPoly->getExteriorRing() ); + poPolygon->addRingDirectly( poOldPoly->stealExteriorRing() ); for( iRing = 0; iRing < poOldPoly->getNumInteriorRings(); iRing++ ) - poPolygon->addRing( poOldPoly->getInteriorRing( iRing ) ); + poPolygon->addRingDirectly( poOldPoly->stealInteriorRing( iRing ) ); } delete poGC; @@ -517,6 +535,29 @@ OGRGeometry *OGRGeometryFactory::forceToPolygon( OGRGeometry *poGeom ) return poPolygon; } +/************************************************************************/ +/* OGR_G_ForceToPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to polygon. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToPolygon(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToPolygon( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToPolygon( (OGRGeometry *) hGeom ); +} + /************************************************************************/ /* forceToMultiPolygon() */ /************************************************************************/ @@ -537,11 +578,21 @@ OGRGeometry *OGRGeometryFactory::forceToMultiPolygon( OGRGeometry *poGeom ) if( poGeom == NULL ) return NULL; + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiPolygon, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiPolygon ) + { + return poGeom; + } + /* -------------------------------------------------------------------- */ /* Check for the case of a geometrycollection that can be */ /* promoted to MultiPolygon. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection ) + if( eGeomType == wkbGeometryCollection ) { int iGeom; int bAllPoly = TRUE; @@ -558,6 +609,8 @@ OGRGeometry *OGRGeometryFactory::forceToMultiPolygon( OGRGeometry *poGeom ) return poGeom; OGRMultiPolygon *poMP = new OGRMultiPolygon(); + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); while( poGC->getNumGeometries() > 0 ) { @@ -574,15 +627,40 @@ OGRGeometry *OGRGeometryFactory::forceToMultiPolygon( OGRGeometry *poGeom ) /* Eventually we should try to split the polygon into component */ /* island polygons. But thats alot of work and can be put off. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) != wkbPolygon ) + if( eGeomType != wkbPolygon ) return poGeom; OGRMultiPolygon *poMP = new OGRMultiPolygon(); + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); poMP->addGeometryDirectly( poGeom ); return poMP; } +/************************************************************************/ +/* OGR_G_ForceToMultiPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to multipolygon. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToMultiPolygon(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToMultiPolygon( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToMultiPolygon( (OGRGeometry *) hGeom ); +} + /************************************************************************/ /* forceToMultiPoint() */ /************************************************************************/ @@ -603,11 +681,21 @@ OGRGeometry *OGRGeometryFactory::forceToMultiPoint( OGRGeometry *poGeom ) if( poGeom == NULL ) return NULL; + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiPoint, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiPoint ) + { + return poGeom; + } + /* -------------------------------------------------------------------- */ /* Check for the case of a geometrycollection that can be */ /* promoted to MultiPoint. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection ) + if( eGeomType == wkbGeometryCollection ) { int iGeom; int bAllPoint = TRUE; @@ -624,6 +712,8 @@ OGRGeometry *OGRGeometryFactory::forceToMultiPoint( OGRGeometry *poGeom ) return poGeom; OGRMultiPoint *poMP = new OGRMultiPoint(); + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); while( poGC->getNumGeometries() > 0 ) { @@ -636,15 +726,40 @@ OGRGeometry *OGRGeometryFactory::forceToMultiPoint( OGRGeometry *poGeom ) return poMP; } - if( wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + if( eGeomType != wkbPoint ) return poGeom; OGRMultiPoint *poMP = new OGRMultiPoint(); + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); poMP->addGeometryDirectly( poGeom ); return poMP; } +/************************************************************************/ +/* OGR_G_ForceToMultiPoint() */ +/************************************************************************/ + +/** + * \brief Convert to multipoint. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToMultiPoint(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToMultiPoint( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToMultiPoint( (OGRGeometry *) hGeom ); +} + /************************************************************************/ /* forceToMultiLinestring() */ /************************************************************************/ @@ -671,11 +786,21 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) if( poGeom == NULL ) return NULL; + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiLineString, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiLineString ) + { + return poGeom; + } + /* -------------------------------------------------------------------- */ /* Check for the case of a geometrycollection that can be */ /* promoted to MultiLineString. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection ) + if( eGeomType == wkbGeometryCollection ) { int iGeom; int bAllLines = TRUE; @@ -692,6 +817,8 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) return poGeom; OGRMultiLineString *poMP = new OGRMultiLineString(); + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); while( poGC->getNumGeometries() > 0 ) { @@ -707,9 +834,11 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) /* -------------------------------------------------------------------- */ /* Turn a linestring into a multilinestring. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + if( eGeomType == wkbLineString ) { OGRMultiLineString *poMP = new OGRMultiLineString(); + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); poMP->addGeometryDirectly( poGeom ); return poMP; } @@ -717,21 +846,31 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) /* -------------------------------------------------------------------- */ /* Convert polygons into a multilinestring. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + if( eGeomType == wkbPolygon ) { OGRMultiLineString *poMP = new OGRMultiLineString(); OGRPolygon *poPoly = (OGRPolygon *) poGeom; int iRing; + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); + for( iRing = 0; iRing < poPoly->getNumInteriorRings()+1; iRing++ ) { OGRLineString *poNewLS, *poLR; if( iRing == 0 ) + { poLR = poPoly->getExteriorRing(); + if( poLR == NULL ) + break; + } else poLR = poPoly->getInteriorRing(iRing-1); + if (poLR == NULL || poLR->getNumPoints() == 0) + continue; + poNewLS = new OGRLineString(); poNewLS->addSubLineString( poLR ); poMP->addGeometryDirectly( poNewLS ); @@ -745,12 +884,15 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) /* -------------------------------------------------------------------- */ /* Convert multi-polygons into a multilinestring. */ /* -------------------------------------------------------------------- */ - if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) + if( eGeomType == wkbMultiPolygon ) { OGRMultiLineString *poMP = new OGRMultiLineString(); OGRMultiPolygon *poMPoly = (OGRMultiPolygon *) poGeom; int iPoly; + if( poGeom->getSpatialReference() != NULL ) + poMP->assignSpatialReference(poGeom->getSpatialReference()); + for( iPoly = 0; iPoly < poMPoly->getNumGeometries(); iPoly++ ) { OGRPolygon *poPoly = (OGRPolygon*) poMPoly->getGeometryRef(iPoly); @@ -761,9 +903,16 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) OGRLineString *poNewLS, *poLR; if( iRing == 0 ) + { poLR = poPoly->getExteriorRing(); + if( poLR == NULL ) + break; + } else poLR = poPoly->getInteriorRing(iRing-1); + + if (poLR == NULL || poLR->getNumPoints() == 0) + continue; poNewLS = new OGRLineString(); poNewLS->addSubLineString( poLR ); @@ -778,6 +927,29 @@ OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) return poGeom; } +/************************************************************************/ +/* OGR_G_ForceToMultiLineString() */ +/************************************************************************/ + +/** + * \brief Convert to multilinestring. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToMultiLineString(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToMultiLineString( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToMultiLineString( (OGRGeometry *) hGeom ); +} + /************************************************************************/ /* organizePolygons() */ /************************************************************************/ @@ -827,7 +999,8 @@ typedef enum { METHOD_NORMAL, METHOD_SKIP, - METHOD_ONLY_CCW + METHOD_ONLY_CCW, + METHOD_CCW_INNER_JUST_AFTER_CW_OUTER } OrganizePolygonMethod; /** @@ -835,7 +1008,8 @@ typedef enum * * Analyse a set of rings (passed as simple polygons), and based on a * geometric analysis convert them into a polygon with inner rings, - * or a MultiPolygon if dealing with more than one polygon. + * (or a MultiPolygon if dealing with more than one polygon) that follow the + * OGC Simple Feature specification. * * All the input geometries must be OGRPolygons with only a valid exterior * ring (at least 4 points) and no interior rings. @@ -854,7 +1028,15 @@ typedef enum * to the option list (the result of the function will be a multi-polygon with all polygons * as toplevel polygons) or only make it analyze counterclockwise polygons by adding * METHOD=ONLY_CCW to the option list if you can assume that the outline - * of holes is counterclockwise defined (this is the convention for shapefiles e.g.) + * of holes is counterclockwise defined (this is the convention for example in shapefiles, + * Personal Geodatabases or File Geodatabases). + * + * For FileGDB, in most cases, but not always, a faster method than ONLY_CCW can be used. It is + * CCW_INNER_JUST_AFTER_CW_OUTER. When using it, inner rings are assumed to be + * counterclockwise oriented, and following immediately the outer ring (clockwise + * oriented) that they belong to. If that assumption is not met, an inner ring + * could be attached to the wrong outer ring, so this method must be used + * with care. * * If the OGR_ORGANIZE_POLYGONS configuration option is defined, its value will override * the value of the METHOD option of papszOptions (usefull to modify the behaviour of the @@ -940,6 +1122,10 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, { method = METHOD_ONLY_CCW; } + else if (EQUAL(pszMethodValue, "CCW_INNER_JUST_AFTER_CW_OUTER")) + { + method = METHOD_CCW_INNER_JUST_AFTER_CW_OUTER; + } else if (!EQUAL(pszMethodValue, "DEFAULT")) { CPLError(CE_Warning, CPLE_AppDefined, @@ -960,7 +1146,8 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, && ((OGRPolygon *) papoPolygons[i])->getNumInteriorRings() == 0 && ((OGRPolygon *)papoPolygons[i])->getExteriorRing()->getNumPoints() >= 4) { - asPolyEx[i].dfArea = asPolyEx[i].poPolygon->get_Area(); + if( method != METHOD_CCW_INNER_JUST_AFTER_CW_OUTER ) + asPolyEx[i].dfArea = asPolyEx[i].poPolygon->get_Area(); asPolyEx[i].poExteriorRing = asPolyEx[i].poPolygon->getExteriorRing(); asPolyEx[i].poExteriorRing->getPoint(0, &asPolyEx[i].poAPoint); asPolyEx[i].bIsCW = asPolyEx[i].poExteriorRing->isClockwise(); @@ -990,14 +1177,15 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, /* If we are in ONLY_CCW mode and that we have found that there is only one outer ring, */ /* then it is pretty easy : we can assume that all other rings are inside */ - if (method == METHOD_ONLY_CCW && nCountCWPolygon == 1 && bUseFastVersion) + if ((method == METHOD_ONLY_CCW || method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER) && + nCountCWPolygon == 1 && bUseFastVersion && !bNonPolygon ) { geom = asPolyEx[indexOfCWPolygon].poPolygon; for(i=0; iaddRing(asPolyEx[i].poPolygon->getExteriorRing()); + ((OGRPolygon*)geom)->addRingDirectly(asPolyEx[i].poPolygon->stealExteriorRing()); delete asPolyEx[i].poPolygon; } } @@ -1007,6 +1195,55 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, return geom; } + if( method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER && !bNonPolygon && asPolyEx[0].bIsCW ) + { + /* Inner rings are CCW oriented and follow immediately the outer */ + /* ring (that is CW oriented) in which they are included */ + OGRMultiPolygon* poMulti = NULL; + OGRPolygon* poCur = asPolyEx[0].poPolygon; + OGRGeometry* poRet = poCur; + /* We have already checked that the first ring is CW */ + OGREnvelope* psEnvelope = &(asPolyEx[0].sEnvelope); + for(i=1;iaddGeometryDirectly(poCur); + } + poCur = asPolyEx[i].poPolygon; + poMulti->addGeometryDirectly(poCur); + psEnvelope = &(asPolyEx[i].sEnvelope); + } + else + { + poCur->addRingDirectly(asPolyEx[i].poPolygon->stealExteriorRing()); + if(!(asPolyEx[i].poAPoint.getX() >= psEnvelope->MinX && + asPolyEx[i].poAPoint.getX() <= psEnvelope->MaxX && + asPolyEx[i].poAPoint.getY() >= psEnvelope->MinY && + asPolyEx[i].poAPoint.getY() <= psEnvelope->MaxY)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Part %d does not respect CCW_INNER_JUST_AFTER_CW_OUTER rule", i); + } + delete asPolyEx[i].poPolygon; + } + } + delete [] asPolyEx; + if (pbIsValidGeometry) + *pbIsValidGeometry = TRUE; + return poRet; + } + else if( method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER && !bNonPolygon ) + { + method = METHOD_ONLY_CCW; + for(i=0;iget_Area(); + } + /* Emits a warning if the number of parts is sufficiently big to anticipate for */ /* very long computation time, and the user didn't specify an explicit method */ if (nPolygonCount > N_CRITICAL_PART_NUMBER && method == METHOD_NORMAL && pszMethodValue == NULL) @@ -1109,9 +1346,11 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, { if (bUseFastVersion) { - /* Note that isPointInRing only test strict inclusion in the ring */ - if (asPolyEx[j].poExteriorRing->isPointInRing(&asPolyEx[i].poAPoint, FALSE)) + if( method == METHOD_ONLY_CCW && j == 0 ) { + /* We are testing if a CCW ring is in the biggest CW ring */ + /* It *must* be inside as this is the last candidate, otherwise */ + /* the winding order rules is broken */ b_i_inside_j = TRUE; } else if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&asPolyEx[i].poAPoint, FALSE)) @@ -1122,22 +1361,56 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, { OGRPoint point; asPolyEx[i].poExteriorRing->getPoint(k, &point); - if (asPolyEx[j].poExteriorRing->isPointInRing(&point, FALSE)) + if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&point, FALSE)) + { + /* If it is on the boundary of j, iterate again */ + } + else if (asPolyEx[j].poExteriorRing->isPointInRing(&point, FALSE)) { /* If then point is strictly included in j, then i is considered inside j */ b_i_inside_j = TRUE; break; } - else if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&point, FALSE)) - { - /* If it is on the boundary of j, iterate again */ - } - else + else { /* If it is outside, then i cannot be inside j */ break; } } + if( !b_i_inside_j && k == nPoints && nPoints > 2 ) + { + /* all points of i are on the boundary of j ... */ + /* take a point in the middle of a segment of i and */ + /* test it against j */ + for(k=0;kgetPoint(k, &point1); + asPolyEx[i].poExteriorRing->getPoint(k+1, &point2); + pointMiddle.setX((point1.getX() + point2.getX()) / 2); + pointMiddle.setY((point1.getY() + point2.getY()) / 2); + if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&pointMiddle, FALSE)) + { + /* If it is on the boundary of j, iterate again */ + } + else if (asPolyEx[j].poExteriorRing->isPointInRing(&pointMiddle, FALSE)) + { + /* If then point is strictly included in j, then i is considered inside j */ + b_i_inside_j = TRUE; + break; + } + else + { + /* If it is outside, then i cannot be inside j */ + break; + } + } + } + } + /* Note that isPointInRing only test strict inclusion in the ring */ + else if (asPolyEx[j].poExteriorRing->isPointInRing(&asPolyEx[i].poAPoint, FALSE)) + { + b_i_inside_j = TRUE; } } else if (asPolyEx[j].poPolygon->Contains(asPolyEx[i].poPolygon)) @@ -1239,8 +1512,8 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, { if (asPolyEx[i].bIsTopLevel == FALSE) { - asPolyEx[i].poEnclosingPolygon->addRing( - asPolyEx[i].poPolygon->getExteriorRing()); + asPolyEx[i].poEnclosingPolygon->addRingDirectly( + asPolyEx[i].poPolygon->stealExteriorRing()); delete asPolyEx[i].poPolygon; } else if (nCountTopLevel == 1) @@ -1287,6 +1560,17 @@ OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, * on the forms of GML geometries supported by this parser, but they are * too numerous to list here. * + * The following GML2 elements are parsed : Point, LineString, Polygon, + * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. + * + * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface, + * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, LineStringSegment, Arc, + * Circle, CompositeSurface, OrientableSurface, Solid, Tin, TriangulatedSurface. + * + * Arc and Circle elements are stroked to linestring, by using a + * 4 degrees step, unless the user has overridden the value with the + * OGR_ARC_STEPSIZE configuration variable. + * * The C function OGR_G_CreateFromGML() is the same as this method. * * @param pszData The GML fragment for the geometry. @@ -1309,7 +1593,7 @@ OGRGeometry *OGRGeometryFactory::createFromGML( const char *pszData ) /************************************************************************/ OGRGeometry * -OGRGeometryFactory::createFromGEOS( GEOSGeom geosGeom ) +OGRGeometryFactory::createFromGEOS( GEOSContextHandle_t hGEOSCtxt, GEOSGeom geosGeom ) { #ifndef HAVE_GEOS @@ -1324,7 +1608,21 @@ OGRGeometryFactory::createFromGEOS( GEOSGeom geosGeom ) unsigned char *pabyBuf = NULL; OGRGeometry *poGeometry = NULL; - pabyBuf = GEOSGeomToWKB_buf( geosGeom, &nSize ); + /* Special case as POINT EMPTY cannot be translated to WKB */ + if (GEOSGeomTypeId_r(hGEOSCtxt, geosGeom) == GEOS_POINT && + GEOSisEmpty_r(hGEOSCtxt, geosGeom)) + return new OGRPoint(); + +#if GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3) + /* GEOSGeom_getCoordinateDimension only available in GEOS 3.3.0 (unreleased at time of writing) */ + int nCoordDim = GEOSGeom_getCoordinateDimension_r(hGEOSCtxt, geosGeom); + GEOSWKBWriter* wkbwriter = GEOSWKBWriter_create_r(hGEOSCtxt); + GEOSWKBWriter_setOutputDimension_r(hGEOSCtxt, wkbwriter, nCoordDim); + pabyBuf = GEOSWKBWriter_write_r(hGEOSCtxt, wkbwriter, geosGeom, &nSize ); + GEOSWKBWriter_destroy_r(hGEOSCtxt, wkbwriter); +#else + pabyBuf = GEOSGeomToWKB_buf_r( hGEOSCtxt, geosGeom, &nSize ); +#endif if( pabyBuf == NULL || nSize == 0 ) { return NULL; @@ -1339,8 +1637,9 @@ OGRGeometryFactory::createFromGEOS( GEOSGeom geosGeom ) if( pabyBuf != NULL ) { + /* Since GEOS 3.1.1, so we test 3.2.0 */ #if GEOS_CAPI_VERSION_MAJOR >= 2 || (GEOS_CAPI_VERSION_MAJOR == 1 && GEOS_CAPI_VERSION_MINOR >= 6) - GEOSFree( pabyBuf ); + GEOSFree_r( hGEOSCtxt, pabyBuf ); #else free( pabyBuf ); #endif @@ -1351,18 +1650,6 @@ OGRGeometryFactory::createFromGEOS( GEOSGeom geosGeom ) #endif /* HAVE_GEOS */ } -/************************************************************************/ -/* getGEOSGeometryFactory() */ -/************************************************************************/ - -void *OGRGeometryFactory::getGEOSGeometryFactory() - -{ - // XXX - mloskot - What to do with this call - // after GEOS C++ API has been stripped? - return NULL; -} - /************************************************************************/ /* haveGEOS() */ /************************************************************************/ @@ -1419,6 +1706,22 @@ OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, int nBytes, int *pnBytesConsumed ) +{ + return createFromFgfInternal(pabyData, poSR, ppoReturn, nBytes, + pnBytesConsumed, 0); +} + + +/************************************************************************/ +/* createFromFgfInternal() */ +/************************************************************************/ + +OGRErr OGRGeometryFactory::createFromFgfInternal( unsigned char *pabyData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn, + int nBytes, + int *pnBytesConsumed, + int nRecLevel ) { OGRErr eErr = OGRERR_NONE; OGRGeometry *poGeom = NULL; @@ -1428,6 +1731,15 @@ OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, (void) iOrdinal; + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing FGF geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } + *ppoReturn = NULL; if( nBytes < 4 ) @@ -1581,18 +1893,27 @@ OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, for( iRing = 0; iRing < nRingCount; iRing++ ) { if( nBytes - nNextByte < 4 ) + { + delete poGeom; return OGRERR_NOT_ENOUGH_DATA; + } memcpy( &nPointCount, pabyData + nNextByte, 4 ); CPL_LSBPTR32( &nPointCount ); if (nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8)) + { + delete poGeom; return OGRERR_CORRUPT_DATA; + } nNextByte += 4; if( nBytes - nNextByte < nTupleSize * 8 * nPointCount ) + { + delete poGeom; return OGRERR_NOT_ENOUGH_DATA; + } poLR = new OGRLinearRing(); poLR->setNumPoints( nPointCount ); @@ -1631,15 +1952,6 @@ OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, GInt32 nGeomCount; int iGeom, nBytesUsed; - if( nGType == 4 ) - poGC = new OGRMultiPoint(); - else if( nGType == 5 ) - poGC = new OGRMultiLineString(); - else if( nGType == 6 ) - poGC = new OGRMultiPolygon(); - else if( nGType == 7 ) - poGC = new OGRGeometryCollection(); - if( nBytes < 8 ) return OGRERR_NOT_ENOUGH_DATA; @@ -1655,13 +1967,22 @@ OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, nBytesUsed = 8; + if( nGType == 4 ) + poGC = new OGRMultiPoint(); + else if( nGType == 5 ) + poGC = new OGRMultiLineString(); + else if( nGType == 6 ) + poGC = new OGRMultiPolygon(); + else if( nGType == 7 ) + poGC = new OGRGeometryCollection(); + for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) { int nThisGeomSize; OGRGeometry *poThisGeom = NULL; - eErr = createFromFgf( pabyData + nBytesUsed, poSR, &poThisGeom, - nBytes - nBytesUsed, &nThisGeomSize); + eErr = createFromFgfInternal( pabyData + nBytesUsed, poSR, &poThisGeom, + nBytes - nBytesUsed, &nThisGeomSize, nRecLevel + 1); if( eErr != OGRERR_NONE ) { delete poGC; @@ -1673,6 +1994,7 @@ OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, if( eErr != OGRERR_NONE ) { delete poGC; + delete poThisGeom; return eErr; } } @@ -1725,57 +2047,168 @@ OGRErr CPL_DLL OGR_G_CreateFromFgf( unsigned char *pabyData, nBytes, pnBytesConsumed ); } - /************************************************************************/ -/* Add360ToNegLon() */ +/* SplitLineStringAtDateline() */ /************************************************************************/ -static void Add360ToNegLon( OGRGeometry* poGeom ) +#define SWAP_DBL(a,b) do { double tmp = a; a = b; b = tmp; } while(0) + +static void SplitLineStringAtDateline(OGRGeometryCollection* poMulti, + const OGRLineString* poLS, + double dfDateLineOffset) { - switch (wkbFlatten(poGeom->getGeometryType())) + double dfLeftBorderX = 180 - dfDateLineOffset; + double dfRightBorderX = -180 + dfDateLineOffset; + double dfDiffSpace = 360 - dfDateLineOffset; + + int i; + int bIs3D = poLS->getCoordinateDimension() == 3; + OGRLineString* poNewLS = new OGRLineString(); + poMulti->addGeometryDirectly(poNewLS); + for(i=0;igetNumPoints();i++) { - case wkbPolygon: - case wkbMultiLineString: - case wkbMultiPolygon: - case wkbGeometryCollection: + double dfX = poLS->getX(i); + if (i > 0 && fabs(dfX - poLS->getX(i-1)) > dfDiffSpace) { - int nSubGeomCount = OGR_G_GetGeometryCount((OGRGeometryH)poGeom); - for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ ) + double dfX1 = poLS->getX(i-1); + double dfY1 = poLS->getY(i-1); + double dfZ1 = poLS->getY(i-1); + double dfX2 = poLS->getX(i); + double dfY2 = poLS->getY(i); + double dfZ2 = poLS->getY(i); + + if (dfX1 > -180 && dfX1 < dfRightBorderX && dfX2 == 180 && + i+1 < poLS->getNumPoints() && + poLS->getX(i+1) > -180 && poLS->getX(i+1) < dfRightBorderX) { - Add360ToNegLon((OGRGeometry*)OGR_G_GetGeometryRef((OGRGeometryH)poGeom, iGeom)); + if( bIs3D ) + poNewLS->addPoint(-180, poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(-180, poLS->getY(i)); + + i++; + + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i), poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(poLS->getX(i), poLS->getY(i)); + continue; + } + else if (dfX1 > dfLeftBorderX && dfX1 < 180 && dfX2 == -180 && + i+1 < poLS->getNumPoints() && + poLS->getX(i+1) > dfLeftBorderX && poLS->getX(i+1) < 180) + { + if( bIs3D ) + poNewLS->addPoint(180, poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(180, poLS->getY(i)); + + i++; + + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i), poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(poLS->getX(i), poLS->getY(i)); + continue; + } + + if (dfX1 < dfRightBorderX && dfX2 > dfLeftBorderX) + { + SWAP_DBL(dfX1, dfX2); + SWAP_DBL(dfY1, dfY2); + SWAP_DBL(dfZ1, dfZ2); + } + if (dfX1 > dfLeftBorderX && dfX2 < dfRightBorderX) + dfX2 += 360; + + if (dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2) + { + double dfRatio = (180 - dfX1) / (dfX2 - dfX1); + double dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1; + double dfZ = dfRatio * dfZ2 + (1 - dfRatio) * dfZ1; + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? 180 : -180, dfY, dfZ); + else + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? 180 : -180, dfY); + poNewLS = new OGRLineString(); + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? -180 : 180, dfY, dfZ); + else + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? -180 : 180, dfY); + poMulti->addGeometryDirectly(poNewLS); + } + else + { + poNewLS = new OGRLineString(); + poMulti->addGeometryDirectly(poNewLS); } - - break; } - - case wkbLineString: - case wkbLinearRing: + if( bIs3D ) + poNewLS->addPoint(dfX, poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(dfX, poLS->getY(i)); + } +} + +/************************************************************************/ +/* FixPolygonCoordinatesAtDateLine() */ +/************************************************************************/ + +#ifdef HAVE_GEOS +static void FixPolygonCoordinatesAtDateLine(OGRPolygon* poPoly, double dfDateLineOffset) +{ + double dfLeftBorderX = 180 - dfDateLineOffset; + double dfRightBorderX = -180 + dfDateLineOffset; + double dfDiffSpace = 360 - dfDateLineOffset; + + int i, iPart; + for(iPart = 0; iPart < 1 + poPoly->getNumInteriorRings(); iPart++) + { + OGRLineString* poLS = (iPart == 0) ? poPoly->getExteriorRing() : + poPoly->getInteriorRing(iPart-1); + int bGoEast = FALSE; + int bIs3D = poLS->getCoordinateDimension() == 3; + for(i=1;igetNumPoints();i++) { - OGRLineString* poLineString = (OGRLineString* )poGeom; - int nPointCount = poLineString->getNumPoints(); - int nCoordDim = poLineString->getCoordinateDimension(); - for( int iPoint = 0; iPoint < nPointCount; iPoint++) + double dfX = poLS->getX(i); + double dfPrevX = poLS->getX(i-1); + double dfDiffLong = fabs(dfX - dfPrevX); + if (dfDiffLong > dfDiffSpace) { - if (poLineString->getX(iPoint) < -90) + if ((dfPrevX > dfLeftBorderX && dfX < dfRightBorderX) || (dfX < 0 && bGoEast)) { - if (nCoordDim == 2) - poLineString->setPoint(iPoint, - poLineString->getX(iPoint) + 360, - poLineString->getY(iPoint)); + dfX += 360; + bGoEast = TRUE; + if( bIs3D ) + poLS->setPoint(i, dfX, poLS->getY(i), poLS->getZ(i)); else - poLineString->setPoint(iPoint, - poLineString->getX(iPoint) + 360, - poLineString->getY(iPoint), - poLineString->getZ(iPoint)); + poLS->setPoint(i, dfX, poLS->getY(i)); + } + else if (dfPrevX < dfRightBorderX && dfX > dfLeftBorderX) + { + int j; + for(j=i-1;j>=0;j--) + { + dfX = poLS->getX(j); + if (dfX < 0) + { + if( bIs3D ) + poLS->setPoint(j, dfX + 360, poLS->getY(j), poLS->getZ(j)); + else + poLS->setPoint(j, dfX + 360, poLS->getY(j)); + } + } + bGoEast = FALSE; + } + else + { + bGoEast = FALSE; } } - break; } - - default: - break; } } +#endif /************************************************************************/ /* Sub360ToLon() */ @@ -1863,14 +2296,17 @@ static void AddSimpleGeomToMulti(OGRGeometryCollection* poMulti, /************************************************************************/ static void CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection* poMulti, - const OGRGeometry* poGeom) + const OGRGeometry* poGeom, + double dfDateLineOffset) { - switch (wkbFlatten(poGeom->getGeometryType())) + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + switch (eGeomType) { case wkbPolygon: case wkbLineString: { int bWrapDateline = FALSE; + int bSplitLineStringAtDateline = FALSE; OGREnvelope oEnvelope; poGeom->getEnvelope(&oEnvelope); @@ -1878,23 +2314,68 @@ static void CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection* poMulti, /* Naive heuristics... Place to improvement... */ OGRGeometry* poDupGeom = NULL; - if (oEnvelope.MinX < -170 && oEnvelope.MaxX > 170) + double dfLeftBorderX = 180 - dfDateLineOffset; + double dfRightBorderX = -180 + dfDateLineOffset; + double dfDiffSpace = 360 - dfDateLineOffset; + + if (oEnvelope.MinX > dfLeftBorderX && oEnvelope.MaxX > 180) { +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); +#else bWrapDateline = TRUE; - poDupGeom = poGeom->clone(); - Add360ToNegLon(poDupGeom); +#endif } - else if (oEnvelope.MinX > 170 && oEnvelope.MaxX > 180) - bWrapDateline = TRUE; - - if (bWrapDateline) + else { + OGRLineString* poLS; + if (eGeomType == wkbPolygon) + poLS = ((OGRPolygon*)poGeom)->getExteriorRing(); + else + poLS = (OGRLineString*)poGeom; + if (poLS) + { + int i; + double dfMaxSmallDiffLong = 0; + int bHasBigDiff = FALSE; + /* Detect big gaps in longitude */ + for(i=1;igetNumPoints();i++) + { + double dfPrevX = poLS->getX(i-1); + double dfX = poLS->getX(i); + double dfDiffLong = fabs(dfX - dfPrevX); + if (dfDiffLong > dfDiffSpace && + ((dfX > dfLeftBorderX && dfPrevX < dfRightBorderX) || (dfPrevX > dfLeftBorderX && dfX < dfRightBorderX))) + bHasBigDiff = TRUE; + else if (dfDiffLong > dfMaxSmallDiffLong) + dfMaxSmallDiffLong = dfDiffLong; + } + if (bHasBigDiff && dfMaxSmallDiffLong < dfDateLineOffset) + { + if (eGeomType == wkbLineString) + bSplitLineStringAtDateline = TRUE; + else + { #ifndef HAVE_GEOS - CPLError( CE_Failure, CPLE_NotSupported, - "GEOS support not enabled." ); - - poMulti->addGeometry(poGeom); + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); #else + bWrapDateline = TRUE; + poDupGeom = poGeom->clone(); + FixPolygonCoordinatesAtDateLine((OGRPolygon*)poDupGeom, dfDateLineOffset); +#endif + } + } + } + } + + if (bSplitLineStringAtDateline) + { + SplitLineStringAtDateline(poMulti, (OGRLineString*)poGeom, dfDateLineOffset); + } + else if (bWrapDateline) + { const OGRGeometry* poWorkGeom = (poDupGeom) ? poDupGeom : poGeom; OGRGeometry* poRectangle1 = NULL; OGRGeometry* poRectangle2 = NULL; @@ -1921,7 +2402,6 @@ static void CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection* poMulti, delete poGeom1; delete poGeom2; delete poDupGeom; -#endif } else { @@ -1939,7 +2419,7 @@ static void CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection* poMulti, { OGRGeometry* poSubGeom = (OGRGeometry*)OGR_G_GetGeometryRef((OGRGeometryH)poGeom, iGeom); - CutGeometryOnDateLineAndAddToMulti(poMulti, poSubGeom); + CutGeometryOnDateLineAndAddToMulti(poMulti, poSubGeom, dfDateLineOffset); } break; } @@ -1958,11 +2438,14 @@ OGRGeometry* OGRGeometryFactory::transformWithOptions( const OGRGeometry* poSrcG char** papszOptions ) { OGRGeometry* poDstGeom = poSrcGeom->clone(); - OGRErr eErr = poDstGeom->transform(poCT); - if (eErr != OGRERR_NONE) + if (poCT != NULL) { - delete poDstGeom; - return NULL; + OGRErr eErr = poDstGeom->transform(poCT); + if (eErr != OGRERR_NONE) + { + delete poDstGeom; + return NULL; + } } if (CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "NO"))) @@ -1979,7 +2462,11 @@ OGRGeometry* OGRGeometryFactory::transformWithOptions( const OGRGeometry* poSrcG OGRGeometryCollection* poMulti = (OGRGeometryCollection* )createGeometry(eNewType); - CutGeometryOnDateLineAndAddToMulti(poMulti, poDstGeom); + double dfDateLineOffset = CPLAtofM(CSLFetchNameValueDef(papszOptions, "DATELINEOFFSET", "10")); + if(dfDateLineOffset <= 0 || dfDateLineOffset >= 360) + dfDateLineOffset = 10; + + CutGeometryOnDateLineAndAddToMulti(poMulti, poDstGeom, dfDateLineOffset); if (poMulti->getNumGeometries() == 0) { @@ -2029,6 +2516,8 @@ OGRGeometry* OGRGeometryFactory::transformWithOptions( const OGRGeometry* poSrcG * arc, zero to use the default setting. * * @return OGRLineString geometry representing an approximation of the arc. + * + * @since OGR 1.8.0 */ OGRGeometry* OGRGeometryFactory::approximateArcAngles( @@ -2093,16 +2582,179 @@ OGRGeometry* OGRGeometryFactory::approximateArcAngles( /* OGR_G_ApproximateArcAngles() */ /************************************************************************/ +/** + * Stroke arc to linestring. + * + * Stroke an arc of a circle to a linestring based on a center + * point, radius, start angle and end angle, all angles in degrees. + * + * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be + * used. This is currently 4 degrees unless the user has overridden the + * value with the OGR_ARC_STEPSIZE configuration variable. + * + * @see CPLSetConfigOption() + * + * @param dfCenterX center X + * @param dfCenterY center Y + * @param dfZ center Z + * @param dfPrimaryRadius X radius of ellipse. + * @param dfSecondaryRadius Y radius of ellipse. + * @param dfRotation rotation of the ellipse clockwise. + * @param dfStartAngle angle to first point on arc (clockwise of X-positive) + * @param dfEndAngle angle to last point on arc (clockwise of X-positive) + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * + * @return OGRLineString geometry representing an approximation of the arc. + * + * @since OGR 1.8.0 + */ + OGRGeometryH CPL_DLL OGR_G_ApproximateArcAngles( double dfCenterX, double dfCenterY, double dfZ, - double dfPrimaryRadius, double dfSecondaryAxis, double dfRotation, + double dfPrimaryRadius, double dfSecondaryRadius, double dfRotation, double dfStartAngle, double dfEndAngle, double dfMaxAngleStepSizeDegrees ) { return (OGRGeometryH) OGRGeometryFactory::approximateArcAngles( dfCenterX, dfCenterY, dfZ, - dfPrimaryRadius, dfSecondaryAxis, dfRotation, + dfPrimaryRadius, dfSecondaryRadius, dfRotation, dfStartAngle, dfEndAngle, dfMaxAngleStepSizeDegrees ); } + +/************************************************************************/ +/* forceToLineString() */ +/************************************************************************/ + +/** + * \brief Convert to line string. + * + * Tries to force the provided geometry to be a line string. Currently + * this just effects a change on multilinestrings. The passed in geometry is + * consumed and a new one returned (or potentially the same one). + * + * @param poGeom the input geometry - ownership is passed to the method. + * @param bOnlyInOrder flag that, if set to FALSE, indicate that the order of + * points in a linestring might be reversed if it enables + * to match the extremity of another linestring. If set + * to TRUE, the start of a linestring must match the end + * of another linestring. + * @return new geometry. + */ + +OGRGeometry *OGRGeometryFactory::forceToLineString( OGRGeometry *poGeom, bool bOnlyInOrder ) + +{ + if( poGeom == NULL ) + return NULL; + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + + if( eGeomType != wkbGeometryCollection + && eGeomType != wkbMultiLineString ) + return poGeom; + + // build an aggregated linestring from all the linestrings in the container. + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + + int iGeom0 = 0; + while( iGeom0 < poGC->getNumGeometries() ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom0)->getGeometryType()) + != wkbLineString ) + { + iGeom0++; + continue; + } + + OGRLineString *poLineString0 = (OGRLineString *) poGC->getGeometryRef(iGeom0); + if( poLineString0->getNumPoints() < 2 ) + { + iGeom0++; + continue; + } + + OGRPoint pointStart0, pointEnd0; + poLineString0->StartPoint( &pointStart0 ); + poLineString0->EndPoint( &pointEnd0 ); + + int iGeom1; + for( iGeom1 = iGeom0 + 1; iGeom1 < poGC->getNumGeometries(); iGeom1++ ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom1)->getGeometryType()) + != wkbLineString ) + continue; + + OGRLineString *poLineString1 = (OGRLineString *) poGC->getGeometryRef(iGeom1); + if( poLineString1->getNumPoints() < 2 ) + continue; + + OGRPoint pointStart1, pointEnd1; + poLineString1->StartPoint( &pointStart1 ); + poLineString1->EndPoint( &pointEnd1 ); + + if ( !bOnlyInOrder && + ( pointEnd0.Equals( &pointEnd1 ) || pointStart0.Equals( &pointStart1 ) ) ) + { + poLineString1->reversePoints(); + poLineString1->StartPoint( &pointStart1 ); + poLineString1->EndPoint( &pointEnd1 ); + } + + if ( pointEnd0.Equals( &pointStart1 ) ) + { + poLineString0->addSubLineString( poLineString1, 1 ); + poGC->removeGeometry( iGeom1 ); + break; + } + + if( pointEnd1.Equals( &pointStart0 ) ) + { + poLineString1->addSubLineString( poLineString0, 1 ); + poGC->removeGeometry( iGeom0 ); + break; + } + } + + if ( iGeom1 == poGC->getNumGeometries() ) + { + iGeom0++; + } + } + + if ( poGC->getNumGeometries() == 1 ) + { + OGRLineString *poLineString = (OGRLineString *) poGC->getGeometryRef(0); + poGC->removeGeometry( 0, FALSE ); + delete poGC; + + return poLineString; + } + + return poGC; +} + +/************************************************************************/ +/* OGR_G_ForceToLineString() */ +/************************************************************************/ + +/** + * \brief Convert to line string. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToLineString(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.10.0 + */ + +OGRGeometryH OGR_G_ForceToLineString( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToLineString( (OGRGeometry *) hGeom ); +} diff --git a/ogr/ogrgeomfielddefn.cpp b/ogr/ogrgeomfielddefn.cpp new file mode 100644 index 0000000..fb89508 --- /dev/null +++ b/ogr/ogrgeomfielddefn.cpp @@ -0,0 +1,494 @@ +/****************************************************************************** + * $Id: ogrgeomfielddefn.cpp 27110 2014-03-28 21:29:20Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRGeomFieldDefn class implementation. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_feature.h" +#include "ogr_api.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrgeomfielddefn.cpp 27110 2014-03-28 21:29:20Z rouault $"); + +/************************************************************************/ +/* OGRGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * @param pszNameIn the name of the new field. + * @param eGeomTypeIn the type of the new field. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefn::OGRGeomFieldDefn( const char * pszNameIn, + OGRwkbGeometryType eGeomTypeIn ) + +{ + Initialize( pszNameIn, eGeomTypeIn ); +} + +/************************************************************************/ +/* OGRGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * Create by cloning an existing geometry field definition. + * + * @param poPrototype the geometry field definition to clone. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefn::OGRGeomFieldDefn( OGRGeomFieldDefn *poPrototype ) + +{ + Initialize( poPrototype->GetNameRef(), poPrototype->GetType() ); + SetSpatialRef( poPrototype->GetSpatialRef() ); +} + +/************************************************************************/ +/* OGR_GFld_Create() */ +/************************************************************************/ +/** + * \brief Create a new field geometry definition. + * + * This function is the same as the CPP method OGRGeomFieldDefn::OGRGeomFieldDefn(). + * + * @param pszName the name of the new field definition. + * @param eType the type of the new field definition. + * @return handle to the new field definition. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefnH OGR_GFld_Create( const char *pszName, + OGRwkbGeometryType eType ) + +{ + return (OGRGeomFieldDefnH) (new OGRGeomFieldDefn(pszName,eType)); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void OGRGeomFieldDefn::Initialize( const char * pszNameIn, + OGRwkbGeometryType eTypeIn ) + +{ + pszName = CPLStrdup( pszNameIn ); + eGeomType = eTypeIn; + poSRS = NULL; + bIgnore = FALSE; +} + +/************************************************************************/ +/* ~OGRGeomFieldDefn() */ +/************************************************************************/ + +OGRGeomFieldDefn::~OGRGeomFieldDefn() + +{ + CPLFree( pszName ); + + if( NULL != poSRS ) + poSRS->Release(); +} + +/************************************************************************/ +/* OGR_GFld_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy a geometry field definition. + * + * @param hDefn handle to the geometry field definition to destroy. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_Destroy( OGRGeomFieldDefnH hDefn ) + +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_Destroy" ); + delete (OGRGeomFieldDefn *) hDefn; +} + +/************************************************************************/ +/* SetName() */ +/************************************************************************/ + +/** + * \brief Reset the name of this field. + * + * This method is the same as the C function OGR_GFld_SetName(). + * + * @param pszNameIn the new name to apply. + * + * @since GDAL 1.11 + */ + +void OGRGeomFieldDefn::SetName( const char * pszNameIn ) + +{ + CPLFree( pszName ); + pszName = CPLStrdup( pszNameIn ); +} + +/************************************************************************/ +/* OGR_GFld_SetName() */ +/************************************************************************/ +/** + * \brief Reset the name of this field. + * + * This function is the same as the CPP method OGRGeomFieldDefn::SetName(). + * + * @param hDefn handle to the geometry field definition to apply the new name to. + * @param pszName the new name to apply. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetName( OGRGeomFieldDefnH hDefn, const char *pszName ) + +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetName" ); + ((OGRGeomFieldDefn *) hDefn)->SetName( pszName ); +} + +/************************************************************************/ +/* GetNameRef() */ +/************************************************************************/ + +/** + * \fn const char *OGRGeomFieldDefn::GetNameRef(); + * + * \brief Fetch name of this field. + * + * This method is the same as the C function OGR_GFld_GetNameRef(). + * + * @return pointer to an internal name string that should not be freed or + * modified. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_GetNameRef() */ +/************************************************************************/ +/** + * \brief Fetch name of this field. + * + * This function is the same as the CPP method OGRGeomFieldDefn::GetNameRef(). + * + * @param hDefn handle to the geometry field definition. + * @return the name of the geometry field definition. + * + * @since GDAL 1.11 + */ + +const char *OGR_GFld_GetNameRef( OGRGeomFieldDefnH hDefn ) + +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_GetNameRef", "" ); + return ((OGRGeomFieldDefn *) hDefn)->GetNameRef(); +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +/** + * \fn OGRwkbGeometryType OGRGeomFieldDefn::GetType(); + * + * \brief Fetch geometry type of this field. + * + * This method is the same as the C function OGR_GFld_GetType(). + * + * @return field geometry type. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_GetType() */ +/************************************************************************/ +/** + * \brief Fetch geometry type of this field. + * + * This function is the same as the CPP method OGRGeomFieldDefn::GetType(). + * + * @param hDefn handle to the geometry field definition to get type from. + * @return field geometry type. + * + * @since GDAL 1.11 + */ + +OGRwkbGeometryType OGR_GFld_GetType( OGRGeomFieldDefnH hDefn ) + +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_GetType", wkbUnknown ); + return ((OGRGeomFieldDefn *) hDefn)->GetType(); +} + +/************************************************************************/ +/* SetType() */ +/************************************************************************/ + +/** + * \fn void OGRGeomFieldDefn::SetType( OGRwkbGeometryType eType ); + * + * \brief Set the geometry type of this field. + * This should never be done to an OGRGeomFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This method is the same as the C function OGR_GFld_SetType(). + * + * @param eType the new field geometry type. + * + * @since GDAL 1.11 + */ + +void OGRGeomFieldDefn::SetType( OGRwkbGeometryType eTypeIn ) + +{ + eGeomType = eTypeIn; +} + +/************************************************************************/ +/* OGR_GFld_SetType() */ +/************************************************************************/ +/** + * \brief Set the geometry type of this field. + * This should never be done to an OGRGeomFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This function is the same as the CPP method OGRGeomFieldDefn::SetType(). + * + * @param hDefn handle to the geometry field definition to set type to. + * @param eType the new field geometry type. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetType( OGRGeomFieldDefnH hDefn, OGRwkbGeometryType eType ) + +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetType" ); + ((OGRGeomFieldDefn *) hDefn)->SetType( eType ); +} + +/************************************************************************/ +/* IsIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRGeomFieldDefn::IsIgnored(); + * + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_GFld_IsIgnored(). + * + * @return ignore state + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_IsIgnored() */ +/************************************************************************/ + +/** + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRGeomFieldDefn::IsIgnored(). + * + * @param hDefn handle to the geometry field definition + * @return ignore state + * + * @since GDAL 1.11 + */ + +int OGR_GFld_IsIgnored( OGRGeomFieldDefnH hDefn ) +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_IsIgnored", FALSE ); + return ((OGRGeomFieldDefn *) hDefn)->IsIgnored(); +} + +/************************************************************************/ +/* SetIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRGeomFieldDefn::SetIgnored( int ignore ); + * + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_GFld_SetIgnored(). + * + * @param ignore ignore state + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_SetIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRGeomFieldDefn::SetIgnored(). + * + * @param hDefn handle to the geometry field definition + * @param ignore ignore state + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetIgnored( OGRGeomFieldDefnH hDefn, int ignore ) +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetIgnored" ); + ((OGRGeomFieldDefn *) hDefn)->SetIgnored( ignore ); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ +/** + * \brief Fetch spatial reference system of this field. + * + * This method is the same as the C function OGR_GFld_GetSpatialRef(). + * + * @return field spatial reference system. + * + * @since GDAL 1.11 + */ + +OGRSpatialReference* OGRGeomFieldDefn::GetSpatialRef() +{ + return poSRS; +} + +/************************************************************************/ +/* OGR_GFld_GetSpatialRef() */ +/************************************************************************/ + +/** + * \brief Fetch spatial reference system of this field. + * + * This function is the same as the C++ method OGRGeomFieldDefn::GetSpatialRef(). + * + * @param hDefn handle to the geometry field definition + * + * @return field spatial reference system. + * + * @since GDAL 1.11 + */ + +OGRSpatialReferenceH OGR_GFld_GetSpatialRef( OGRGeomFieldDefnH hDefn ) +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_GetSpatialRef", NULL ); + return (OGRSpatialReferenceH) ((OGRGeomFieldDefn *) hDefn)->GetSpatialRef(); +} + +/************************************************************************/ +/* SetSpatialRef() */ +/************************************************************************/ + +/** + * \brief Set the spatial reference of this field. + * + * This method is the same as the C function OGR_GFld_SetSpatialRef(). + * + * This method drops the reference of the previously set SRS object and + * acquires a new reference on the passed object (if non-NULL). + * + * @param poSRSIn the new SRS to apply. + * + * @since GDAL 1.11 + */ +void OGRGeomFieldDefn::SetSpatialRef(OGRSpatialReference* poSRSIn) +{ + if( poSRS != NULL ) + poSRS->Release(); + poSRS = poSRSIn; + if( poSRS != NULL ) + poSRS->Reference(); +} + +/************************************************************************/ +/* OGR_GFld_SetSpatialRef() */ +/************************************************************************/ + +/** + * \brief Set the spatial reference of this field. + * + * This function is the same as the C++ method OGRGeomFieldDefn::SetSpatialRef(). + * + * This function drops the reference of the previously set SRS object and + * acquires a new reference on the passed object (if non-NULL). + * + * @param hDefn handle to the geometry field definition + * @param hSRS the new SRS to apply. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetSpatialRef( OGRGeomFieldDefnH hDefn, OGRSpatialReferenceH hSRS ) +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetSpatialRef" ); + ((OGRGeomFieldDefn *) hDefn)->SetSpatialRef( (OGRSpatialReference*) hSRS ); +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the geometry field definition is identical to the other one. + * + * @param poOtherFieldDefn the other field definition to compare to. + * @return TRUE if the geometry field definition is identical to the other one. + * + * @since GDAL 1.11 + */ + +int OGRGeomFieldDefn::IsSame( OGRGeomFieldDefn * poOtherFieldDefn ) +{ + if( !(strcmp(GetNameRef(), poOtherFieldDefn->GetNameRef()) == 0 && + GetType() == poOtherFieldDefn->GetType()) ) + return FALSE; + OGRSpatialReference* poMySRS = GetSpatialRef(); + OGRSpatialReference* poOtherSRS = poOtherFieldDefn->GetSpatialRef(); + return ((poMySRS == poOtherSRS) || + (poMySRS != NULL && poOtherSRS != NULL && + poMySRS->IsSame(poOtherSRS))); +} diff --git a/ogr/ogrlayer.cpp b/ogr/ogrlayer.cpp index fc467d4..18a464d 100644 --- a/ogr/ogrlayer.cpp +++ b/ogr/ogrlayer.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrlayer.cpp 17223 2009-06-07 19:41:08Z rouault $ + * $Id: ogrlayer.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The generic portions of the OGRSFLayer class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,8 +32,9 @@ #include "ogr_api.h" #include "ogr_p.h" #include "ogr_attrind.h" +#include "swq.h" -CPL_CVSID("$Id: ogrlayer.cpp 17223 2009-06-07 19:41:08Z rouault $"); +CPL_CVSID("$Id: ogrlayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); /************************************************************************/ /* OGRLayer() */ @@ -43,6 +45,7 @@ OGRLayer::OGRLayer() { m_poStyleTable = NULL; m_poAttrQuery = NULL; + m_pszAttrQueryString = NULL; m_poAttrIndex = NULL; m_nRefCount = 0; @@ -50,6 +53,8 @@ OGRLayer::OGRLayer() m_poFilterGeom = NULL; m_bFilterIsEnvelope = FALSE; + m_pPreparedFilterGeom = NULL; + m_iGeomFieldFilter = 0; } /************************************************************************/ @@ -77,11 +82,19 @@ OGRLayer::~OGRLayer() m_poAttrQuery = NULL; } + CPLFree( m_pszAttrQueryString ); + if( m_poFilterGeom ) { delete m_poFilterGeom; m_poFilterGeom = NULL; } + + if( m_pPreparedFilterGeom != NULL ) + { + OGRDestroyPreparedGeometry(m_pPreparedFilterGeom); + m_pPreparedFilterGeom = NULL; + } } /************************************************************************/ @@ -192,22 +205,43 @@ int OGR_L_GetFeatureCount( OGRLayerH hLayer, int bForce ) OGRErr OGRLayer::GetExtent(OGREnvelope *psExtent, int bForce ) +{ + return GetExtentInternal(0, psExtent, bForce); +} + +OGRErr OGRLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce ) + +{ + if( iGeomField == 0 ) + return GetExtent(psExtent, bForce); + else + return GetExtentInternal(iGeomField, psExtent, bForce); +} + +OGRErr OGRLayer::GetExtentInternal(int iGeomField, OGREnvelope *psExtent, int bForce ) + { OGRFeature *poFeature; OGREnvelope oEnv; GBool bExtentSet = FALSE; + psExtent->MinX = 0.0; + psExtent->MaxX = 0.0; + psExtent->MinY = 0.0; + psExtent->MaxY = 0.0; + /* -------------------------------------------------------------------- */ /* If this layer has a none geometry type, then we can */ /* reasonably assume there are not extents available. */ /* -------------------------------------------------------------------- */ - if( GetLayerDefn()->GetGeomType() == wkbNone ) + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) { - psExtent->MinX = 0.0; - psExtent->MaxX = 0.0; - psExtent->MinY = 0.0; - psExtent->MaxY = 0.0; - + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } return OGRERR_FAILURE; } @@ -225,13 +259,17 @@ OGRErr OGRLayer::GetExtent(OGREnvelope *psExtent, int bForce ) ResetReading(); while( (poFeature = GetNextFeature()) != NULL ) { - OGRGeometry *poGeom = poFeature->GetGeometryRef(); - if (poGeom && !bExtentSet) + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField); + if (poGeom == NULL || poGeom->IsEmpty()) + { + /* Do nothing */ + } + else if (!bExtentSet) { poGeom->getEnvelope(psExtent); bExtentSet = TRUE; } - else if (poGeom) + else { poGeom->getEnvelope(&oEnv); if (oEnv.MinX < psExtent->MinX) @@ -262,6 +300,19 @@ OGRErr OGR_L_GetExtent( OGRLayerH hLayer, OGREnvelope *psExtent, int bForce ) return ((OGRLayer *) hLayer)->GetExtent( psExtent, bForce ); } +/************************************************************************/ +/* OGR_L_GetExtentEx() */ +/************************************************************************/ + +OGRErr OGR_L_GetExtentEx( OGRLayerH hLayer, int iGeomField, + OGREnvelope *psExtent, int bForce ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetExtentEx", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *) hLayer)->GetExtent( iGeomField, psExtent, bForce ); +} + /************************************************************************/ /* SetAttributeFilter() */ /************************************************************************/ @@ -269,6 +320,9 @@ OGRErr OGR_L_GetExtent( OGRLayerH hLayer, OGREnvelope *psExtent, int bForce ) OGRErr OGRLayer::SetAttributeFilter( const char *pszQuery ) { + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + /* -------------------------------------------------------------------- */ /* Are we just clearing any existing query? */ /* -------------------------------------------------------------------- */ @@ -303,6 +357,51 @@ OGRErr OGRLayer::SetAttributeFilter( const char *pszQuery ) return eErr; } +/************************************************************************/ +/* ContainGeomSpecialField() */ +/************************************************************************/ + +static int ContainGeomSpecialField(swq_expr_node* expr, + int nLayerFieldCount) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if( expr->table_index == 0 && expr->field_index != -1 ) + { + int nSpecialFieldIdx = expr->field_index - + nLayerFieldCount; + return nSpecialFieldIdx == SPF_OGR_GEOMETRY || + nSpecialFieldIdx == SPF_OGR_GEOM_WKT || + nSpecialFieldIdx == SPF_OGR_GEOM_AREA; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (ContainGeomSpecialField(expr->papoSubExpr[i], + nLayerFieldCount)) + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* AttributeFilterEvaluationNeedsGeometry() */ +/************************************************************************/ + +int OGRLayer::AttributeFilterEvaluationNeedsGeometry() +{ + if( !m_poAttrQuery ) + return FALSE; + + swq_expr_node* expr = (swq_expr_node *) m_poAttrQuery->GetSWGExpr(); + int nLayerFieldCount = GetLayerDefn()->GetFieldCount(); + + return ContainGeomSpecialField(expr, nLayerFieldCount); +} + /************************************************************************/ /* OGR_L_SetAttributeFilter() */ /************************************************************************/ @@ -324,16 +423,30 @@ OGRFeature *OGRLayer::GetFeature( long nFID ) { OGRFeature *poFeature; + /* Save old attribute and spatial filters */ + char* pszOldFilter = m_pszAttrQueryString ? CPLStrdup(m_pszAttrQueryString) : NULL; + OGRGeometry* poOldFilterGeom = ( m_poFilterGeom != NULL ) ? m_poFilterGeom->clone() : NULL; + int iOldGeomFieldFilter = m_iGeomFieldFilter; + /* Unset filters */ + SetAttributeFilter(NULL); + SetSpatialFilter(0, NULL); + ResetReading(); while( (poFeature = GetNextFeature()) != NULL ) { if( poFeature->GetFID() == nFID ) - return poFeature; + break; else delete poFeature; } - return NULL; + /* Restore filters */ + SetAttributeFilter(pszOldFilter); + CPLFree(pszOldFilter); + SetSpatialFilter(iOldGeomFieldFilter, poOldFilterGeom); + delete poOldFilterGeom; + + return poFeature; } /************************************************************************/ @@ -357,6 +470,9 @@ OGRErr OGRLayer::SetNextByIndex( long nIndex ) { OGRFeature *poFeature; + if( nIndex < 0 ) + return OGRERR_FAILURE; + ResetReading(); while( nIndex-- > 0 ) { @@ -435,22 +551,11 @@ OGRErr OGR_L_CreateFeature( OGRLayerH hLayer, OGRFeatureH hFeat ) { VALIDATE_POINTER1( hLayer, "OGR_L_CreateFeature", OGRERR_INVALID_HANDLE ); - VALIDATE_POINTER1( hFeat, "OGR_L_SetFeature", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hFeat, "OGR_L_CreateFeature", OGRERR_INVALID_HANDLE ); return ((OGRLayer *) hLayer)->CreateFeature( (OGRFeature *) hFeat ); } -/************************************************************************/ -/* GetInfo() */ -/************************************************************************/ - -const char *OGRLayer::GetInfo( const char * pszTag ) - -{ - (void) pszTag; - return NULL; -} - /************************************************************************/ /* CreateField() */ /************************************************************************/ @@ -482,6 +587,193 @@ OGRErr OGR_L_CreateField( OGRLayerH hLayer, OGRFieldDefnH hField, bApproxOK ); } +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRLayer::DeleteField( int iField ) + +{ + (void) iField; + + CPLError( CE_Failure, CPLE_NotSupported, + "DeleteField() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_DeleteField() */ +/************************************************************************/ + +OGRErr OGR_L_DeleteField( OGRLayerH hLayer, int iField ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_DeleteField", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *) hLayer)->DeleteField( iField ); +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRLayer::ReorderFields( int* panMap ) + +{ + (void) panMap; + + CPLError( CE_Failure, CPLE_NotSupported, + "ReorderFields() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_ReorderFields() */ +/************************************************************************/ + +OGRErr OGR_L_ReorderFields( OGRLayerH hLayer, int* panMap ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_ReorderFields", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *) hLayer)->ReorderFields( panMap ); +} + +/************************************************************************/ +/* ReorderField() */ +/************************************************************************/ + +OGRErr OGRLayer::ReorderField( int iOldFieldPos, int iNewFieldPos ) + +{ + OGRErr eErr; + + int nFieldCount = GetLayerDefn()->GetFieldCount(); + + if (iOldFieldPos < 0 || iOldFieldPos >= nFieldCount) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + if (iNewFieldPos < 0 || iNewFieldPos >= nFieldCount) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + if (iNewFieldPos == iOldFieldPos) + return OGRERR_NONE; + + int* panMap = (int*) CPLMalloc(sizeof(int) * nFieldCount); + int i; + if (iOldFieldPos < iNewFieldPos) + { + /* "0","1","2","3","4" (1,3) -> "0","2","3","1","4" */ + for(i=0;i "0","3","1","2","4" */ + for(i=0;iReorderField( iOldFieldPos, iNewFieldPos ); +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, + int nFlags ) + +{ + (void) iField; + (void) poNewFieldDefn; + (void) nFlags; + + CPLError( CE_Failure, CPLE_NotSupported, + "AlterFieldDefn() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGR_L_AlterFieldDefn( OGRLayerH hLayer, int iField, OGRFieldDefnH hNewFieldDefn, + int nFlags ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_AlterFieldDefn", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hNewFieldDefn, "OGR_L_AlterFieldDefn", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *) hLayer)->AlterFieldDefn( iField, (OGRFieldDefn*) hNewFieldDefn, nFlags ); +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRLayer::CreateGeomField( OGRGeomFieldDefn * poField, int bApproxOK ) + +{ + (void) poField; + (void) bApproxOK; + + CPLError( CE_Failure, CPLE_NotSupported, + "CreateGeomField() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_CreateGeomField() */ +/************************************************************************/ + +OGRErr OGR_L_CreateGeomField( OGRLayerH hLayer, OGRGeomFieldDefnH hField, + int bApproxOK ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_CreateGeomField", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hField, "OGR_L_CreateGeomField", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *) hLayer)->CreateGeomField( (OGRGeomFieldDefn *) hField, + bApproxOK ); +} + /************************************************************************/ /* StartTransaction() */ /************************************************************************/ @@ -560,6 +852,39 @@ OGRFeatureDefnH OGR_L_GetLayerDefn( OGRLayerH hLayer ) return (OGRFeatureDefnH) ((OGRLayer *)hLayer)->GetLayerDefn(); } +/************************************************************************/ +/* OGR_L_FindFieldIndex() */ +/************************************************************************/ + +int OGR_L_FindFieldIndex( OGRLayerH hLayer, const char *pszFieldName, int bExactMatch ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_FindFieldIndex", -1 ); + + return ((OGRLayer *)hLayer)->FindFieldIndex( pszFieldName, bExactMatch ); +} + +/************************************************************************/ +/* FindFieldIndex() */ +/************************************************************************/ + +int OGRLayer::FindFieldIndex( const char *pszFieldName, int bExactMatch ) +{ + return GetLayerDefn()->GetFieldIndex( pszFieldName ); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRLayer::GetSpatialRef() +{ + if( GetLayerDefn()->GetGeomFieldCount() > 0 ) + return GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef(); + else + return NULL; +} + /************************************************************************/ /* OGR_L_GetSpatialRef() */ /************************************************************************/ @@ -614,10 +939,35 @@ OGRGeometryH OGR_L_GetSpatialFilter( OGRLayerH hLayer ) void OGRLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) { + m_iGeomFieldFilter = 0; if( InstallFilter( poGeomIn ) ) ResetReading(); } + +void OGRLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField == 0 ) + { + m_iGeomFieldFilter = iGeomField; + SetSpatialFilter( poGeomIn ); + } + else + { + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeomIn ) ) + ResetReading(); + } +} + /************************************************************************/ /* OGR_L_SetSpatialFilter() */ /************************************************************************/ @@ -630,6 +980,18 @@ void OGR_L_SetSpatialFilter( OGRLayerH hLayer, OGRGeometryH hGeom ) ((OGRLayer *) hLayer)->SetSpatialFilter( (OGRGeometry *) hGeom ); } +/************************************************************************/ +/* OGR_L_SetSpatialFilterEx() */ +/************************************************************************/ + +void OGR_L_SetSpatialFilterEx( OGRLayerH hLayer, int iGeomField, + OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetSpatialFilterEx" ); + + ((OGRLayer *) hLayer)->SetSpatialFilter( iGeomField, (OGRGeometry *) hGeom ); +} /************************************************************************/ /* SetSpatialFilterRect() */ /************************************************************************/ @@ -637,6 +999,15 @@ void OGR_L_SetSpatialFilter( OGRLayerH hLayer, OGRGeometryH hGeom ) void OGRLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, double dfMaxX, double dfMaxY ) +{ + SetSpatialFilterRect( 0, dfMinX, dfMinY, dfMaxX, dfMaxY ); +} + + +void OGRLayer::SetSpatialFilterRect( int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) + { OGRLinearRing oRing; OGRPolygon oPoly; @@ -649,7 +1020,11 @@ void OGRLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, oPoly.addRing( &oRing ); - SetSpatialFilter( &oPoly ); + if( iGeomField == 0 ) + /* for drivers that only overload SetSpatialFilter(OGRGeometry*) */ + SetSpatialFilter( &oPoly ); + else + SetSpatialFilter( iGeomField, &oPoly ); } /************************************************************************/ @@ -667,6 +1042,23 @@ void OGR_L_SetSpatialFilterRect( OGRLayerH hLayer, dfMaxX, dfMaxY ); } +/************************************************************************/ +/* OGR_L_SetSpatialFilterRectEx() */ +/************************************************************************/ + +void OGR_L_SetSpatialFilterRectEx( OGRLayerH hLayer, + int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetSpatialFilterRectEx" ); + + ((OGRLayer *) hLayer)->SetSpatialFilterRect( iGeomField, + dfMinX, dfMinY, + dfMaxX, dfMaxY ); +} + /************************************************************************/ /* InstallFilter() */ /* */ @@ -696,6 +1088,12 @@ int OGRLayer::InstallFilter( OGRGeometry * poFilter ) m_poFilterGeom = NULL; } + if( m_pPreparedFilterGeom != NULL ) + { + OGRDestroyPreparedGeometry(m_pPreparedFilterGeom); + m_pPreparedFilterGeom = NULL; + } + if( poFilter != NULL ) m_poFilterGeom = poFilter->clone(); @@ -707,6 +1105,9 @@ int OGRLayer::InstallFilter( OGRGeometry * poFilter ) if( m_poFilterGeom != NULL ) m_poFilterGeom->getEnvelope( &m_sFilterEnvelope ); + /* Compile geometry filter as a prepared geometry */ + m_pPreparedFilterGeom = OGRCreatePreparedGeometry(m_poFilterGeom); + /* -------------------------------------------------------------------- */ /* Now try to determine if the filter is really a rectangle. */ /* -------------------------------------------------------------------- */ @@ -802,11 +1203,67 @@ int OGRLayer::FilterGeometry( OGRGeometry *poGeometry ) else { /* -------------------------------------------------------------------- */ +/* If the filter geometry is its own envelope and if the */ +/* the geometry (line, or polygon without hole) h has at least one */ +/* point inside the filter geometry, the geometry itself is inside */ +/* the filter geometry. */ +/* -------------------------------------------------------------------- */ + if( m_bFilterIsEnvelope ) + { + OGRLineString* poLS = NULL; + + switch( wkbFlatten(poGeometry->getGeometryType()) ) + { + case wkbPolygon: + { + OGRPolygon* poPoly = (OGRPolygon* )poGeometry; + OGRLinearRing* poRing = poPoly->getExteriorRing(); + if (poRing != NULL && poPoly->getNumInteriorRings() == 0) + { + poLS = poRing; + } + break; + } + + case wkbLineString: + poLS = (OGRLineString* )poGeometry; + break; + + default: + break; + } + + if( poLS != NULL ) + { + int nNumPoints = poLS->getNumPoints(); + for(int i = 0; i < nNumPoints; i++) + { + double x = poLS->getX(i); + double y = poLS->getY(i); + if (x >= m_sFilterEnvelope.MinX && + y >= m_sFilterEnvelope.MinY && + x <= m_sFilterEnvelope.MaxX && + y <= m_sFilterEnvelope.MaxY) + { + return TRUE; + } + } + } + } + +/* -------------------------------------------------------------------- */ /* Fallback to full intersect test (using GEOS) if we still */ /* don't know for sure. */ /* -------------------------------------------------------------------- */ if( OGRGeometryFactory::haveGEOS() ) - return m_poFilterGeom->Intersects( poGeometry ); + { + //CPLDebug("OGRLayer", "GEOS intersection"); + if( m_pPreparedFilterGeom != NULL ) + return OGRPreparedGeometryIntersects(m_pPreparedFilterGeom, + poGeometry); + else + return m_poFilterGeom->Intersects( poGeometry ); + } else return TRUE; } @@ -837,6 +1294,9 @@ OGRErr OGRLayer::InitializeIndexSupport( const char *pszFilename ) { OGRErr eErr; + if (m_poAttrIndex != NULL) + return OGRERR_NONE; + m_poAttrIndex = OGRCreateDefaultLayerIndex(); eErr = m_poAttrIndex->Initialize( pszFilename, this ); @@ -944,7 +1404,10 @@ const char *OGR_L_GetFIDColumn( OGRLayerH hLayer ) const char *OGRLayer::GetGeometryColumn() { - return ""; + if( GetLayerDefn()->GetGeomFieldCount() > 0 ) + return GetLayerDefn()->GetGeomFieldDefn(0)->GetNameRef(); + else + return ""; } /************************************************************************/ @@ -960,14 +1423,46 @@ const char *OGR_L_GetGeometryColumn( OGRLayerH hLayer ) } /************************************************************************/ -/* OGR_L_GetStyleTable() */ +/* GetStyleTable() */ /************************************************************************/ -OGRStyleTableH OGR_L_GetStyleTable( OGRLayerH hLayer ) - +OGRStyleTable *OGRLayer::GetStyleTable() { - VALIDATE_POINTER1( hLayer, "OGR_L_GetStyleTable", NULL ); - + return m_poStyleTable; +} + +/************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +void OGRLayer::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = poStyleTable; +} + +/************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ + +void OGRLayer::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + if ( poStyleTable ) + m_poStyleTable = poStyleTable->Clone(); +} + +/************************************************************************/ +/* OGR_L_GetStyleTable() */ +/************************************************************************/ + +OGRStyleTableH OGR_L_GetStyleTable( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetStyleTable", NULL ); + return (OGRStyleTableH) ((OGRLayer *) hLayer)->GetStyleTable( ); } @@ -997,3 +1492,2176 @@ void OGR_L_SetStyleTable( OGRLayerH hLayer, ((OGRLayer *) hLayer)->SetStyleTable( (OGRStyleTable *) hStyleTable); } + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRLayer::GetName() + +{ + return GetLayerDefn()->GetName(); +} + +/************************************************************************/ +/* OGR_L_GetName() */ +/************************************************************************/ + +const char* OGR_L_GetName( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetName", "" ); + + return ((OGRLayer *) hLayer)->GetName(); +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRLayer::GetGeomType() +{ + return GetLayerDefn()->GetGeomType(); +} +/************************************************************************/ +/* OGR_L_GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGR_L_GetGeomType( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetGeomType", wkbUnknown ); + + return ((OGRLayer *) hLayer)->GetGeomType(); +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRLayer::SetIgnoredFields( const char **papszFields ) +{ + OGRFeatureDefn *poDefn = GetLayerDefn(); + + // first set everything as *not* ignored + for( int iField = 0; iField < poDefn->GetFieldCount(); iField++ ) + { + poDefn->GetFieldDefn(iField)->SetIgnored( FALSE ); + } + poDefn->SetGeometryIgnored( FALSE ); + poDefn->SetStyleIgnored( FALSE ); + + if ( papszFields == NULL ) + return OGRERR_NONE; + + // ignore some fields + while ( *papszFields ) + { + const char* pszFieldName = *papszFields; + // check special fields + if ( EQUAL(pszFieldName, "OGR_GEOMETRY") ) + poDefn->SetGeometryIgnored( TRUE ); + else if ( EQUAL(pszFieldName, "OGR_STYLE") ) + poDefn->SetStyleIgnored( TRUE ); + else + { + // check ordinary fields + int iField = poDefn->GetFieldIndex(pszFieldName); + if ( iField == -1 ) + { + // check geometry field + iField = poDefn->GetGeomFieldIndex(pszFieldName); + if ( iField == -1 ) + { + return OGRERR_FAILURE; + } + else + poDefn->GetGeomFieldDefn(iField)->SetIgnored( TRUE ); + } + else + poDefn->GetFieldDefn(iField)->SetIgnored( TRUE ); + } + papszFields++; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_L_SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGR_L_SetIgnoredFields( OGRLayerH hLayer, const char **papszFields ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_SetIgnoredFields", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *) hLayer)->SetIgnoredFields( papszFields ); +} + +/************************************************************************/ +/* helper functions for layer overlay methods */ +/************************************************************************/ + +static +OGRErr clone_spatial_filter(OGRLayer *pLayer, OGRGeometry **ppGeometry) +{ + OGRErr ret = OGRERR_NONE; + OGRGeometry *g = pLayer->GetSpatialFilter(); + *ppGeometry = g ? g->clone() : NULL; + return ret; +} + +static +OGRErr create_field_map(OGRFeatureDefn *poDefn, int **map) +{ + OGRErr ret = OGRERR_NONE; + int n = poDefn->GetFieldCount(); + if (n > 0) { + *map = (int*)VSIMalloc(sizeof(int) * n); + if (!(*map)) return OGRERR_NOT_ENOUGH_MEMORY; + } + return ret; +} + +static +OGRErr set_result_schema(OGRLayer *pLayerResult, + OGRFeatureDefn *poDefnInput, + OGRFeatureDefn *poDefnMethod, + int *mapInput, + int *mapMethod, + int combined, + char** papszOptions) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnResult = pLayerResult->GetLayerDefn(); + const char* pszInputPrefix = CSLFetchNameValue(papszOptions, "INPUT_PREFIX"); + const char* pszMethodPrefix = CSLFetchNameValue(papszOptions, "METHOD_PREFIX"); + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + if (poDefnResult->GetFieldCount() > 0) { + // the user has defined the schema of the output layer + for( int iField = 0; iField < poDefnInput->GetFieldCount(); iField++ ) { + CPLString osName(poDefnInput->GetFieldDefn(iField)->GetNameRef()); + if( pszInputPrefix != NULL ) + osName = pszInputPrefix + osName; + mapInput[iField] = poDefnResult->GetFieldIndex(osName); + } + if (!mapMethod) return ret; + for( int iField = 0; iField < poDefnMethod->GetFieldCount(); iField++ ) { + CPLString osName(poDefnMethod->GetFieldDefn(iField)->GetNameRef()); + if( pszMethodPrefix != NULL ) + osName = pszMethodPrefix + osName; + mapMethod[iField] = poDefnResult->GetFieldIndex(osName); + } + } else { + // use schema from the input layer or from input and method layers + int nFieldsInput = poDefnInput->GetFieldCount(); + for( int iField = 0; iField < nFieldsInput; iField++ ) { + OGRFieldDefn oFieldDefn(poDefnInput->GetFieldDefn(iField)); + if( pszInputPrefix != NULL ) + oFieldDefn.SetName(CPLSPrintf("%s%s", pszInputPrefix, oFieldDefn.GetNameRef())); + ret = pLayerResult->CreateField(&oFieldDefn); + if (ret != OGRERR_NONE) { + if (!bSkipFailures) + return ret; + else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + mapInput[iField] = iField; + } + if (!combined) return ret; + if (!mapMethod) return ret; + for( int iField = 0; iField < poDefnMethod->GetFieldCount(); iField++ ) { + OGRFieldDefn oFieldDefn(poDefnMethod->GetFieldDefn(iField)); + if( pszMethodPrefix != NULL ) + oFieldDefn.SetName(CPLSPrintf("%s%s", pszMethodPrefix, oFieldDefn.GetNameRef())); + ret = pLayerResult->CreateField(&oFieldDefn); + if (ret != OGRERR_NONE) { + if (!bSkipFailures) + return ret; + else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + mapMethod[iField] = nFieldsInput+iField; + } + } + return ret; +} + +static +OGRGeometry *set_filter_from(OGRLayer *pLayer, OGRGeometry *pGeometryExistingFilter, OGRFeature *pFeature) +{ + OGRGeometry *geom = pFeature->GetGeometryRef(); + if (!geom) return NULL; + if (pGeometryExistingFilter) { + if (!geom->Intersects(pGeometryExistingFilter)) return NULL; + OGRGeometry *intersection = geom->Intersection(pGeometryExistingFilter); + pLayer->SetSpatialFilter(intersection); + if (intersection) delete intersection; + } else { + pLayer->SetSpatialFilter(geom); + } + return geom; +} + +static OGRGeometry* promote_to_multi(OGRGeometry* poGeom) +{ + OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType()); + if( eType == wkbPolygon ) + return OGRGeometryFactory::forceToMultiPolygon(poGeom); + else if( eType == wkbLineString ) + return OGRGeometryFactory::forceToMultiLineString(poGeom); + else + return poGeom; +} + +/************************************************************************/ +/* Intersection() */ +/************************************************************************/ +/** + * \brief Intersection of two layers. + * + * The result layer contains features whose geometries represent areas + * that are common between features in the input layer and in the + * method layer. The features in the result layer have attributes from + * both input and method layers. The schema of the result layer can be + * set by the user or, if it is empty, is initialized to contain all + * fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Intersection(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Intersection( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + OGREnvelope sEnvelopeMethod; + GBool bEnvelopeSet; + double progress_max = GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + bEnvelopeSet = pLayerMethod->GetExtent(&sEnvelopeMethod, 1) == OGRERR_NONE; + + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // is it worth to proceed? + if (bEnvelopeSet) { + OGRGeometry *x_geom = x->GetGeometryRef(); + if (x_geom) { + OGREnvelope x_env; + x_geom->getEnvelope(&x_env); + if (x_env.MaxX < sEnvelopeMethod.MinX + || x_env.MaxY < sEnvelopeMethod.MinY + || sEnvelopeMethod.MaxX < x_env.MinX + || sEnvelopeMethod.MaxY < x_env.MinY) { + delete x; + continue; + } + } else { + delete x; + continue; + } + } + + // set up the filter for method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry* poIntersection = x_geom->Intersection(y_geom); + if( poIntersection == NULL || poIntersection->IsEmpty() || + (x_geom->getDimension() == 2 && + y_geom->getDimension() == 2 && + poIntersection->getDimension() < 2) ) + { + delete poIntersection; + delete y; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + z->SetFieldsFrom(y, mapMethod); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + delete x; + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + delete x; + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Intersection() */ +/************************************************************************/ +/** + * \brief Intersection of two layers. + * + * The result layer contains features whose geometries represent areas + * that are common between features in the input layer and in the + * method layer. The features in the result layer have attributes from + * both input and method layers. The schema of the result layer can be + * set by the user or, if it is empty, is initialized to contain all + * fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Intersection(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Intersection( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Intersection", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Intersection", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Intersection", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Intersection( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Union() */ +/************************************************************************/ + +/** + * \brief Union of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer. The + * features in the result layer have attributes from both input and + * method layers. For features which represent areas that are only in + * the input or in the method layer the respective attributes have + * undefined values. The schema of the result layer can be set by the + * user or, if it is empty, is initialized to contain all fields in + * the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Union(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Union( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + OGRGeometry *pGeometryInputFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = GetFeatureCount(0) + pLayerMethod->GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(this, &pGeometryInputFilter); + if (ret != OGRERR_NONE) goto done; + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // add features based on input layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *poIntersection = x_geom->Intersection(y_geom); + if( poIntersection == NULL || poIntersection->IsEmpty() || + (x_geom->getDimension() == 2 && + y_geom->getDimension() == 2 && + poIntersection->getDimension() < 2) ) + { + delete poIntersection; + delete y; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + z->SetFieldsFrom(y, mapMethod); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + delete x; + if (x_geom_diff) + delete x_geom_diff; + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + // restore filter on method layer and add features based on it + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + pLayerMethod->ResetReading(); + while (OGRFeature *x = pLayerMethod->GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on input layer + OGRGeometry *x_geom = set_filter_from(this, pGeometryInputFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); // this will be the geometry of the result feature + ResetReading(); + while (OGRFeature *y = GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapMethod); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + SetSpatialFilter(pGeometryInputFilter); + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (pGeometryInputFilter) delete pGeometryInputFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Union() */ +/************************************************************************/ + +/** + * \brief Union of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer. The + * features in the result layer have attributes from both input and + * method layers. For features which represent areas that are only in + * the input or in the method layer the respective attributes have + * undefined values. The schema of the result layer can be set by the + * user or, if it is empty, is initialized to contain all fields in + * the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Union(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Union( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Union", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Union", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Union", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Union( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* SymDifference() */ +/************************************************************************/ + +/** + * \brief Symmetrical difference of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer but + * not in both. The features in the result layer have attributes from + * both input and method layers. For features which represent areas + * that are only in the input or in the method layer the respective + * attributes have undefined values. The schema of the result layer + * can be set by the user or, if it is empty, is initialized to + * contain all fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_SymDifference(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::SymDifference( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + OGRGeometry *pGeometryInputFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = GetFeatureCount(0) + pLayerMethod->GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(this, &pGeometryInputFilter); + if (ret != OGRERR_NONE) goto done; + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // add features based on input layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = x_geom->clone(); // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *geom_new = geom ? geom->Difference(y_geom) : NULL; + if (geom) delete geom; + geom = geom_new; + delete y; + if (geom && geom->IsEmpty()) break; + } + + OGRFeature *z = NULL; + if (geom && !geom->IsEmpty()) { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + geom = promote_to_multi(geom); + z->SetGeometryDirectly(geom); + } else { + if (geom) delete geom; + } + delete x; + if (z) { + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + // restore filter on method layer and add features based on it + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + pLayerMethod->ResetReading(); + while (OGRFeature *x = pLayerMethod->GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on input layer + OGRGeometry *x_geom = set_filter_from(this, pGeometryInputFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = x_geom->clone(); // this will be the geometry of the result feature + ResetReading(); + while (OGRFeature *y = GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *geom_new = geom ? geom->Difference(y_geom) : NULL; + if (geom) delete geom; + geom = geom_new; + delete y; + if (geom == NULL || geom->IsEmpty()) break; + } + + OGRFeature *z = NULL; + if (geom && !geom->IsEmpty()) { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapMethod); + if( bPromoteToMulti ) + geom = promote_to_multi(geom); + z->SetGeometryDirectly(geom); + } else { + if (geom) delete geom; + } + delete x; + if (z) { + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + SetSpatialFilter(pGeometryInputFilter); + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (pGeometryInputFilter) delete pGeometryInputFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_SymDifference() */ +/************************************************************************/ + +/** + * \brief Symmetrical difference of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer but + * not in both. The features in the result layer have attributes from + * both input and method layers. For features which represent areas + * that are only in the input or in the method layer the respective + * attributes have undefined values. The schema of the result layer + * can be set by the user or, if it is empty, is initialized to + * contain all fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::SymDifference(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_SymDifference( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_SymDifference", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_SymDifference", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_SymDifference", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->SymDifference( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Identity() */ +/************************************************************************/ + +/** + * \brief Identify the features of this layer with the ones from the + * identity layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer. The features in the result layer have + * attributes from both input and method layers. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Identity(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Identity( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // split the features in input layer to the result layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry* poIntersection = x_geom->Intersection(y_geom); + if( poIntersection == NULL || poIntersection->IsEmpty() || + (x_geom->getDimension() == 2 && + y_geom->getDimension() == 2 && + poIntersection->getDimension() < 2) ) + { + delete poIntersection; + delete y; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + z->SetFieldsFrom(y, mapMethod); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + delete x; + delete x_geom_diff; + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Identity() */ +/************************************************************************/ + +/** + * \brief Identify the features of this layer with the ones from the + * identity layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer. The features in the result layer have + * attributes from both input and method layers. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Identity(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Identity( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Identity", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Identity", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Identity", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Identity( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Update() */ +/************************************************************************/ + +/** + * \brief Update this layer with features from the update layer. + * + * The result layer contains features whose geometries represent areas + * that are either in the input layer or in the method layer. The + * features in the result layer have areas of the features of the + * method layer or those ares of the features of the input layer that + * are not covered by the method layer. The features of the result + * layer get their attributes from the input layer. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in the input layer. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in the method layer, then + * the attribute in the result feature the originates from the method + * layer will get the value from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Update(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Update( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = GetFeatureCount(0) + pLayerMethod->GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 0, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // add clipped features from the input layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); //this will be the geometry of a result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + // restore the original filter and add features from the update layer + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete y; + goto done; + } + } + progress_counter += 1.0; + } + + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRFeature *z = new OGRFeature(poDefnResult); + if (mapMethod) z->SetFieldsFrom(y, mapMethod); + z->SetGeometry(y_geom); + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Update() */ +/************************************************************************/ + +/** + * \brief Update this layer with features from the update layer. + * + * The result layer contains features whose geometries represent areas + * that are either in the input layer or in the method layer. The + * features in the result layer have areas of the features of the + * method layer or those ares of the features of the input layer that + * are not covered by the method layer. The features of the result + * layer get their attributes from the input layer. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in the input layer. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in the method layer, then + * the attribute in the result feature the originates from the method + * layer will get the value from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Update(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Update( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Update", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Update", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Update", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Update( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Clip() */ +/************************************************************************/ + +/** + * \brief Clip off areas that are not covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer and in the method layer. The features + * in the result layer have the (possibly clipped) areas of features + * in the input layer and the attributes from the same features. The + * schema of the result layer can be set by the user or, if it is + * empty, is initialized to contain all fields in the input layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Clip(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Clip( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + double progress_max = GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, NULL, mapInput, NULL, 0, papszOptions); + if (ret != OGRERR_NONE) goto done; + + poDefnResult = pLayerResult->GetLayerDefn(); + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = NULL; // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + // incrementally add area from y to geom + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + if (!geom) { + geom = y_geom->clone(); + } else { + OGRGeometry *geom_new = geom->Union(y_geom); + delete geom; + geom = geom_new; + } + delete y; + } + + // possibly add a new feature with area x intersection sum of y + OGRFeature *z = NULL; + if (geom) { + OGRGeometry* poIntersection = x_geom->Intersection(geom); + if( poIntersection != NULL && !poIntersection->IsEmpty() ) + { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + } + else + delete poIntersection; + delete geom; + } + delete x; + if (z) { + if (z->GetGeometryRef() != NULL && !z->GetGeometryRef()->IsEmpty()) + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + return ret; +} + +/************************************************************************/ +/* OGR_L_Clip() */ +/************************************************************************/ + +/** + * \brief Clip off areas that are not covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer and in the method layer. The features + * in the result layer have the (possibly clipped) areas of features + * in the input layer and the attributes from the same features. The + * schema of the result layer can be set by the user or, if it is + * empty, is initialized to contain all fields in the input layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Clip(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Clip( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Clip", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Clip", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Clip", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Clip( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Erase() */ +/************************************************************************/ + +/** + * \brief Remove areas that are covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer but not in the method layer. The + * features in the result layer have attributes from the input + * layer. The schema of the result layer can be set by the user or, if + * it is empty, is initialized to contain all fields in the input + * layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Erase(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Erase( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + double progress_max = GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, NULL, mapInput, NULL, 0, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on the method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = NULL; // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + // incrementally add area from y to geom + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + if (!geom) { + geom = y_geom->clone(); + } else { + OGRGeometry *geom_new = geom->Union(y_geom); + delete geom; + geom = geom_new; + } + delete y; + } + + // possibly add a new feature with area x minus sum of y + OGRFeature *z = NULL; + if (geom) { + OGRGeometry* x_geom_diff = x_geom->Difference(geom); + if( x_geom_diff != NULL && !x_geom_diff->IsEmpty() ) + { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + } + else + delete x_geom_diff; + delete geom; + } + delete x; + if (z) { + if (z->GetGeometryRef() != NULL && !z->GetGeometryRef()->IsEmpty()) + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + return ret; +} + +/************************************************************************/ +/* OGR_L_Erase() */ +/************************************************************************/ + +/** + * \brief Remove areas that are covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer but not in the method layer. The + * features in the result layer have attributes from the input + * layer. The schema of the result layer can be set by the user or, if + * it is empty, is initialized to contain all fields in the input + * layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Erase(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Erase( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Erase", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Erase", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Erase", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Erase( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} diff --git a/ogr/ogrlayerdecorator.cpp b/ogr/ogrlayerdecorator.cpp new file mode 100644 index 0000000..8d11f1e --- /dev/null +++ b/ogr/ogrlayerdecorator.cpp @@ -0,0 +1,225 @@ +/****************************************************************************** + * $Id: ogrlayerdecorator.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRLayerDecorator class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrlayerdecorator.h" + +CPL_CVSID("$Id: ogrlayerdecorator.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +OGRLayerDecorator::OGRLayerDecorator(OGRLayer* poDecoratedLayer, + int bTakeOwnership) : + m_poDecoratedLayer(poDecoratedLayer), + m_bHasOwnership(bTakeOwnership) +{ + CPLAssert(poDecoratedLayer != NULL); +} + +OGRLayerDecorator::~OGRLayerDecorator() +{ + if( m_bHasOwnership ) + delete m_poDecoratedLayer; +} + + +OGRGeometry *OGRLayerDecorator::GetSpatialFilter() +{ + return m_poDecoratedLayer->GetSpatialFilter(); +} + +void OGRLayerDecorator::SetSpatialFilter( OGRGeometry * poGeom ) +{ + m_poDecoratedLayer->SetSpatialFilter(poGeom); +} + +void OGRLayerDecorator::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + m_poDecoratedLayer->SetSpatialFilter(iGeomField, poGeom); +} + +void OGRLayerDecorator::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + m_poDecoratedLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +void OGRLayerDecorator::SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + m_poDecoratedLayer->SetSpatialFilterRect(iGeomField, dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +OGRErr OGRLayerDecorator::SetAttributeFilter( const char * poAttrFilter ) +{ + return m_poDecoratedLayer->SetAttributeFilter(poAttrFilter); +} + +void OGRLayerDecorator::ResetReading() +{ + m_poDecoratedLayer->ResetReading(); +} + +OGRFeature *OGRLayerDecorator::GetNextFeature() +{ + return m_poDecoratedLayer->GetNextFeature(); +} + +OGRErr OGRLayerDecorator::SetNextByIndex( long nIndex ) +{ + return m_poDecoratedLayer->SetNextByIndex(nIndex); +} + +OGRFeature *OGRLayerDecorator::GetFeature( long nFID ) +{ + return m_poDecoratedLayer->GetFeature(nFID); +} + +OGRErr OGRLayerDecorator::SetFeature( OGRFeature *poFeature ) +{ + return m_poDecoratedLayer->SetFeature(poFeature); +} + +OGRErr OGRLayerDecorator::CreateFeature( OGRFeature *poFeature ) +{ + return m_poDecoratedLayer->CreateFeature(poFeature); +} + +OGRErr OGRLayerDecorator::DeleteFeature( long nFID ) +{ + return m_poDecoratedLayer->DeleteFeature(nFID); +} + +const char *OGRLayerDecorator::GetName() +{ + return m_poDecoratedLayer->GetName(); +} + +OGRwkbGeometryType OGRLayerDecorator::GetGeomType() +{ + return m_poDecoratedLayer->GetGeomType(); +} + +OGRFeatureDefn *OGRLayerDecorator::GetLayerDefn() +{ + return m_poDecoratedLayer->GetLayerDefn(); +} + +OGRSpatialReference *OGRLayerDecorator::GetSpatialRef() +{ + return m_poDecoratedLayer->GetSpatialRef(); +} + +int OGRLayerDecorator::GetFeatureCount( int bForce ) +{ + return m_poDecoratedLayer->GetFeatureCount(bForce); +} + +OGRErr OGRLayerDecorator::GetExtent(OGREnvelope *psExtent, int bForce) +{ + return m_poDecoratedLayer->GetExtent(psExtent, bForce); +} + +OGRErr OGRLayerDecorator::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + return m_poDecoratedLayer->GetExtent(iGeomField, psExtent, bForce); +} + +int OGRLayerDecorator::TestCapability( const char * pszCapability ) +{ + return m_poDecoratedLayer->TestCapability(pszCapability); +} + +OGRErr OGRLayerDecorator::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + return m_poDecoratedLayer->CreateField(poField, bApproxOK); +} + +OGRErr OGRLayerDecorator::DeleteField( int iField ) +{ + return m_poDecoratedLayer->DeleteField(iField); +} + +OGRErr OGRLayerDecorator::ReorderFields( int* panMap ) +{ + return m_poDecoratedLayer->ReorderFields(panMap); +} + +OGRErr OGRLayerDecorator::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + return m_poDecoratedLayer->AlterFieldDefn(iField, poNewFieldDefn, nFlags); +} + +OGRErr OGRLayerDecorator::SyncToDisk() +{ + return m_poDecoratedLayer->SyncToDisk(); +} + +OGRStyleTable *OGRLayerDecorator::GetStyleTable() +{ + return m_poDecoratedLayer->GetStyleTable(); +} + +void OGRLayerDecorator::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + return m_poDecoratedLayer->SetStyleTableDirectly(poStyleTable); +} + +void OGRLayerDecorator::SetStyleTable(OGRStyleTable *poStyleTable) +{ + return m_poDecoratedLayer->SetStyleTable(poStyleTable); +} + +OGRErr OGRLayerDecorator::StartTransaction() +{ + return m_poDecoratedLayer->StartTransaction(); +} + +OGRErr OGRLayerDecorator::CommitTransaction() +{ + return m_poDecoratedLayer->CommitTransaction(); +} + +OGRErr OGRLayerDecorator::RollbackTransaction() +{ + return m_poDecoratedLayer->RollbackTransaction(); +} + +const char *OGRLayerDecorator::GetFIDColumn() +{ + return m_poDecoratedLayer->GetFIDColumn(); +} + +const char *OGRLayerDecorator::GetGeometryColumn() +{ + return m_poDecoratedLayer->GetGeometryColumn(); +} + +OGRErr OGRLayerDecorator::SetIgnoredFields( const char **papszFields ) +{ + return m_poDecoratedLayer->SetIgnoredFields(papszFields); +} diff --git a/ogr/ogrlayerdecorator.h b/ogr/ogrlayerdecorator.h new file mode 100644 index 0000000..b0e2822 --- /dev/null +++ b/ogr/ogrlayerdecorator.h @@ -0,0 +1,102 @@ +/****************************************************************************** + * $Id: ogrlayerdecorator.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerDecorator class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRLAYERDECORATOR_H_INCLUDED +#define _OGRLAYERDECORATOR_H_INCLUDED + +#include "ogrsf_frmts.h" + +class OGRLayerDecorator : public OGRLayer +{ + protected: + OGRLayer *m_poDecoratedLayer; + int m_bHasOwnership; + + public: + + OGRLayerDecorator(OGRLayer* poDecoratedLayer, + int bTakeOwnership); + virtual ~OGRLayerDecorator(); + + virtual OGRGeometry *GetSpatialFilter(); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex( long nIndex ); + virtual OGRFeature *GetFeature( long nFID ); + virtual OGRErr SetFeature( OGRFeature *poFeature ); + virtual OGRErr CreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( long nFID ); + + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr SyncToDisk(); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + OGRLayer* GetBaseLayer() { return m_poDecoratedLayer; } +}; + +#endif // _OGRLAYERDECORATOR_H_INCLUDED diff --git a/ogr/ogrlayerpool.cpp b/ogr/ogrlayerpool.cpp new file mode 100644 index 0000000..2ae0209 --- /dev/null +++ b/ogr/ogrlayerpool.cpp @@ -0,0 +1,580 @@ +/****************************************************************************** + * $Id: ogrlayerpool.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerPool and OGRProxiedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrlayerpool.h" + +CPL_CVSID("$Id: ogrlayerpool.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRAbstractProxiedLayer() */ +/************************************************************************/ + +OGRAbstractProxiedLayer::OGRAbstractProxiedLayer(OGRLayerPool* poPool) +{ + CPLAssert(poPool != NULL); + this->poPool = poPool; + poPrevLayer = NULL; + poNextLayer = NULL; +} + +/************************************************************************/ +/* ~OGRAbstractProxiedLayer() */ +/************************************************************************/ + +OGRAbstractProxiedLayer::~OGRAbstractProxiedLayer() +{ + /* Remove us from the list of LRU layers if necessary */ + poPool->UnchainLayer(this); +} + + + +/************************************************************************/ +/* OGRLayerPool() */ +/************************************************************************/ + +OGRLayerPool::OGRLayerPool(int nMaxSimultaneouslyOpened) +{ + poMRULayer = NULL; + poLRULayer = NULL; + nMRUListSize = 0; + this->nMaxSimultaneouslyOpened = nMaxSimultaneouslyOpened; +} + +/************************************************************************/ +/* ~OGRLayerPool() */ +/************************************************************************/ + +OGRLayerPool::~OGRLayerPool() +{ + CPLAssert( poMRULayer == NULL ); + CPLAssert( poLRULayer == NULL ); + CPLAssert( nMRUListSize == 0 ); +} + +/************************************************************************/ +/* SetLastUsedLayer() */ +/************************************************************************/ + +void OGRLayerPool::SetLastUsedLayer(OGRAbstractProxiedLayer* poLayer) +{ + /* If we are already the MRU layer, nothing to do */ + if (poLayer == poMRULayer) + return; + + //CPLDebug("OGR", "SetLastUsedLayer(%s)", poLayer->GetName()); + + if (poLayer->poPrevLayer != NULL || poLayer->poNextLayer != NULL) + { + /* Remove current layer from its current place in the list */ + UnchainLayer(poLayer); + } + else if (nMRUListSize == nMaxSimultaneouslyOpened) + { + /* If we have reached the maximum allowed number of layers */ + /* simultaneously opened, then close the LRU one that */ + /* was still active until now */ + CPLAssert(poLRULayer != NULL); + + poLRULayer->CloseUnderlyingLayer(); + UnchainLayer(poLRULayer); + } + + /* Put current layer on top of MRU list */ + CPLAssert(poLayer->poPrevLayer == NULL); + CPLAssert(poLayer->poNextLayer == NULL); + poLayer->poNextLayer = poMRULayer; + if (poMRULayer != NULL) + { + CPLAssert(poMRULayer->poPrevLayer == NULL); + poMRULayer->poPrevLayer = poLayer; + } + poMRULayer = poLayer; + if (poLRULayer == NULL) + poLRULayer = poLayer; + nMRUListSize ++; +} + +/************************************************************************/ +/* UnchainLayer() */ +/************************************************************************/ + +void OGRLayerPool::UnchainLayer(OGRAbstractProxiedLayer* poLayer) +{ + OGRAbstractProxiedLayer* poPrevLayer = poLayer->poPrevLayer; + OGRAbstractProxiedLayer* poNextLayer = poLayer->poNextLayer; + + CPLAssert(poPrevLayer == NULL || poPrevLayer->poNextLayer == poLayer); + CPLAssert(poNextLayer == NULL || poNextLayer->poPrevLayer == poLayer); + + if (poPrevLayer != NULL || poNextLayer != NULL || poLayer == poMRULayer) + nMRUListSize --; + + if (poLayer == poMRULayer) + poMRULayer = poNextLayer; + if (poLayer == poLRULayer) + poLRULayer = poPrevLayer; + if (poPrevLayer != NULL) + poPrevLayer->poNextLayer = poNextLayer; + if (poNextLayer != NULL) + poNextLayer->poPrevLayer = poPrevLayer; + poLayer->poPrevLayer = NULL; + poLayer->poNextLayer = NULL; +} + + + +/************************************************************************/ +/* OGRProxiedLayer() */ +/************************************************************************/ + +OGRProxiedLayer::OGRProxiedLayer(OGRLayerPool* poPool, + OpenLayerFunc pfnOpenLayer, + FreeUserDataFunc pfnFreeUserData, + void* pUserData) : OGRAbstractProxiedLayer(poPool) +{ + CPLAssert(pfnOpenLayer != NULL); + + this->pfnOpenLayer = pfnOpenLayer; + this->pfnFreeUserData = pfnFreeUserData; + this->pUserData = pUserData; + poUnderlyingLayer = NULL; + poFeatureDefn = NULL; + poSRS = NULL; +} + +/************************************************************************/ +/* ~OGRProxiedLayer() */ +/************************************************************************/ + +OGRProxiedLayer::~OGRProxiedLayer() +{ + delete poUnderlyingLayer; + + if( poSRS ) + poSRS->Release(); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( pfnFreeUserData != NULL ) + pfnFreeUserData(pUserData); +} + +/************************************************************************/ +/* OpenUnderlyingLayer() */ +/************************************************************************/ + +int OGRProxiedLayer::OpenUnderlyingLayer() +{ + CPLDebug("OGR", "OpenUnderlyingLayer(%p)", this); + CPLAssert(poUnderlyingLayer == NULL); + poPool->SetLastUsedLayer(this); + poUnderlyingLayer = pfnOpenLayer(pUserData); + if( poUnderlyingLayer == NULL ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot open underlying layer"); + } + return poUnderlyingLayer != NULL; +} + +/************************************************************************/ +/* CloseUnderlyingLayer() */ +/************************************************************************/ + +void OGRProxiedLayer::CloseUnderlyingLayer() +{ + CPLDebug("OGR", "CloseUnderlyingLayer(%p)", this); + delete poUnderlyingLayer; + poUnderlyingLayer = NULL; +} + +/************************************************************************/ +/* GetUnderlyingLayer() */ +/************************************************************************/ + +OGRLayer* OGRProxiedLayer::GetUnderlyingLayer() +{ + if( poUnderlyingLayer == NULL ) + OpenUnderlyingLayer(); + return poUnderlyingLayer; +} + +/************************************************************************/ +/* GetSpatialFilter() */ +/************************************************************************/ + +OGRGeometry *OGRProxiedLayer::GetSpatialFilter() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetSpatialFilter(); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRProxiedLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + poUnderlyingLayer->SetSpatialFilter(poGeom); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRProxiedLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + poUnderlyingLayer->SetSpatialFilter(iGeomField, poGeom); +} +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetAttributeFilter( const char * poAttrFilter ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetAttributeFilter(poAttrFilter); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRProxiedLayer::ResetReading() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + poUnderlyingLayer->ResetReading(); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRProxiedLayer::GetNextFeature() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetNextFeature(); +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetNextByIndex( long nIndex ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetNextByIndex(nIndex); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRProxiedLayer::GetFeature( long nFID ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetFeature(nFID); +} + +/************************************************************************/ +/* SetFeature() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetFeature( OGRFeature *poFeature ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetFeature(poFeature); +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::CreateFeature( OGRFeature *poFeature ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->CreateFeature(poFeature); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::DeleteFeature( long nFID ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->DeleteFeature(nFID); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRProxiedLayer::GetName() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return ""; + return poUnderlyingLayer->GetName(); +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRProxiedLayer::GetGeomType() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return wkbUnknown; + return poUnderlyingLayer->GetGeomType(); +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRProxiedLayer::GetLayerDefn() +{ + if( poFeatureDefn != NULL ) + return poFeatureDefn; + + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) + { + poFeatureDefn = new OGRFeatureDefn(""); + } + else + { + poFeatureDefn = poUnderlyingLayer->GetLayerDefn(); + } + + poFeatureDefn->Reference(); + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRProxiedLayer::GetSpatialRef() +{ + if( poSRS != NULL ) + return poSRS; + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + OGRSpatialReference* poRet = poUnderlyingLayer->GetSpatialRef(); + if( poRet != NULL ) + { + poSRS = poRet; + poSRS->Reference(); + } + return poRet; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +int OGRProxiedLayer::GetFeatureCount( int bForce ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return 0; + return poUnderlyingLayer->GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->GetExtent(iGeomField, psExtent, bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->GetExtent(psExtent, bForce); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRProxiedLayer::TestCapability( const char * pszCapability ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return FALSE; + return poUnderlyingLayer->TestCapability(pszCapability); +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->CreateField(poField, bApproxOK); +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::DeleteField( int iField ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->DeleteField(iField); +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::ReorderFields( int* panMap ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->ReorderFields(panMap); +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->AlterFieldDefn(iField, poNewFieldDefn, nFlags); +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SyncToDisk() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SyncToDisk(); +} + +/************************************************************************/ +/* GetStyleTable() */ +/************************************************************************/ + +OGRStyleTable *OGRProxiedLayer::GetStyleTable() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetStyleTable(); +} + +/************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +void OGRProxiedLayer::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + return poUnderlyingLayer->SetStyleTableDirectly(poStyleTable); +} + +/************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ + +void OGRProxiedLayer::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + return poUnderlyingLayer->SetStyleTable(poStyleTable); +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::StartTransaction() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->StartTransaction(); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::CommitTransaction() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::RollbackTransaction() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->RollbackTransaction(); +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRProxiedLayer::GetFIDColumn() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return ""; + return poUnderlyingLayer->GetFIDColumn(); +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRProxiedLayer::GetGeometryColumn() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return ""; + return poUnderlyingLayer->GetGeometryColumn(); +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetIgnoredFields( const char **papszFields ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetIgnoredFields(papszFields); +} + diff --git a/ogr/ogrlayerpool.h b/ogr/ogrlayerpool.h new file mode 100644 index 0000000..19d1bc2 --- /dev/null +++ b/ogr/ogrlayerpool.h @@ -0,0 +1,162 @@ +/****************************************************************************** + * $Id: ogrlayerpool.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerPool and OGRProxiedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRLAYERPOOL_H_INCLUDED +#define _OGRLAYERPOOL_H_INCLUDED + +#include "ogrsf_frmts.h" + +typedef OGRLayer* (*OpenLayerFunc)(void* user_data); +typedef void (*FreeUserDataFunc)(void* user_data); + +class OGRLayerPool; + +/************************************************************************/ +/* OGRAbstractProxiedLayer */ +/************************************************************************/ + +class OGRAbstractProxiedLayer : public OGRLayer +{ + friend class OGRLayerPool; + + OGRAbstractProxiedLayer *poPrevLayer; /* Chain to a layer that was used more recently */ + OGRAbstractProxiedLayer *poNextLayer; /* Chain to a layer that was used less recently */ + + protected: + OGRLayerPool *poPool; + + virtual void CloseUnderlyingLayer() = 0; + + public: + OGRAbstractProxiedLayer(OGRLayerPool* poPool); + virtual ~OGRAbstractProxiedLayer(); +}; + +/************************************************************************/ +/* OGRLayerPool */ +/************************************************************************/ + +class OGRLayerPool +{ + protected: + OGRAbstractProxiedLayer *poMRULayer; /* the most recently used layer */ + OGRAbstractProxiedLayer *poLRULayer; /* the least recently used layer (still opened) */ + int nMRUListSize; /* the size of the list */ + int nMaxSimultaneouslyOpened; + + public: + OGRLayerPool(int nMaxSimultaneouslyOpened = 100); + ~OGRLayerPool(); + + void SetLastUsedLayer(OGRAbstractProxiedLayer* poProxiedLayer); + void UnchainLayer(OGRAbstractProxiedLayer* poProxiedLayer); + + int GetMaxSimultaneouslyOpened() const { return nMaxSimultaneouslyOpened; } + int GetSize() const { return nMRUListSize; } +}; + +/************************************************************************/ +/* OGRProxiedLayer */ +/************************************************************************/ + +class OGRProxiedLayer : public OGRAbstractProxiedLayer +{ + OpenLayerFunc pfnOpenLayer; + FreeUserDataFunc pfnFreeUserData; + void *pUserData; + OGRLayer *poUnderlyingLayer; + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSRS; + + int OpenUnderlyingLayer(); + + protected: + + virtual void CloseUnderlyingLayer(); + + public: + + OGRProxiedLayer(OGRLayerPool* poPool, + OpenLayerFunc pfnOpenLayer, + FreeUserDataFunc pfnFreeUserData, + void* pUserData); + virtual ~OGRProxiedLayer(); + + OGRLayer *GetUnderlyingLayer(); + + virtual OGRGeometry *GetSpatialFilter(); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex( long nIndex ); + virtual OGRFeature *GetFeature( long nFID ); + virtual OGRErr SetFeature( OGRFeature *poFeature ); + virtual OGRErr CreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( long nFID ); + + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr SyncToDisk(); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); +}; + +#endif // _OGRLAYERPOOL_H_INCLUDED diff --git a/ogr/ogrlinearring.cpp b/ogr/ogrlinearring.cpp index 09107d1..231713e 100644 --- a/ogr/ogrlinearring.cpp +++ b/ogr/ogrlinearring.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrlinearring.cpp 18676 2010-01-27 23:18:08Z rouault $ + * $Id: ogrlinearring.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRLinearRing geometry class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,7 @@ #include "ogr_geometry.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrlinearring.cpp 18676 2010-01-27 23:18:08Z rouault $"); +CPL_CVSID("$Id: ogrlinearring.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRLinearRing() */ @@ -62,7 +63,7 @@ OGRLinearRing::OGRLinearRing( OGRLinearRing * poSrcRing ) return; } - setNumPoints( poSrcRing->getNumPoints() ); + setNumPoints( poSrcRing->getNumPoints(), FALSE ); memcpy( paoPoints, poSrcRing->paoPoints, sizeof(OGRRawPoint) * getNumPoints() ); @@ -118,11 +119,13 @@ OGRErr OGRLinearRing::importFromWkb( unsigned char *pabyData, int nSize ) /************************************************************************/ OGRErr OGRLinearRing::exportToWkb( OGRwkbByteOrder eByteOrder, - unsigned char * pabyData ) const + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const { (void) eByteOrder; (void) pabyData; + (void) eWkbVariant; return OGRERR_UNSUPPORTED_OPERATION; } @@ -169,7 +172,7 @@ OGRErr OGRLinearRing::_importFromWkb( OGRwkbByteOrder eByteOrder, int b3D, } /* (Re)Allocation of paoPoints buffer. */ - setNumPoints( nNewNumPoints ); + setNumPoints( nNewNumPoints, FALSE ); if( b3D ) Make3D(); @@ -335,7 +338,8 @@ int OGRLinearRing::isClockwise() const { int i, v, next; - double dx0, dy0, dx1, dy1, crossproduct; + double dx0, dy0, dx1, dy1, crossproduct; + int bUseFallback = FALSE; if( nPointCount < 2 ) return TRUE; @@ -350,71 +354,66 @@ int OGRLinearRing::isClockwise() const paoPoints[i].x > paoPoints[v].x ) ) { v = i; + bUseFallback = FALSE; } - } - - /* Vertices may be duplicate, we have to go to nearest different in each direction */ - /* preceding */ - next = v - 1; - while ( 1 ) - { - if ( next < 0 ) - { - next = nPointCount - 1 - 1; - } - - if( !epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) - || !epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON) ) + else if ( paoPoints[i].y == paoPoints[v].y && + paoPoints[i].x == paoPoints[v].x ) { - break; + /* Two vertex with same coordinates are the lowest rightmost */ + /* vertex! We cannot use that point as the pivot (#5342) */ + bUseFallback = TRUE; } + } - if ( next == v ) /* So we cannot get into endless loop */ - { - break; - } + /* previous */ + next = v - 1; + if ( next < 0 ) + { + next = nPointCount - 1 - 1; + } - next--; + if( epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) && + epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON) ) + { + /* Don't try to be too clever by retrying with a next point */ + /* This can lead to false results as in the case of #3356 */ + bUseFallback = TRUE; } - + dx0 = paoPoints[next].x - paoPoints[v].x; dy0 = paoPoints[next].y - paoPoints[v].y; /* following */ next = v + 1; - while ( 1 ) + if ( next >= nPointCount - 1 ) { - if ( next >= nPointCount - 1 ) - { - next = 0; - } - - if ( !epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) - || !epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON) ) - { - break; - } - - if ( next == v ) /* So we cannot get into endless loop */ - { - break; - } + next = 0; + } - next++; + if( epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) && + epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON) ) + { + /* Don't try to be too clever by retrying with a next point */ + /* This can lead to false results as in the case of #3356 */ + bUseFallback = TRUE; } dx1 = paoPoints[next].x - paoPoints[v].x; dy1 = paoPoints[next].y - paoPoints[v].y; crossproduct = dx1 * dy0 - dx0 * dy1; - - if ( crossproduct > 0 ) /* CCW */ - return FALSE; - else if ( crossproduct < 0 ) /* CW */ - return TRUE; + + if (!bUseFallback) + { + if ( crossproduct > 0 ) /* CCW */ + return FALSE; + else if ( crossproduct < 0 ) /* CW */ + return TRUE; + } /* ok, this is a degenerate case : the extent of the polygon is less than EPSILON */ + /* or 2 nearly identical points were found */ /* Try with Green Formula as a fallback, but this is not a guarantee */ /* as we'll probably be affected by numerical instabilities */ @@ -437,15 +436,16 @@ void OGRLinearRing::reverseWindingOrder() { int pos = 0; - OGRPoint tempPoint; + OGRPoint pointA, pointB; for( int i = 0; i < nPointCount / 2; i++ ) { - getPoint( i, &tempPoint ); - pos = nPointCount - i - 1; - setPoint( i, getX(pos), getY(pos), getZ(pos) ); - setPoint( pos, tempPoint.getX(), tempPoint.getY(), tempPoint.getZ() ); - } + getPoint( i, &pointA ); + pos = nPointCount - i - 1; + getPoint( pos, &pointB ); + setPoint( i, &pointB ); + setPoint( pos, &pointA ); + } } /************************************************************************/ @@ -462,14 +462,9 @@ void OGRLinearRing::closeRings() || getY(0) != getY(nPointCount-1) || getZ(0) != getZ(nPointCount-1) ) { - /* Avoid implicit change of coordinate dimensionality - * if z=0.0 and dim=2 - */ - if( getCoordinateDimension() == 2 ) - addPoint( getX(0), getY(0) ); - else - addPoint( getX(0), getY(0), getZ(0) ); - + OGRPoint oFirstPoint; + getPoint( 0, &oFirstPoint ); + addPoint( &oFirstPoint ); } } @@ -546,17 +541,18 @@ OGRBoolean OGRLinearRing::isPointInRing(const OGRPoint* poPoint, int bTestEnvelo // test if ray starting from given point crosses segment (p - 1, p) int iNumCrossings = 0; + double prev_diff_x = getX(0) - dfTestX; + double prev_diff_y = getY(0) - dfTestY; + for ( int iPoint = 1; iPoint < iNumPoints; iPoint++ ) { - const int iPointPrev = iPoint - 1; - const double x1 = getX(iPoint) - dfTestX; const double y1 = getY(iPoint) - dfTestY; - const double x2 = getX(iPointPrev) - dfTestX; - const double y2 = getY(iPointPrev) - dfTestY; + const double x2 = prev_diff_x; + const double y2 = prev_diff_y; - if( ( ( y1 > 0 ) && ( y2 <= 0 ) ) || ( ( y2 > 0 ) && ( y1 <= 0 ) ) ) + if( ( ( y1 > 0 ) && ( y2 <= 0 ) ) || ( ( y2 > 0 ) && ( y1 <= 0 ) ) ) { // Check if ray intersects with segment of the ring const double dfIntersection = ( x1 * y2 - x2 * y1 ) / (y2 - y1); @@ -566,6 +562,9 @@ OGRBoolean OGRLinearRing::isPointInRing(const OGRPoint* poPoint, int bTestEnvelo iNumCrossings++; } } + + prev_diff_x = x1; + prev_diff_y = y1; } // If iNumCrossings number is even, given point is outside the ring, @@ -595,38 +594,44 @@ OGRBoolean OGRLinearRing::isPointOnRingBoundary(const OGRPoint* poPoint, int bTe const double dfTestY = poPoint->getY(); // Fast test if point is inside extent of the ring - OGREnvelope extent; - getEnvelope(&extent); - if ( !( dfTestX >= extent.MinX && dfTestX <= extent.MaxX - && dfTestY >= extent.MinY && dfTestY <= extent.MaxY ) ) + if( bTestEnvelope ) { - return 0; + OGREnvelope extent; + getEnvelope(&extent); + if ( !( dfTestX >= extent.MinX && dfTestX <= extent.MaxX + && dfTestY >= extent.MinY && dfTestY <= extent.MaxY ) ) + { + return 0; + } } + double prev_diff_x = getX(0) - dfTestX; + double prev_diff_y = getY(0) - dfTestY; + for ( int iPoint = 1; iPoint < iNumPoints; iPoint++ ) { - const int iPointPrev = iPoint - 1; - const double x1 = getX(iPoint) - dfTestX; const double y1 = getY(iPoint) - dfTestY; - const double x2 = getX(iPointPrev) - dfTestX; - const double y2 = getY(iPointPrev) - dfTestY; - - /* If iPoint and iPointPrev are the same, go on */ - if (x1 == x2 && y1 == y2) - { - continue; - } + const double x2 = prev_diff_x; + const double y2 = prev_diff_y; /* If the point is on the segment, return immediatly */ /* FIXME? If the test point is not exactly identical to one of */ /* the vertices of the ring, but somewhere on a segment, there's */ /* little chance that we get 0. So that should be tested against some epsilon */ + if ( x1 * y2 - x2 * y1 == 0 ) { - return 1; + /* If iPoint and iPointPrev are the same, go on */ + if( !(x1 == x2 && y1 == y2) ) + { + return 1; + } } + + prev_diff_x = x1; + prev_diff_y = y1; } return 0; diff --git a/ogr/ogrlinestring.cpp b/ogr/ogrlinestring.cpp index f918f21..cbed43d 100644 --- a/ogr/ogrlinestring.cpp +++ b/ogr/ogrlinestring.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrlinestring.cpp 17792 2009-10-11 17:44:13Z rouault $ + * $Id: ogrlinestring.cpp 27111 2014-03-28 21:34:19Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRLineString geometry class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,8 +31,9 @@ #include "ogr_geometry.h" #include "ogr_p.h" #include +#include "ogr_geos.h" -CPL_CVSID("$Id: ogrlinestring.cpp 17792 2009-10-11 17:44:13Z rouault $"); +CPL_CVSID("$Id: ogrlinestring.cpp 27111 2014-03-28 21:34:19Z rouault $"); /************************************************************************/ /* OGRLineString() */ @@ -69,12 +71,13 @@ OGRLineString::~OGRLineString() OGRwkbGeometryType OGRLineString::getGeometryType() const { - if( getCoordinateDimension() == 3 ) + if( nCoordDimension == 3 ) return wkbLineString25D; else return wkbLineString; } + /************************************************************************/ /* flattenTo2D() */ /************************************************************************/ @@ -297,7 +300,7 @@ double OGRLineString::getZ( int iVertex ) const * @param nNewPointCount the new number of points for geometry. */ -void OGRLineString::setNumPoints( int nNewPointCount ) +void OGRLineString::setNumPoints( int nNewPointCount, int bZeroizeNewContent ) { if( nNewPointCount == 0 ) @@ -314,19 +317,33 @@ void OGRLineString::setNumPoints( int nNewPointCount ) if( nNewPointCount > nPointCount ) { - paoPoints = (OGRRawPoint *) - OGRRealloc(paoPoints, sizeof(OGRRawPoint) * nNewPointCount); - - assert( paoPoints != NULL ); + OGRRawPoint* paoNewPoints = (OGRRawPoint *) + VSIRealloc(paoPoints, sizeof(OGRRawPoint) * nNewPointCount); + if (paoNewPoints == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate array for %d points", nNewPointCount); + return; + } + paoPoints = paoNewPoints; - memset( paoPoints + nPointCount, + if( bZeroizeNewContent ) + memset( paoPoints + nPointCount, 0, sizeof(OGRRawPoint) * (nNewPointCount - nPointCount) ); if( getCoordinateDimension() == 3 ) { - padfZ = (double *) - OGRRealloc( padfZ, sizeof(double)*nNewPointCount ); - memset( padfZ + nPointCount, 0, + double* padfNewZ = (double *) + VSIRealloc( padfZ, sizeof(double)*nNewPointCount ); + if (padfNewZ == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate array for %d points", nNewPointCount); + return; + } + padfZ = padfNewZ; + if( bZeroizeNewContent ) + memset( padfZ + nPointCount, 0, sizeof(double) * (nNewPointCount - nPointCount) ); } } @@ -354,7 +371,10 @@ void OGRLineString::setNumPoints( int nNewPointCount ) void OGRLineString::setPoint( int iPoint, OGRPoint * poPoint ) { - setPoint( iPoint, poPoint->getX(), poPoint->getY(), poPoint->getZ() ); + if (poPoint->getCoordinateDimension() < 3) + setPoint( iPoint, poPoint->getX(), poPoint->getY() ); + else + setPoint( iPoint, poPoint->getX(), poPoint->getY(), poPoint->getZ() ); } /************************************************************************/ @@ -383,7 +403,11 @@ void OGRLineString::setPoint( int iPoint, double xIn, double yIn, double zIn ) Make3D(); if( iPoint >= nPointCount ) + { setNumPoints( iPoint+1 ); + if (nPointCount < iPoint + 1) + return; + } paoPoints[iPoint].x = xIn; paoPoints[iPoint].y = yIn; @@ -403,12 +427,36 @@ void OGRLineString::setPoint( int iPoint, double xIn, double yIn ) { if( iPoint >= nPointCount ) + { setNumPoints( iPoint+1 ); + if (nPointCount < iPoint + 1) + return; + } paoPoints[iPoint].x = xIn; paoPoints[iPoint].y = yIn; } +/************************************************************************/ +/* setZ() */ +/************************************************************************/ + +void OGRLineString::setZ( int iPoint, double zIn ) +{ + if( getCoordinateDimension() == 2 ) + Make3D(); + + if( iPoint >= nPointCount ) + { + setNumPoints( iPoint+1 ); + if (nPointCount < iPoint + 1) + return; + } + + if( padfZ ) + padfZ[iPoint] = zIn; +} + /************************************************************************/ /* addPoint() */ /************************************************************************/ @@ -427,7 +475,10 @@ void OGRLineString::setPoint( int iPoint, double xIn, double yIn ) void OGRLineString::addPoint( OGRPoint * poPoint ) { - setPoint( nPointCount, poPoint->getX(), poPoint->getY(), poPoint->getZ() ); + if ( poPoint->getCoordinateDimension() < 3 ) + setPoint( nPointCount, poPoint->getX(), poPoint->getY() ); + else + setPoint( nPointCount, poPoint->getX(), poPoint->getY(), poPoint->getZ() ); } /************************************************************************/ @@ -481,7 +532,10 @@ void OGRLineString::setPoints( int nPointsIn, OGRRawPoint * paoPointsIn, double * padfZ ) { - setNumPoints( nPointsIn ); + setNumPoints( nPointsIn, FALSE ); + if (nPointCount < nPointsIn) + return; + memcpy( paoPoints, paoPointsIn, sizeof(OGRRawPoint) * nPointsIn); /* -------------------------------------------------------------------- */ @@ -534,7 +588,9 @@ void OGRLineString::setPoints( int nPointsIn, double * padfX, double * padfY, /* -------------------------------------------------------------------- */ /* Assign values. */ /* -------------------------------------------------------------------- */ - setNumPoints( nPointsIn ); + setNumPoints( nPointsIn, FALSE ); + if (nPointCount < nPointsIn) + return; for( i = 0; i < nPointsIn; i++ ) { @@ -583,6 +639,97 @@ void OGRLineString::getPoints( OGRRawPoint * paoPointsOut, double * padfZ ) cons } +/************************************************************************/ +/* getPoints() */ +/************************************************************************/ + +/** + * \brief Returns all points of line string. + * + * This method copies all points into user arrays. The user provides the + * stride between 2 consecutives elements of the array. + * + * On some CPU architectures, care must be taken so that the arrays are properly aligned. + * + * There is no SFCOM analog to this method. + * + * @param pabyX a buffer of at least (sizeof(double) * nXStride * nPointCount) bytes, may be NULL. + * @param nXStride the number of bytes between 2 elements of pabyX. + * @param pabyY a buffer of at least (sizeof(double) * nYStride * nPointCount) bytes, may be NULL. + * @param nYStride the number of bytes between 2 elements of pabyY. + * @param pabyZ a buffer of at last size (sizeof(double) * nZStride * nPointCount) bytes, may be NULL. + * @param nZStride the number of bytes between 2 elements of pabyZ. + * + * @since OGR 1.9.0 + */ + +void OGRLineString::getPoints( void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride) const +{ + int i; + if (pabyX != NULL && nXStride == 0) + return; + if (pabyY != NULL && nYStride == 0) + return; + if (pabyZ != NULL && nZStride == 0) + return; + if (nXStride == 2 * sizeof(double) && + nYStride == 2 * sizeof(double) && + (char*)pabyY == (char*)pabyX + sizeof(double) && + (pabyZ == NULL || nZStride == sizeof(double))) + { + getPoints((OGRRawPoint *)pabyX, (double*)pabyZ); + return; + } + for(i=0;igetNumPoints(); + if (nOtherLineNumPoints == 0) + return; + /* -------------------------------------------------------------------- */ /* Do a bit of argument defaulting and validation. */ /* -------------------------------------------------------------------- */ if( nEndVertex == -1 ) - nEndVertex = poOtherLine->getNumPoints() - 1; + nEndVertex = nOtherLineNumPoints - 1; if( nStartVertex < 0 || nEndVertex < 0 - || nStartVertex >= poOtherLine->getNumPoints() - || nEndVertex >= poOtherLine->getNumPoints() ) + || nStartVertex >= nOtherLineNumPoints + || nEndVertex >= nOtherLineNumPoints ) { CPLAssert( FALSE ); return; @@ -625,7 +776,9 @@ void OGRLineString::addSubLineString( const OGRLineString *poOtherLine, int nOldPoints = nPointCount; int nPointsToAdd = ABS(nEndVertex-nStartVertex) + 1; - setNumPoints( nPointsToAdd + nOldPoints ); + setNumPoints( nPointsToAdd + nOldPoints, FALSE ); + if (nPointCount < nPointsToAdd + nOldPoints) + return; /* -------------------------------------------------------------------- */ /* Copy the x/y points - forward copies use memcpy. */ @@ -698,21 +851,11 @@ OGRErr OGRLineString::importFromWkb( unsigned char * pabyData, /* geometry type is between 0 and 255 so we only have to fetch */ /* one byte. */ /* -------------------------------------------------------------------- */ + OGRBoolean bIs3D; OGRwkbGeometryType eGeometryType; - int bIs3D = FALSE; + OGRErr err = OGRReadWKBGeometryType( pabyData, &eGeometryType, &bIs3D ); - if( eByteOrder == wkbNDR ) - { - eGeometryType = (OGRwkbGeometryType) pabyData[1]; - bIs3D = pabyData[4] & 0x80 || pabyData[2] & 0x80; - } - else - { - eGeometryType = (OGRwkbGeometryType) pabyData[4]; - bIs3D = pabyData[1] & 0x80 || pabyData[3] & 0x80; - } - - if( eGeometryType != wkbLineString ) + if( err != OGRERR_NONE || eGeometryType != wkbLineString ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ @@ -741,7 +884,9 @@ OGRErr OGRLineString::importFromWkb( unsigned char * pabyData, return OGRERR_NOT_ENOUGH_DATA; } - setNumPoints( nNewNumPoints ); + setNumPoints( nNewNumPoints, FALSE ); + if (nPointCount < nNewNumPoints) + return OGRERR_FAILURE; if( bIs3D ) Make3D(); @@ -796,7 +941,8 @@ OGRErr OGRLineString::importFromWkb( unsigned char * pabyData, /************************************************************************/ OGRErr OGRLineString::exportToWkb( OGRwkbByteOrder eByteOrder, - unsigned char * pabyData ) const + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const { /* -------------------------------------------------------------------- */ @@ -808,6 +954,9 @@ OGRErr OGRLineString::exportToWkb( OGRwkbByteOrder eByteOrder, /* Set the geometry feature type. */ /* -------------------------------------------------------------------- */ GUInt32 nGType = getGeometryType(); + + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); if( eByteOrder == wkbNDR ) nGType = CPL_LSBWORD32( nGType ); @@ -869,16 +1018,7 @@ OGRErr OGRLineString::importFromWkt( char ** ppszInput ) char szToken[OGR_WKT_TOKEN_MAX]; const char *pszInput = *ppszInput; - if( paoPoints != NULL ) - { - nPointCount = 0; - - CPLFree( paoPoints ); - paoPoints = NULL; - - CPLFree( padfZ ); - padfZ = NULL; - } + empty(); /* -------------------------------------------------------------------- */ /* Read and verify the ``LINESTRING'' keyword token. */ @@ -889,9 +1029,10 @@ OGRErr OGRLineString::importFromWkt( char ** ppszInput ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ -/* Check for EMPTY or (EMPTY). */ +/* Check for EMPTY */ /* -------------------------------------------------------------------- */ const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; pszPreScan = OGRWktReadToken( pszInput, szToken ); if( EQUAL(szToken,"EMPTY") ) @@ -900,20 +1041,57 @@ OGRErr OGRLineString::importFromWkt( char ** ppszInput ) return OGRERR_NONE; } +/* -------------------------------------------------------------------- */ +/* Check for Z, M or ZM. Will ignore the Measure */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szToken,"Z") ) + { + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } + + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ + return OGRERR_NONE; + } + } + if( !EQUAL(szToken,"(") ) return OGRERR_CORRUPT_DATA; - - pszPreScan = OGRWktReadToken( pszPreScan, szToken ); - if( EQUAL(szToken,"EMPTY") ) + + if ( !bHasZ && !bHasM ) { + /* Test for old-style LINESTRING(EMPTY) */ pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszInput = OGRWktReadToken( pszPreScan, szToken ); - *ppszInput = (char *) pszPreScan; - - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else - return OGRERR_NONE; + if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszInput; + empty(); + return OGRERR_NONE; + } + } } /* -------------------------------------------------------------------- */ @@ -933,7 +1111,17 @@ OGRErr OGRLineString::importFromWkt( char ** ppszInput ) if( padfZ == NULL ) nCoordDimension = 2; else - nCoordDimension = 3; + { + /* Ignore Z array when we have a LINESTRING M */ + if (bHasM && !bHasZ) + { + nCoordDimension = 2; + CPLFree(padfZ); + padfZ = NULL; + } + else + nCoordDimension = 3; + } return OGRERR_NONE; } @@ -954,7 +1142,7 @@ OGRErr OGRLineString::exportToWkt( char ** ppszDstText ) const /* -------------------------------------------------------------------- */ /* Handle special empty case. */ /* -------------------------------------------------------------------- */ - if( nPointCount == 0 ) + if( IsEmpty() ) { CPLString osEmpty; osEmpty.Printf("%s EMPTY",getGeometryName()); @@ -1109,6 +1297,265 @@ void OGRLineString::Value( double dfDistance, OGRPoint * poPoint ) const EndPoint( poPoint ); } +/************************************************************************/ +/* Project() */ +/* */ +/* Return distance of point projected on line from origin of this line */ +/************************************************************************/ + +/** +* \brief Project point on linestring. +* +* The input point projeted on linestring. This is the shortest distance +* from point to the linestring. The distance from begin of linestring to +* the point projection returned. +* +* This method is built on the GEOS library (GEOS >= 3.2.0), check it for the +* definition of the geometry operation. +* If OGR is built without the GEOS library, this method will always return -1, +* issuing a CPLE_NotSupported error. +* +* @return a distance from the begin of the linestring to the projected point. +* +* @since OGR 1.11.0 +*/ + +/* GEOS >= 3.2.0 for project capabilty */ +#if defined(HAVE_GEOS) +#if GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 2) +#define HAVE_GEOS_PROJECT +#endif +#endif + + +double OGRLineString::Project(const OGRPoint *poPoint) const + +{ + double dfResult = -1; +#ifndef HAVE_GEOS_PROJECT + + + CPLError(CE_Failure, CPLE_NotSupported, + "GEOS support not enabled."); + return dfResult; + +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hPointGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hPointGeosGeom = poPoint->exportToGEOS(hGEOSCtxt); + if (hThisGeosGeom != NULL && hPointGeosGeom != NULL) + { + dfResult = GEOSProject_r(hGEOSCtxt, hThisGeosGeom, hPointGeosGeom); + } + GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom); + GEOSGeom_destroy_r(hGEOSCtxt, hPointGeosGeom); + freeGEOSContext(hGEOSCtxt); + + return dfResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* getSubLine() */ +/* */ +/* Extracts a portion of this OGRLineString into a new OGRLineString */ +/************************************************************************/ + + +/** +* \brief Get the portion of linestring. +* +* The portion of the linestring extracted to new one. The input distances +* (maybe present as ratio of length of linestring) set begin and end of +* extracted portion. +* +* @param dfDistanceFrom The distance from the origin of linestring, where the subline should begins +* @param dfDistanceTo The distance from the origin of linestring, where the subline should ends +* @param bAsRatio The flag indicating that distances are the ratio of the linestring length. +* +* @return a newly allocated linestring now owned by the caller, or NULL on failure. +* +* @since OGR 1.11.0 +*/ + + +OGRLineString* OGRLineString::getSubLine(double dfDistanceFrom, double dfDistanceTo, int bAsRatio) const + +{ + OGRLineString *poNewLineString; + double dfLength = 0; + int i; + + poNewLineString = new OGRLineString(); + + poNewLineString->assignSpatialReference(getSpatialReference()); + //poNewLineString->setPoints(nPointCount, paoPoints, padfZ); + poNewLineString->setCoordinateDimension(getCoordinateDimension()); + + double dfLen = get_Length(); + if (bAsRatio == TRUE) + { + //convert to real distance + dfDistanceFrom *= dfLen; + dfDistanceTo *= dfLen; + } + + if (dfDistanceFrom < 0) + dfDistanceFrom = 0; + if (dfDistanceTo > dfLen) + dfDistanceTo = dfLen; + + if ( dfDistanceFrom > dfDistanceTo || dfDistanceFrom >= dfLen) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Input distances are invalid."); + + return NULL; + } + + //get first point + + if (dfDistanceFrom == 0) + { + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y, padfZ[0]); + else + poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y); + + i = 0; + } + else + { + for (i = 0; i < nPointCount - 1; i++) + { + double dfDeltaX, dfDeltaY, dfSegLength; + + dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x; + dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y; + dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + + double dfX, dfY, dfRatio; + + if (dfSegLength > 0) + { + if ((dfLength <= dfDistanceFrom) && ((dfLength + dfSegLength) >= + dfDistanceFrom)) + { + dfRatio = (dfDistanceFrom - dfLength) / dfSegLength; + + dfX = paoPoints[i].x * (1 - dfRatio) + + paoPoints[i + 1].x * dfRatio; + dfY = paoPoints[i].y * (1 - dfRatio) + + paoPoints[i + 1].y * dfRatio; + + if (getCoordinateDimension() == 3) + { + poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio) + + padfZ[i+1] * dfRatio); + } + else + { + poNewLineString->addPoint(dfX, dfY); + } + + //check if dfDistanceTo is in same segment + if ((dfLength <= dfDistanceTo) && ((dfLength + dfSegLength) >= + dfDistanceTo)) + { + dfRatio = (dfDistanceTo - dfLength) / dfSegLength; + + dfX = paoPoints[i].x * (1 - dfRatio) + + paoPoints[i + 1].x * dfRatio; + dfY = paoPoints[i].y * (1 - dfRatio) + + paoPoints[i + 1].y * dfRatio; + + if (getCoordinateDimension() == 3) + { + poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio) + + padfZ[i + 1] * dfRatio); + } + else + { + poNewLineString->addPoint(dfX, dfY); + } + + if (poNewLineString->getNumPoints() < 2) + { + delete poNewLineString; + poNewLineString = NULL; + } + + return poNewLineString; + } + i++; + dfLength += dfSegLength; + break; + } + + dfLength += dfSegLength; + } + } + } + + //add points + for (; i < nPointCount - 1; i++) + { + double dfDeltaX, dfDeltaY, dfSegLength; + + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y, padfZ[i]); + else + poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y); + + dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x; + dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y; + dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + + double dfX, dfY, dfRatio; + + if (dfSegLength > 0) + { + if ((dfLength <= dfDistanceTo) && ((dfLength + dfSegLength) >= + dfDistanceTo)) + { + dfRatio = (dfDistanceTo - dfLength) / dfSegLength; + + dfX = paoPoints[i].x * (1 - dfRatio) + + paoPoints[i + 1].x * dfRatio; + dfY = paoPoints[i].y * (1 - dfRatio) + + paoPoints[i + 1].y * dfRatio; + + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio) + + padfZ[i + 1] * dfRatio); + else + poNewLineString->addPoint(dfX, dfY); + + return poNewLineString; + } + + dfLength += dfSegLength; + } + } + + + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(paoPoints[nPointCount - 1].x, paoPoints[nPointCount - 1].y, padfZ[nPointCount - 1]); + else + poNewLineString->addPoint(paoPoints[nPointCount - 1].x, paoPoints[nPointCount - 1].y); + + if (poNewLineString->getNumPoints() < 2) + { + delete poNewLineString; + poNewLineString = NULL; + } + + return poNewLineString; +} + /************************************************************************/ /* getEnvelope() */ /************************************************************************/ @@ -1118,8 +1565,14 @@ void OGRLineString::getEnvelope( OGREnvelope * psEnvelope ) const { double dfMinX, dfMinY, dfMaxX, dfMaxY; - if( nPointCount == 0 ) + if( IsEmpty() ) + { + psEnvelope->MinX = 0; + psEnvelope->MaxX = 0; + psEnvelope->MinY = 0; + psEnvelope->MaxY = 0; return; + } dfMinX = dfMaxX = paoPoints[0].x; dfMinY = dfMaxY = paoPoints[0].y; @@ -1142,6 +1595,39 @@ void OGRLineString::getEnvelope( OGREnvelope * psEnvelope ) const psEnvelope->MaxY = dfMaxY; } + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRLineString::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + getEnvelope((OGREnvelope*)psEnvelope); + + double dfMinZ, dfMaxZ; + + if( IsEmpty() || padfZ == NULL ) + { + psEnvelope->MinZ = 0; + psEnvelope->MaxZ = 0; + return; + } + + dfMinZ = dfMaxZ = padfZ[0]; + + for( int iPoint = 1; iPoint < nPointCount; iPoint++ ) + { + if( dfMinZ > padfZ[iPoint] ) + dfMinZ = padfZ[iPoint]; + if( dfMaxZ < padfZ[iPoint] ) + dfMaxZ = padfZ[iPoint]; + } + + psEnvelope->MinZ = dfMinZ; + psEnvelope->MaxZ = dfMaxZ; +} + /************************************************************************/ /* Equals() */ /************************************************************************/ @@ -1157,6 +1643,9 @@ OGRBoolean OGRLineString::Equals( OGRGeometry * poOther ) const if( poOther->getGeometryType() != getGeometryType() ) return FALSE; + if( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + // we should eventually test the SRS. if( getNumPoints() != poOLine->getNumPoints() ) @@ -1184,16 +1673,23 @@ OGRErr OGRLineString::transform( OGRCoordinateTransformation *poCT ) return OGRERR_FAILURE; #else double *xyz; - int i; + int *pabSuccess; + int i, j; /* -------------------------------------------------------------------- */ -/* Because we don't want to partially transform this geometry */ -/* (if some points fail after some have succeeded) we will */ -/* instead make a copy of the points to operate on. */ +/* Make a copy of the points to operate on, so as to be able to */ +/* keep only valid reprojected points if partial reprojection enabled */ +/* or keeping intact the original geometry if only full reprojection */ +/* allowed. */ /* -------------------------------------------------------------------- */ - xyz = (double *) CPLMalloc(sizeof(double) * nPointCount * 3); - if( xyz == NULL ) + xyz = (double *) VSIMalloc(sizeof(double) * nPointCount * 3); + pabSuccess = (int *) VSICalloc(sizeof(int), nPointCount); + if( xyz == NULL || pabSuccess == NULL ) + { + VSIFree(xyz); + VSIFree(pabSuccess); return OGRERR_NOT_ENOUGH_MEMORY; + } for( i = 0; i < nPointCount; i++ ) { @@ -1208,22 +1704,75 @@ OGRErr OGRLineString::transform( OGRCoordinateTransformation *poCT ) /* -------------------------------------------------------------------- */ /* Transform and reapply. */ /* -------------------------------------------------------------------- */ - if( !poCT->Transform( nPointCount, xyz, xyz + nPointCount, - xyz+nPointCount*2 ) ) + poCT->TransformEx( nPointCount, xyz, xyz + nPointCount, + xyz+nPointCount*2, pabSuccess ); + + const char* pszEnablePartialReprojection = NULL; + + for( i = 0, j = 0; i < nPointCount; i++ ) { - CPLFree( xyz ); - return OGRERR_FAILURE; + if (pabSuccess[i]) + { + xyz[j] = xyz[i]; + xyz[j+nPointCount] = xyz[i+nPointCount]; + xyz[j+2*nPointCount] = xyz[i+2*nPointCount]; + j ++; + } + else + { + if (pszEnablePartialReprojection == NULL) + pszEnablePartialReprojection = + CPLGetConfigOption("OGR_ENABLE_PARTIAL_REPROJECTION", NULL); + if (pszEnablePartialReprojection == NULL) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + /* Check that there is at least one valid reprojected point */ + /* and issue an error giving an hint to use OGR_ENABLE_PARTIAL_REPROJECTION */ + int bHasOneValidPoint = (j != 0); + for( ; i < nPointCount && !bHasOneValidPoint; i++ ) + { + if (pabSuccess[i]) + bHasOneValidPoint = TRUE; + } + if (bHasOneValidPoint) + { + bHasWarned = TRUE; + CPLError(CE_Failure, CPLE_AppDefined, + "Full reprojection failed, but partial is possible if you define " + "OGR_ENABLE_PARTIAL_REPROJECTION configuration option to TRUE"); + } + } + + CPLFree( xyz ); + CPLFree( pabSuccess ); + return OGRERR_FAILURE; + } + else if (!CSLTestBoolean(pszEnablePartialReprojection)) + { + CPLFree( xyz ); + CPLFree( pabSuccess ); + return OGRERR_FAILURE; + } + } } - else + + if (j == 0 && nPointCount != 0) { - setPoints( nPointCount, xyz, xyz+nPointCount, - ( padfZ ) ? xyz+nPointCount*2 : NULL); CPLFree( xyz ); + CPLFree( pabSuccess ); + return OGRERR_FAILURE; + } - assignSpatialReference( poCT->GetTargetCS() ); + setPoints( j, xyz, xyz+nPointCount, + ( padfZ ) ? xyz+nPointCount*2 : NULL); + CPLFree( xyz ); + CPLFree( pabSuccess ); - return OGRERR_NONE; - } + assignSpatialReference( poCT->GetTargetCS() ); + + return OGRERR_NONE; #endif } @@ -1254,6 +1803,7 @@ void OGRLineString::segmentize( double dfMaxLength ) double* padfNewZ = NULL; int nNewPointCount = 0; double dfSquareMaxLength = dfMaxLength * dfMaxLength; + const int nCoordinateDimension = getCoordinateDimension(); for( i = 0; i < nPointCount; i++ ) { @@ -1261,7 +1811,7 @@ void OGRLineString::segmentize( double dfMaxLength ) OGRRealloc(paoNewPoints, sizeof(OGRRawPoint) * (nNewPointCount + 1)); paoNewPoints[nNewPointCount] = paoPoints[i]; - if( getCoordinateDimension() == 3 ) + if( nCoordinateDimension == 3 ) { padfNewZ = (double *) OGRRealloc(padfNewZ, sizeof(double) * (nNewPointCount + 1)); @@ -1283,7 +1833,7 @@ void OGRLineString::segmentize( double dfMaxLength ) paoNewPoints = (OGRRawPoint *) OGRRealloc(paoNewPoints, sizeof(OGRRawPoint) * (nNewPointCount + nIntermediatePoints)); - if( getCoordinateDimension() == 3 ) + if( nCoordinateDimension == 3 ) { padfNewZ = (double *) OGRRealloc(padfNewZ, sizeof(double) * (nNewPointCount + nIntermediatePoints)); @@ -1293,10 +1843,10 @@ void OGRLineString::segmentize( double dfMaxLength ) { paoNewPoints[nNewPointCount + j - 1].x = paoPoints[i].x + j * dfX / (nIntermediatePoints + 1); paoNewPoints[nNewPointCount + j - 1].y = paoPoints[i].y + j * dfY / (nIntermediatePoints + 1); - if( getCoordinateDimension() == 3 ) + if( nCoordinateDimension == 3 ) { /* No interpolation */ - padfNewZ[nNewPointCount + j - 1] = 0; + padfNewZ[nNewPointCount + j - 1] = padfZ[i]; } } @@ -1308,9 +1858,24 @@ void OGRLineString::segmentize( double dfMaxLength ) paoPoints = paoNewPoints; nPointCount = nNewPointCount; - if( getCoordinateDimension() == 3 ) + if( nCoordinateDimension == 3 ) { OGRFree(padfZ); padfZ = padfNewZ; } } + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRLineString::swapXY() +{ + int i; + for( i = 0; i < nPointCount; i++ ) + { + double dfTemp = paoPoints[i].x; + paoPoints[i].x = paoPoints[i].y; + paoPoints[i].y = dfTemp; + } +} diff --git a/ogr/ogrmultilinestring.cpp b/ogr/ogrmultilinestring.cpp index e7fa975..0815586 100644 --- a/ogr/ogrmultilinestring.cpp +++ b/ogr/ogrmultilinestring.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrmultilinestring.cpp 14336 2008-04-20 14:36:09Z rouault $ + * $Id: ogrmultilinestring.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRMultiLineString class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,7 @@ #include "ogr_geometry.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrmultilinestring.cpp 14336 2008-04-20 14:36:09Z rouault $"); +CPL_CVSID("$Id: ogrmultilinestring.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRMultiLineString() */ @@ -61,6 +62,16 @@ OGRwkbGeometryType OGRMultiLineString::getGeometryType() const return wkbMultiLineString; } +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRMultiLineString::getDimension() const + +{ + return 1; +} + /************************************************************************/ /* getGeometryName() */ /************************************************************************/ @@ -133,38 +144,79 @@ OGRErr OGRMultiLineString::importFromWkt( char ** ppszInput ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ -/* The next character should be a ( indicating the start of the */ -/* list of linestrings. */ +/* Check for EMPTY ... */ /* -------------------------------------------------------------------- */ - pszInput = OGRWktReadToken( pszInput, szToken ); + const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; + pszPreScan = OGRWktReadToken( pszInput, szToken ); if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszInput; + *ppszInput = (char *) pszPreScan; + empty(); return OGRERR_NONE; } - if( szToken[0] != '(' ) - return OGRERR_CORRUPT_DATA; - /* -------------------------------------------------------------------- */ -/* If the next token is EMPTY, then verify that we have proper */ -/* EMPTY format will a trailing closing bracket. */ +/* Check for Z, M or ZM. Will ignore the Measure */ /* -------------------------------------------------------------------- */ - OGRWktReadToken( pszInput, szToken ); - if( EQUAL(szToken,"EMPTY") ) + else if( EQUAL(szToken,"Z") ) { - pszInput = OGRWktReadToken( pszInput, szToken ); - pszInput = OGRWktReadToken( pszInput, szToken ); - - *ppszInput = (char *) pszInput; + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ return OGRERR_NONE; + } } + if( !EQUAL(szToken,"(") ) + return OGRERR_CORRUPT_DATA; + + if ( !bHasZ && !bHasM ) + { + /* Test for old-style MULTILINESTRING(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + + if( EQUAL(szToken,",") ) + { + /* This is OK according to SFSQL SPEC. */ + } + else if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszPreScan; + empty(); + return OGRERR_NONE; + } + } + } + + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + /* ==================================================================== */ /* Read each line in turn. Note that we try to reuse the same */ /* point list buffer from ring to ring to cut down on */ @@ -178,13 +230,26 @@ OGRErr OGRMultiLineString::importFromWkt( char ** ppszInput ) { int nPoints = 0; + const char* pszNext = OGRWktReadToken( pszInput, szToken ); + if (EQUAL(szToken,"EMPTY")) + { + eErr = addGeometryDirectly( new OGRLineString() ); + if( eErr != OGRERR_NONE ) + return eErr; + + pszInput = OGRWktReadToken( pszNext, szToken ); + if ( !EQUAL(szToken, ",") ) + break; + + continue; + } /* -------------------------------------------------------------------- */ /* Read points for one line from input. */ /* -------------------------------------------------------------------- */ pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoints, &nPoints ); - if( pszInput == NULL ) + if( pszInput == NULL || nPoints == 0 ) { eErr = OGRERR_CORRUPT_DATA; break; @@ -196,7 +261,11 @@ OGRErr OGRMultiLineString::importFromWkt( char ** ppszInput ) OGRLineString *poLine; poLine = new OGRLineString(); - poLine->setPoints( nPoints, paoPoints, padfZ ); + /* Ignore Z array when we have a MULTILINESTRING M */ + if (bHasM && !bHasZ) + poLine->setPoints( nPoints, paoPoints, NULL ); + else + poLine->setPoints( nPoints, paoPoints, padfZ ); eErr = addGeometryDirectly( poLine ); diff --git a/ogr/ogrmultipoint.cpp b/ogr/ogrmultipoint.cpp index a35f645..4a13e85 100644 --- a/ogr/ogrmultipoint.cpp +++ b/ogr/ogrmultipoint.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrmultipoint.cpp 14336 2008-04-20 14:36:09Z rouault $ + * $Id: ogrmultipoint.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRMultiPoint class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,7 +32,7 @@ #include "ogr_p.h" #include -CPL_CVSID("$Id: ogrmultipoint.cpp 14336 2008-04-20 14:36:09Z rouault $"); +CPL_CVSID("$Id: ogrmultipoint.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRMultiPoint() */ @@ -54,6 +55,16 @@ OGRwkbGeometryType OGRMultiPoint::getGeometryType() const return wkbMultiPoint; } +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRMultiPoint::getDimension() const + +{ + return 0; +} + /************************************************************************/ /* getGeometryName() */ /************************************************************************/ @@ -192,59 +203,90 @@ OGRErr OGRMultiPoint::importFromWkt( char ** ppszInput ) if( !EQUAL(szToken,getGeometryName()) ) return OGRERR_CORRUPT_DATA; + /* -------------------------------------------------------------------- */ -/* Skip past first bracket for checking purposes, but don't */ -/* alter pszInput. */ +/* Check for EMPTY ... */ /* -------------------------------------------------------------------- */ - const char *pszPreScan = pszInput; + const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; - // skip white space. - while( *pszPreScan == ' ' || *pszPreScan == '\t' ) - pszPreScan++; - - // Handle the proper EMPTY syntax. - if( EQUALN(pszPreScan,"EMPTY",5) ) + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszPreScan+5; + *ppszInput = (char *) pszPreScan; + empty(); return OGRERR_NONE; } - // Skip outer bracket. - if( *pszPreScan != '(' ) - return OGRERR_CORRUPT_DATA; - - pszPreScan++; - /* -------------------------------------------------------------------- */ -/* If the next token is EMPTY, then verify that we have proper */ -/* EMPTY format will a trailing closing bracket. */ +/* Check for Z, M or ZM. Will ignore the Measure */ /* -------------------------------------------------------------------- */ - OGRWktReadToken( pszPreScan, szToken ); - if( EQUAL(szToken,"EMPTY") ) + else if( EQUAL(szToken,"Z") ) { - pszInput = OGRWktReadToken( pszPreScan, szToken ); - pszInput = OGRWktReadToken( pszInput, szToken ); - - *ppszInput = (char *) pszInput; + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ return OGRERR_NONE; + } } -/* -------------------------------------------------------------------- */ -/* Check for inner bracket indicating the improper bracketed */ -/* format which we still want to support. */ -/* -------------------------------------------------------------------- */ - // skip white space. - while( *pszPreScan == ' ' || *pszPreScan == '\t' ) - pszPreScan++; + if( !EQUAL(szToken,"(") ) + return OGRERR_CORRUPT_DATA; + + if ( !bHasZ && !bHasM ) + { + /* Test for old-style MULTIPOINT(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + + if( EQUAL(szToken,",") ) + { + /* This is OK according to SFSQL SPEC. */ + } + else if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszPreScan; + empty(); + return OGRERR_NONE; + } + } + } + + pszPreScan = OGRWktReadToken( pszInput, szToken ); + OGRWktReadToken( pszPreScan, szToken ); // Do we have an inner bracket? - if( *pszPreScan == '(' ) - return importFromWkt_Bracketed( ppszInput ); - + if (EQUAL(szToken,"(") || EQUAL(szToken, "EMPTY") ) + return importFromWkt_Bracketed( ppszInput, bHasM, bHasZ ); + + if (bHasZ || bHasM) + { + return OGRERR_CORRUPT_DATA; + } + /* -------------------------------------------------------------------- */ /* Read the point list which should consist of exactly one point. */ /* -------------------------------------------------------------------- */ @@ -256,7 +298,11 @@ OGRErr OGRMultiPoint::importFromWkt( char ** ppszInput ) pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoint, &nPointCount ); if( pszInput == NULL ) + { + OGRFree( paoPoints ); + OGRFree( padfZ ); return OGRERR_CORRUPT_DATA; + } /* -------------------------------------------------------------------- */ /* Transform raw points into point objects. */ @@ -297,7 +343,7 @@ OGRErr OGRMultiPoint::importFromWkt( char ** ppszInput ) /* importFromWkt(). */ /************************************************************************/ -OGRErr OGRMultiPoint::importFromWkt_Bracketed( char ** ppszInput ) +OGRErr OGRMultiPoint::importFromWkt_Bracketed( char ** ppszInput, int bHasM, int bHasZ ) { @@ -310,6 +356,12 @@ OGRErr OGRMultiPoint::importFromWkt_Bracketed( char ** ppszInput ) /* -------------------------------------------------------------------- */ pszInput = OGRWktReadToken( pszInput, szToken ); + if (bHasZ || bHasM) + { + /* Skip Z, M or ZM */ + pszInput = OGRWktReadToken( pszInput, szToken ); + } + /* -------------------------------------------------------------------- */ /* Read points till we get to the closing bracket. */ /* -------------------------------------------------------------------- */ @@ -318,18 +370,37 @@ OGRErr OGRMultiPoint::importFromWkt_Bracketed( char ** ppszInput ) OGRRawPoint *paoPoints = NULL; double *padfZ = NULL; - while( (pszInput = OGRWktReadToken( pszInput, szToken )) + while( (pszInput = OGRWktReadToken( pszInput, szToken )) != NULL && (EQUAL(szToken,"(") || EQUAL(szToken,",")) ) { OGRGeometry *poGeom; + const char* pszNext = OGRWktReadToken( pszInput, szToken ); + if (EQUAL(szToken,"EMPTY")) + { + poGeom = new OGRPoint(0,0); + poGeom->empty(); + eErr = addGeometryDirectly( poGeom ); + if( eErr != OGRERR_NONE ) + return eErr; + + pszInput = pszNext; + + continue; + } + pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoint, &nPointCount ); if( pszInput == NULL || nPointCount != 1 ) + { + OGRFree( paoPoints ); + OGRFree( padfZ ); return OGRERR_CORRUPT_DATA; + } - if( padfZ ) + /* Ignore Z array when we have a MULTIPOINT M */ + if( padfZ && !(bHasM && !bHasZ)) poGeom = new OGRPoint( paoPoints[0].x, paoPoints[0].y, padfZ[0] ); diff --git a/ogr/ogrmultipolygon.cpp b/ogr/ogrmultipolygon.cpp index a04412b..913a781 100644 --- a/ogr/ogrmultipolygon.cpp +++ b/ogr/ogrmultipolygon.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrmultipolygon.cpp 16898 2009-05-01 12:23:36Z rouault $ + * $Id: ogrmultipolygon.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRMultiPolygon class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,7 @@ #include "ogr_geometry.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrmultipolygon.cpp 16898 2009-05-01 12:23:36Z rouault $"); +CPL_CVSID("$Id: ogrmultipolygon.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRMultiPolygon() */ @@ -53,6 +54,16 @@ OGRwkbGeometryType OGRMultiPolygon::getGeometryType() const return wkbMultiPolygon; } +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRMultiPolygon::getDimension() const + +{ + return 2; +} + /************************************************************************/ /* getGeometryName() */ /************************************************************************/ @@ -125,38 +136,79 @@ OGRErr OGRMultiPolygon::importFromWkt( char ** ppszInput ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ -/* The next character should be a ( indicating the start of the */ -/* list of polygons. */ +/* Check for EMPTY ... */ /* -------------------------------------------------------------------- */ - pszInput = OGRWktReadToken( pszInput, szToken ); + const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; + pszPreScan = OGRWktReadToken( pszInput, szToken ); if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszInput; + *ppszInput = (char *) pszPreScan; + empty(); return OGRERR_NONE; } - if( szToken[0] != '(' ) - return OGRERR_CORRUPT_DATA; - /* -------------------------------------------------------------------- */ -/* If the next token is EMPTY, then verify that we have proper */ -/* EMPTY format will a trailing closing bracket. */ +/* Check for Z, M or ZM. Will ignore the Measure */ /* -------------------------------------------------------------------- */ - OGRWktReadToken( pszInput, szToken ); - if( EQUAL(szToken,"EMPTY") ) + else if( EQUAL(szToken,"Z") ) { - pszInput = OGRWktReadToken( pszInput, szToken ); - pszInput = OGRWktReadToken( pszInput, szToken ); - - *ppszInput = (char *) pszInput; + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ return OGRERR_NONE; + } + } + + if( !EQUAL(szToken,"(") ) + return OGRERR_CORRUPT_DATA; + + if ( !bHasZ && !bHasM ) + { + /* Test for old-style MULTIPOLYGON(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + + if( EQUAL(szToken,",") ) + { + /* This is OK according to SFSQL SPEC. */ + } + else if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszPreScan; + empty(); + return OGRERR_NONE; + } + } } + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + /* ==================================================================== */ /* Read each polygon in turn. Note that we try to reuse the same */ /* point list buffer from ring to ring to cut down on */ @@ -175,7 +227,19 @@ OGRErr OGRMultiPolygon::importFromWkt( char ** ppszInput ) /* list of polygons. */ /* -------------------------------------------------------------------- */ pszInput = OGRWktReadToken( pszInput, szToken ); - if( szToken[0] != '(' ) + if( EQUAL(szToken, "EMPTY") ) + { + eErr = addGeometryDirectly( poPolygon ); + if( eErr != OGRERR_NONE ) + return eErr; + + pszInput = OGRWktReadToken( pszInput, szToken ); + if ( !EQUAL(szToken, ",") ) + break; + + continue; + } + else if( szToken[0] != '(' ) { eErr = OGRERR_CORRUPT_DATA; delete poPolygon; @@ -189,13 +253,25 @@ OGRErr OGRMultiPolygon::importFromWkt( char ** ppszInput ) { int nPoints = 0; + const char* pszNext = OGRWktReadToken( pszInput, szToken ); + if (EQUAL(szToken,"EMPTY")) + { + poPolygon->addRingDirectly( new OGRLinearRing() ); + + pszInput = OGRWktReadToken( pszNext, szToken ); + if ( !EQUAL(szToken, ",") ) + break; + + continue; + } + /* -------------------------------------------------------------------- */ /* Read points for one line from input. */ /* -------------------------------------------------------------------- */ pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoints, &nPoints ); - if( pszInput == NULL ) + if( pszInput == NULL || nPoints == 0 ) { eErr = OGRERR_CORRUPT_DATA; break; @@ -207,7 +283,11 @@ OGRErr OGRMultiPolygon::importFromWkt( char ** ppszInput ) OGRLinearRing *poLine; poLine = new OGRLinearRing(); - poLine->setPoints( nPoints, paoPoints, padfZ ); + /* Ignore Z array when we have a MULTIPOLYGON M */ + if (bHasM && !bHasZ) + poLine->setPoints( nPoints, paoPoints, NULL ); + else + poLine->setPoints( nPoints, paoPoints, padfZ ); poPolygon->addRingDirectly( poLine ); diff --git a/ogr/ogrpoint.cpp b/ogr/ogrpoint.cpp index 763f50e..49a11c5 100644 --- a/ogr/ogrpoint.cpp +++ b/ogr/ogrpoint.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrpoint.cpp 16853 2009-04-26 12:22:10Z rouault $ + * $Id: ogrpoint.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The Point geometry class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,7 +31,10 @@ #include "ogr_geometry.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrpoint.cpp 16853 2009-04-26 12:22:10Z rouault $"); +/* for std::numeric_limits */ +#include + +CPL_CVSID("$Id: ogrpoint.cpp 27044 2014-03-16 23:41:27Z rouault $"); /************************************************************************/ /* OGRPoint() */ @@ -129,10 +133,10 @@ int OGRPoint::getDimension() const OGRwkbGeometryType OGRPoint::getGeometryType() const { - if( nCoordDimension < 3 ) - return wkbPoint; - else + if( nCoordDimension == 3 ) return wkbPoint25D; + else + return wkbPoint; } /************************************************************************/ @@ -214,27 +218,18 @@ OGRErr OGRPoint::importFromWkb( unsigned char * pabyData, /* geometry type is between 0 and 255 so we only have to fetch */ /* one byte. */ /* -------------------------------------------------------------------- */ + OGRBoolean bIs3D; OGRwkbGeometryType eGeometryType; - int bIs3D; - - if( eByteOrder == wkbNDR ) - { - eGeometryType = (OGRwkbGeometryType) pabyData[1]; - bIs3D = pabyData[4] & 0x80 || pabyData[2] & 0x80; - } - else - { - eGeometryType = (OGRwkbGeometryType) pabyData[4]; - bIs3D = pabyData[1] & 0x80 || pabyData[3] & 0x80; - } + OGRErr err = OGRReadWKBGeometryType( pabyData, &eGeometryType, &bIs3D ); - if( eGeometryType != wkbPoint ) + if( err != OGRERR_NONE || eGeometryType != wkbPoint ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ /* Get the vertex. */ /* -------------------------------------------------------------------- */ - memcpy( &x, pabyData + 5, 16 ); + memcpy( &x, pabyData + 5, 8 ); + memcpy( &y, pabyData + 5 + 8, 8 ); if( OGR_SWAP( eByteOrder ) ) { @@ -244,6 +239,9 @@ OGRErr OGRPoint::importFromWkb( unsigned char * pabyData, if( bIs3D ) { + if ( nSize < 29 && nSize != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + memcpy( &z, pabyData + 5 + 16, 8 ); if( OGR_SWAP( eByteOrder ) ) { @@ -267,7 +265,8 @@ OGRErr OGRPoint::importFromWkb( unsigned char * pabyData, /************************************************************************/ OGRErr OGRPoint::exportToWkb( OGRwkbByteOrder eByteOrder, - unsigned char * pabyData ) const + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const { /* -------------------------------------------------------------------- */ @@ -280,6 +279,9 @@ OGRErr OGRPoint::exportToWkb( OGRwkbByteOrder eByteOrder, /* -------------------------------------------------------------------- */ GUInt32 nGType = getGeometryType(); + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); + if( eByteOrder == wkbNDR ) nGType = CPL_LSBWORD32( nGType ); else @@ -290,11 +292,22 @@ OGRErr OGRPoint::exportToWkb( OGRwkbByteOrder eByteOrder, /* -------------------------------------------------------------------- */ /* Copy in the raw data. */ /* -------------------------------------------------------------------- */ - memcpy( pabyData+5, &x, 16 ); - if( nCoordDimension == 3 ) + if ( IsEmpty() && eWkbVariant == wkbVariantIso ) { - memcpy( pabyData + 5 + 16, &z, 8 ); + double dNan = std::numeric_limits::quiet_NaN(); + memcpy( pabyData+5, &dNan, 8 ); + memcpy( pabyData+5+8, &dNan, 8 ); + if( nCoordDimension == 3 ) + memcpy( pabyData+5+16, &dNan, 8 ); + } + else + { + memcpy( pabyData+5, &x, 16 ); + if( nCoordDimension == 3 ) + { + memcpy( pabyData + 5 + 16, &z, 8 ); + } } /* -------------------------------------------------------------------- */ @@ -337,33 +350,69 @@ OGRErr OGRPoint::importFromWkt( char ** ppszInput ) /* Check for EMPTY ... but treat like a point at 0,0. */ /* -------------------------------------------------------------------- */ const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; pszPreScan = OGRWktReadToken( pszInput, szToken ); if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszInput; + *ppszInput = (char *) pszPreScan; empty(); return OGRERR_NONE; } - if( !EQUAL(szToken,"(") ) - return OGRERR_CORRUPT_DATA; +/* -------------------------------------------------------------------- */ +/* Check for Z, M or ZM. Will ignore the Measure */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szToken,"Z") ) + { + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } - pszPreScan = OGRWktReadToken( pszPreScan, szToken ); - if( EQUAL(szToken,"EMPTY") ) + if (bHasZ || bHasM) { - pszInput = OGRWktReadToken( pszPreScan, szToken ); - - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszInput; + *ppszInput = (char *) pszPreScan; empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ return OGRERR_NONE; } } + if( !EQUAL(szToken,"(") ) + return OGRERR_CORRUPT_DATA; + + if ( !bHasZ && !bHasM ) + { + /* Test for old-style POINT(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszInput = OGRWktReadToken( pszPreScan, szToken ); + + if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszInput; + empty(); + return OGRERR_NONE; + } + } + } + /* -------------------------------------------------------------------- */ /* Read the point list which should consist of exactly one point. */ /* -------------------------------------------------------------------- */ @@ -374,7 +423,11 @@ OGRErr OGRPoint::importFromWkt( char ** ppszInput ) pszInput = OGRWktReadPoints( pszInput, &poPoints, &padfZ, &nMaxPoint, &nPoints ); if( pszInput == NULL || nPoints != 1 ) + { + CPLFree( poPoints ); + CPLFree( padfZ ); return OGRERR_CORRUPT_DATA; + } x = poPoints[0].x; y = poPoints[0].y; @@ -383,10 +436,23 @@ OGRErr OGRPoint::importFromWkt( char ** ppszInput ) if( padfZ != NULL ) { - z = padfZ[0]; - nCoordDimension = 3; + /* If there's a 3rd value, and it is not a POINT M, */ + /* then assume it is the Z */ + if ((!(bHasM && !bHasZ))) + { + z = padfZ[0]; + nCoordDimension = 3; + } + else + nCoordDimension = 2; CPLFree( padfZ ); } + else if ( bHasZ ) + { + /* In theory we should have a z coordinate for POINT Z */ + /* oh well, let be tolerant */ + nCoordDimension = 3; + } else nCoordDimension = 2; @@ -408,7 +474,7 @@ OGRErr OGRPoint::exportToWkt( char ** ppszDstText ) const char szTextEquiv[140]; char szCoordinate[80]; - if (nCoordDimension == 0) + if ( IsEmpty() ) *ppszDstText = CPLStrdup( "POINT EMPTY" ); else { @@ -431,6 +497,17 @@ void OGRPoint::getEnvelope( OGREnvelope * psEnvelope ) const psEnvelope->MinY = psEnvelope->MaxY = getY(); } +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRPoint::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + psEnvelope->MinX = psEnvelope->MaxX = getX(); + psEnvelope->MinY = psEnvelope->MaxY = getY(); + psEnvelope->MinZ = psEnvelope->MaxZ = getZ(); +} /** @@ -504,6 +581,9 @@ OGRBoolean OGRPoint::Equals( OGRGeometry * poOther ) const if( poOther->getGeometryType() != getGeometryType() ) return FALSE; + if ( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + // we should eventually test the SRS. if( poOPoint->getX() != getX() @@ -542,3 +622,14 @@ OGRBoolean OGRPoint::IsEmpty( ) const { return nCoordDimension == 0; } + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRPoint::swapXY() +{ + double dfTemp = x; + x = y; + y = dfTemp; +} diff --git a/ogr/ogrpolygon.cpp b/ogr/ogrpolygon.cpp index 85e95cf..3b2f819 100644 --- a/ogr/ogrpolygon.cpp +++ b/ogr/ogrpolygon.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrpolygon.cpp 16898 2009-05-01 12:23:36Z rouault $ + * $Id: ogrpolygon.cpp 27420 2014-05-28 21:53:37Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRPolygon geometry class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -32,7 +33,7 @@ #include "ogr_geos.h" #include "ogr_api.h" -CPL_CVSID("$Id: ogrpolygon.cpp 16898 2009-05-01 12:23:36Z rouault $"); +CPL_CVSID("$Id: ogrpolygon.cpp 27420 2014-05-28 21:53:37Z rouault $"); /************************************************************************/ /* OGRPolygon() */ @@ -106,12 +107,13 @@ void OGRPolygon::empty() OGRwkbGeometryType OGRPolygon::getGeometryType() const { - if( getCoordinateDimension() == 3 ) + if( nCoordDimension == 3 ) return wkbPolygon25D; else return wkbPolygon; } + /************************************************************************/ /* getDimension() */ /************************************************************************/ @@ -181,6 +183,28 @@ const OGRLinearRing *OGRPolygon::getExteriorRing() const return NULL; } +/************************************************************************/ +/* stealExteriorRing() */ +/************************************************************************/ + +/** + * \brief "Steal" reference to external polygon ring. + * + * After the call to that function, only call to stealInteriorRing() or + * destruction of the OGRPolygon is valid. Other operations may crash. + * + * @return pointer to external ring. May be NULL if the OGRPolygon is empty. + */ + +OGRLinearRing *OGRPolygon::stealExteriorRing() +{ + if( nRingCount == 0 ) + return NULL; + OGRLinearRing *poRet = papoRings[0]; + papoRings[0] = NULL; + return poRet; +} + /************************************************************************/ /* getNumInteriorRings() */ /************************************************************************/ @@ -220,7 +244,7 @@ int OGRPolygon::getNumInteriorRings() const * * @param iRing internal ring index from 0 to getNumInternalRings() - 1. * - * @return pointer to external ring. May be NULL if the OGRPolygon is empty. + * @return pointer to interior ring. May be NULL. */ OGRLinearRing *OGRPolygon::getInteriorRing( int iRing ) @@ -241,6 +265,29 @@ const OGRLinearRing *OGRPolygon::getInteriorRing( int iRing ) const return papoRings[iRing+1]; } +/************************************************************************/ +/* stealInteriorRing() */ +/************************************************************************/ + +/** + * \brief "Steal" reference to indicated interior ring. + * + * After the call to that function, only call to stealInteriorRing() or + * destruction of the OGRPolygon is valid. Other operations may crash. + * + * @param iRing internal ring index from 0 to getNumInternalRings() - 1. + * @return pointer to interior ring. May be NULL. + */ + +OGRLinearRing *OGRPolygon::stealInteriorRing(int iRing) +{ + if( iRing < 0 || iRing >= nRingCount-1 ) + return NULL; + OGRLinearRing *poRet = papoRings[iRing+1]; + papoRings[iRing+1] = NULL; + return poRet; +} + /************************************************************************/ /* addRing() */ /************************************************************************/ @@ -337,7 +384,7 @@ OGRErr OGRPolygon::importFromWkb( unsigned char * pabyData, { OGRwkbByteOrder eByteOrder; - int nDataOffset, b3D; + int nDataOffset; if( nSize < 9 && nSize != -1 ) return OGRERR_NOT_ENOUGH_DATA; @@ -354,28 +401,20 @@ OGRErr OGRPolygon::importFromWkb( unsigned char * pabyData, /* geometry type is between 0 and 255 so we only have to fetch */ /* one byte. */ /* -------------------------------------------------------------------- */ -#ifdef DEBUG + + OGRBoolean b3D; OGRwkbGeometryType eGeometryType; - - if( eByteOrder == wkbNDR ) - eGeometryType = (OGRwkbGeometryType) pabyData[1]; - else - eGeometryType = (OGRwkbGeometryType) pabyData[4]; + OGRErr err = OGRReadWKBGeometryType( pabyData, &eGeometryType, &b3D ); - if( eGeometryType != wkbPolygon ) + if( err != OGRERR_NONE || eGeometryType != wkbPolygon ) return OGRERR_CORRUPT_DATA; -#endif - - if( eByteOrder == wkbNDR ) - b3D = pabyData[4] & 0x80 || pabyData[2] & 0x80; - else - b3D = pabyData[1] & 0x80 || pabyData[3] & 0x80; if( b3D ) nCoordDimension = 3; else nCoordDimension = 2; + /* -------------------------------------------------------------------- */ /* Do we already have some rings? */ /* -------------------------------------------------------------------- */ @@ -456,7 +495,8 @@ OGRErr OGRPolygon::importFromWkb( unsigned char * pabyData, /************************************************************************/ OGRErr OGRPolygon::exportToWkb( OGRwkbByteOrder eByteOrder, - unsigned char * pabyData ) const + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const { int nOffset; @@ -471,6 +511,9 @@ OGRErr OGRPolygon::exportToWkb( OGRwkbByteOrder eByteOrder, /* Set the geometry feature type. */ /* -------------------------------------------------------------------- */ GUInt32 nGType = getGeometryType(); + + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); if( eByteOrder == wkbNDR ) nGType = CPL_LSBWORD32( nGType ); @@ -522,19 +565,11 @@ OGRErr OGRPolygon::importFromWkt( char ** ppszInput ) { char szToken[OGR_WKT_TOKEN_MAX]; const char *pszInput = *ppszInput; - int iRing; /* -------------------------------------------------------------------- */ /* Clear existing rings. */ /* -------------------------------------------------------------------- */ - if( nRingCount > 0 ) - { - for( iRing = 0; iRing < nRingCount; iRing++ ) - delete papoRings[iRing]; - - nRingCount = 0; - CPLFree( papoRings ); - } + empty(); /* -------------------------------------------------------------------- */ /* Read and verify the ``POLYGON'' keyword token. */ @@ -545,35 +580,79 @@ OGRErr OGRPolygon::importFromWkt( char ** ppszInput ) return OGRERR_CORRUPT_DATA; /* -------------------------------------------------------------------- */ -/* The next character should be a ( indicating the start of the */ -/* list of rings. We also need to support POLYGON EMPTY and */ -/* POLYGON(EMPTY). */ +/* Check for EMPTY ... */ /* -------------------------------------------------------------------- */ - pszInput = OGRWktReadToken( pszInput, szToken ); + const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; + pszPreScan = OGRWktReadToken( pszInput, szToken ); if( EQUAL(szToken,"EMPTY") ) { - *ppszInput = (char *) pszInput; + *ppszInput = (char *) pszPreScan; + empty(); return OGRERR_NONE; } - if( szToken[0] != '(' ) +/* -------------------------------------------------------------------- */ +/* Check for Z, M or ZM. Will ignore the Measure */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szToken,"Z") ) + { + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } + + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + /* FIXME?: In theory we should store the dimension and M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ + return OGRERR_NONE; + } + } + + if( !EQUAL(szToken,"(") ) return OGRERR_CORRUPT_DATA; - OGRWktReadToken( pszInput, szToken ); - if( EQUAL(szToken,"EMPTY") ) + if ( !bHasZ && !bHasM ) { - pszInput = OGRWktReadToken( pszInput, szToken ); - pszInput = OGRWktReadToken( pszInput, szToken ); - - *ppszInput = (char *) pszInput; + /* Test for old-style POLYGON(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); - if( !EQUAL(szToken,")") ) - return OGRERR_CORRUPT_DATA; - else - return OGRERR_NONE; + if( EQUAL(szToken,",") ) + { + /* This is OK according to SFSQL SPEC. */ + } + else if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszPreScan; + empty(); + return OGRERR_NONE; + } + } } + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + /* ==================================================================== */ /* Read each ring in turn. Note that we try to reuse the same */ /* point list buffer from ring to ring to cut down on */ @@ -589,15 +668,38 @@ OGRErr OGRPolygon::importFromWkt( char ** ppszInput ) { int nPoints = 0; + const char* pszNext = OGRWktReadToken( pszInput, szToken ); + if (EQUAL(szToken,"EMPTY")) + { +/* -------------------------------------------------------------------- */ +/* Do we need to grow the ring array? */ +/* -------------------------------------------------------------------- */ + if( nRingCount == nMaxRings ) + { + nMaxRings = nMaxRings * 2 + 1; + papoRings = (OGRLinearRing **) + CPLRealloc(papoRings, nMaxRings * sizeof(OGRLinearRing*)); + } + papoRings[nRingCount] = new OGRLinearRing(); + nRingCount++; + + pszInput = OGRWktReadToken( pszNext, szToken ); + if ( !EQUAL(szToken, ",") ) + break; + + continue; + } + /* -------------------------------------------------------------------- */ /* Read points for one ring from input. */ /* -------------------------------------------------------------------- */ pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoints, &nPoints ); - if( pszInput == NULL ) + if( pszInput == NULL || nPoints == 0 ) { CPLFree( paoPoints ); + CPLFree( padfZ ); return OGRERR_CORRUPT_DATA; } @@ -615,11 +717,15 @@ OGRErr OGRPolygon::importFromWkt( char ** ppszInput ) /* Create the new ring, and assign to ring list. */ /* -------------------------------------------------------------------- */ papoRings[nRingCount] = new OGRLinearRing(); - papoRings[nRingCount]->setPoints( nPoints, paoPoints, padfZ ); + /* Ignore Z array when we have a POLYGON M */ + if (bHasM && !bHasZ) + papoRings[nRingCount]->setPoints( nPoints, paoPoints, NULL ); + else + papoRings[nRingCount]->setPoints( nPoints, paoPoints, padfZ ); nRingCount++; - if( padfZ ) + if( padfZ && !(bHasM && !bHasZ) ) nCoordDimension = 3; /* -------------------------------------------------------------------- */ @@ -660,8 +766,8 @@ OGRErr OGRPolygon::exportToWkt( char ** ppszDstText ) const /* -------------------------------------------------------------------- */ /* If we have no valid exterior ring, return POLYGON EMPTY. */ /* -------------------------------------------------------------------- */ - if (getExteriorRing() == NULL || - getExteriorRing()->IsEmpty()) + if (getExteriorRing() == NULL || + getExteriorRing()->IsEmpty() ) { *ppszDstText = CPLStrdup("POLYGON EMPTY"); return OGRERR_NONE; @@ -742,166 +848,118 @@ OGRErr OGRPolygon::exportToWkt( char ** ppszDstText ) const } /************************************************************************/ -/* Centroid() */ +/* PointOnSurface() */ /************************************************************************/ -/** - * \brief Compute the polygon centroid. - * - * The centroid location is applied to the passed in OGRPoint object. - * - * @return OGRERR_NONE on success or OGRERR_FAILURE on error. - */ - -int OGRPolygon::Centroid( OGRPoint *poPoint ) const +int OGRPolygon::PointOnSurface( OGRPoint *poPoint ) const { - if( poPoint == NULL ) + if( poPoint == NULL || poPoint->IsEmpty() ) return OGRERR_FAILURE; -#ifndef HAVE_GEOS - // notdef ... not implemented yet. - - return OGRERR_FAILURE; - -#else - - GEOSGeom hThisGeosGeom = NULL; - GEOSGeom hOtherGeosGeom = NULL; - - hThisGeosGeom = exportToGEOS(); - - if( hThisGeosGeom != NULL ) - { - hOtherGeosGeom = GEOSGetCentroid( hThisGeosGeom ); - OGRPoint *poCentroid = (OGRPoint *) - OGRGeometryFactory::createFromGEOS( hOtherGeosGeom ); - - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); - - if( poPoint == NULL - || wkbFlatten(poPoint->getGeometryType()) != wkbPoint ) - return OGRERR_FAILURE; - - poPoint->setX( poCentroid->getX() ); - poPoint->setY( poCentroid->getY() ); - - delete poCentroid; + OGRGeometryH hInsidePoint = OGR_G_PointOnSurface( (OGRGeometryH) this ); + if( hInsidePoint == NULL ) + return OGRERR_FAILURE; - return OGRERR_NONE; - } + OGRPoint *poInsidePoint = (OGRPoint *) hInsidePoint; + if( poInsidePoint->IsEmpty() ) + poPoint->empty(); else { - return OGRERR_FAILURE; + poPoint->setX( poInsidePoint->getX() ); + poPoint->setY( poInsidePoint->getY() ); } -#endif /* HAVE_GEOS */ + return OGRERR_NONE; } + /************************************************************************/ -/* OGR_G_Centroid() */ +/* getEnvelope() */ /************************************************************************/ -int OGR_G_Centroid( OGRGeometryH hPolygon, OGRGeometryH hCentroidPoint ) +void OGRPolygon::getEnvelope( OGREnvelope * psEnvelope ) const { - OGRPolygon *poPoly = ((OGRPolygon *) hPolygon); - OGRPoint *poCentroid = ((OGRPoint *) hCentroidPoint); - - if( poCentroid == NULL ) - return OGRERR_FAILURE; + OGREnvelope oRingEnv; + int bExtentSet = FALSE; - if( wkbFlatten(poCentroid->getGeometryType()) != wkbPoint ) + for( int iRing = 0; iRing < nRingCount; iRing++ ) { - CPLError( CE_Failure, CPLE_AppDefined, - "Passed wrong geometry type as centroid argument." ); - return OGRERR_FAILURE; + if (!papoRings[iRing]->IsEmpty()) + { + if (!bExtentSet) + { + papoRings[iRing]->getEnvelope( psEnvelope ); + bExtentSet = TRUE; + } + else + { + papoRings[iRing]->getEnvelope( &oRingEnv ); + + if( psEnvelope->MinX > oRingEnv.MinX ) + psEnvelope->MinX = oRingEnv.MinX; + if( psEnvelope->MinY > oRingEnv.MinY ) + psEnvelope->MinY = oRingEnv.MinY; + if( psEnvelope->MaxX < oRingEnv.MaxX ) + psEnvelope->MaxX = oRingEnv.MaxX; + if( psEnvelope->MaxY < oRingEnv.MaxY ) + psEnvelope->MaxY = oRingEnv.MaxY; + } + } } - if( wkbFlatten(poPoly->getGeometryType()) != wkbPolygon ) + if (!bExtentSet) { - CPLError( CE_Failure, CPLE_AppDefined, - "Invoked Centroid() on non-Polygon." ); - return OGRERR_FAILURE; + psEnvelope->MinX = psEnvelope->MinY = 0; + psEnvelope->MaxX = psEnvelope->MaxY = 0; } - - return poPoly->Centroid( poCentroid ); } - -/************************************************************************/ -/* PointOnSurface() */ -/************************************************************************/ - -int OGRPolygon::PointOnSurface( OGRPoint *poPoint ) const - -{ - if( poPoint == NULL ) - return OGRERR_FAILURE; - -#ifndef HAVE_GEOS - return OGRERR_FAILURE; -#else - GEOSGeom hThisGeosGeom = NULL; - GEOSGeom hOtherGeosGeom = NULL; - - hThisGeosGeom = exportToGEOS(); - - if( hThisGeosGeom != NULL ) - { - hOtherGeosGeom = GEOSPointOnSurface( hThisGeosGeom ); - OGRPoint *poInsidePoint = (OGRPoint *) - OGRGeometryFactory::createFromGEOS( hOtherGeosGeom ); - - GEOSGeom_destroy( hThisGeosGeom ); - GEOSGeom_destroy( hOtherGeosGeom ); - - if( poPoint == NULL - || wkbFlatten(poPoint->getGeometryType()) != wkbPoint ) - return OGRERR_FAILURE; - - poPoint->setX( poInsidePoint->getX() ); - poPoint->setY( poInsidePoint->getY() ); - - delete poInsidePoint; - - return OGRERR_NONE; - } - else - { - return OGRERR_FAILURE; - } -#endif /* HAVE_GEOS */ -} - - /************************************************************************/ /* getEnvelope() */ /************************************************************************/ -void OGRPolygon::getEnvelope( OGREnvelope * psEnvelope ) const +void OGRPolygon::getEnvelope( OGREnvelope3D * psEnvelope ) const { - OGREnvelope oRingEnv; - - if( nRingCount == 0 ) - return; + OGREnvelope3D oRingEnv; + int bExtentSet = FALSE; - papoRings[0]->getEnvelope( psEnvelope ); + for( int iRing = 0; iRing < nRingCount; iRing++ ) + { + if (!papoRings[iRing]->IsEmpty()) + { + if (!bExtentSet) + { + papoRings[iRing]->getEnvelope( psEnvelope ); + bExtentSet = TRUE; + } + else + { + papoRings[iRing]->getEnvelope( &oRingEnv ); + + if( psEnvelope->MinX > oRingEnv.MinX ) + psEnvelope->MinX = oRingEnv.MinX; + if( psEnvelope->MinY > oRingEnv.MinY ) + psEnvelope->MinY = oRingEnv.MinY; + if( psEnvelope->MaxX < oRingEnv.MaxX ) + psEnvelope->MaxX = oRingEnv.MaxX; + if( psEnvelope->MaxY < oRingEnv.MaxY ) + psEnvelope->MaxY = oRingEnv.MaxY; + + if( psEnvelope->MinZ > oRingEnv.MinZ ) + psEnvelope->MinZ = oRingEnv.MinZ; + if( psEnvelope->MaxZ < oRingEnv.MaxZ ) + psEnvelope->MaxZ = oRingEnv.MaxZ; + } + } + } - for( int iRing = 1; iRing < nRingCount; iRing++ ) + if (!bExtentSet) { - papoRings[iRing]->getEnvelope( &oRingEnv ); - - if( psEnvelope->MinX > oRingEnv.MinX ) - psEnvelope->MinX = oRingEnv.MinX; - if( psEnvelope->MinY > oRingEnv.MinY ) - psEnvelope->MinY = oRingEnv.MinY; - if( psEnvelope->MaxX < oRingEnv.MaxX ) - psEnvelope->MaxX = oRingEnv.MaxX; - if( psEnvelope->MaxY < oRingEnv.MaxY ) - psEnvelope->MaxY = oRingEnv.MaxY; + psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0; + psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0; } } @@ -920,6 +978,9 @@ OGRBoolean OGRPolygon::Equals( OGRGeometry * poOther ) const if( poOther->getGeometryType() != getGeometryType() ) return FALSE; + if ( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + if( getNumInteriorRings() != poOPoly->getNumInteriorRings() ) return FALSE; @@ -1061,7 +1122,7 @@ void OGRPolygon::setCoordinateDimension( int nNewDimension ) OGRBoolean OGRPolygon::IsEmpty( ) const { for( int iRing = 0; iRing < nRingCount; iRing++ ) - if (papoRings[iRing]->IsEmpty() == FALSE) + if ( papoRings[iRing]->IsEmpty() == FALSE ) return FALSE; return TRUE; } @@ -1075,3 +1136,13 @@ void OGRPolygon::segmentize( double dfMaxLength ) for( int iRing = 0; iRing < nRingCount; iRing++ ) papoRings[iRing]->segmentize(dfMaxLength); } + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRPolygon::swapXY() +{ + for( int iRing = 0; iRing < nRingCount; iRing++ ) + papoRings[iRing]->swapXY(); +} diff --git a/ogr/ogrregisterall.cpp b/ogr/ogrregisterall.cpp new file mode 100644 index 0000000..53f490b --- /dev/null +++ b/ogr/ogrregisterall.cpp @@ -0,0 +1,281 @@ +/****************************************************************************** + * $Id: ogrregisterall.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Function to register all known OGR drivers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: ogrregisterall.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRRegisterAll() */ +/************************************************************************/ + +void OGRRegisterAll() +{ + GDALAllRegister(); +} + +void OGRRegisterAllInternal() +{ + +#ifdef SHAPE_ENABLED + RegisterOGRShape(); +#endif +#ifdef TAB_ENABLED + RegisterOGRTAB(); +#endif +#ifdef NTF_ENABLED + RegisterOGRNTF(); +#endif +#ifdef SDTS_ENABLED + RegisterOGRSDTS(); +#endif +#ifdef S57_ENABLED + RegisterOGRS57(); +#endif +#ifdef DGN_ENABLED + RegisterOGRDGN(); +#endif +#ifdef VRT_ENABLED + RegisterOGRVRT(); +#endif +#ifdef REC_ENABLED + RegisterOGRREC(); +#endif +#ifdef MEM_ENABLED + RegisterOGRMEM(); +#endif +#ifdef BNA_ENABLED + RegisterOGRBNA(); +#endif +#ifdef CSV_ENABLED + RegisterOGRCSV(); +#endif +#ifdef NAS_ENABLED + RegisterOGRNAS(); +#endif +#ifdef GML_ENABLED + RegisterOGRGML(); +#endif +#ifdef GPX_ENABLED + RegisterOGRGPX(); +#endif +#ifdef LIBKML_ENABLED + RegisterOGRLIBKML(); +#endif +#ifdef KML_ENABLED + RegisterOGRKML(); +#endif +#ifdef GEOJSON_ENABLED + RegisterOGRGeoJSON(); +#endif +#ifdef ILI_ENABLED + RegisterOGRILI1(); + RegisterOGRILI2(); +#endif +#ifdef GMT_ENABLED + RegisterOGRGMT(); +#endif +#ifdef SQLITE_ENABLED + RegisterOGRGeoPackage(); + RegisterOGRSQLite(); +#endif +#ifdef DODS_ENABLED + RegisterOGRDODS(); +#endif +#ifdef ODBC_ENABLED + RegisterOGRODBC(); +#endif +#ifdef WASP_ENABLED + RegisterOGRWAsP(); +#endif + +/* Register before PGeo and Geomedia drivers */ +/* that don't work well on Linux */ +#ifdef MDB_ENABLED + RegisterOGRMDB(); +#endif + +#ifdef PGEO_ENABLED + RegisterOGRPGeo(); +#endif +#ifdef MSSQLSPATIAL_ENABLED + RegisterOGRMSSQLSpatial(); +#endif +#ifdef OGDI_ENABLED + RegisterOGROGDI(); +#endif +#ifdef PG_ENABLED + RegisterOGRPG(); +#endif +#ifdef MYSQL_ENABLED + RegisterOGRMySQL(); +#endif +#ifdef OCI_ENABLED + RegisterOGROCI(); +#endif +#ifdef INGRES_ENABLED + RegisterOGRIngres(); +#endif +#ifdef SDE_ENABLED + RegisterOGRSDE(); +#endif +/* Register OpenFileGDB before FGDB as it is more capable for read-only */ +#ifdef OPENFILEGDB_ENABLED + RegisterOGROpenFileGDB(); +#endif +#ifdef FGDB_ENABLED + RegisterOGRFileGDB(); +#endif +#ifdef XPLANE_ENABLED + RegisterOGRXPlane(); +#endif +#ifdef DWGDIRECT_ENABLED + RegisterOGRDXFDWG(); +#endif +#ifdef DXF_ENABLED + RegisterOGRDXF(); +#endif +#ifdef GRASS_ENABLED + RegisterOGRGRASS(); +#endif +#ifdef FME_ENABLED + RegisterOGRFME(); +#endif +#ifdef IDB_ENABLED + RegisterOGRIDB(); +#endif +#ifdef GEOCONCEPT_ENABLED + RegisterOGRGeoconcept(); +#endif +#ifdef GEORSS_ENABLED + RegisterOGRGeoRSS(); +#endif +#ifdef GTM_ENABLED + RegisterOGRGTM(); +#endif +#ifdef VFK_ENABLED + RegisterOGRVFK(); +#endif +#ifdef PGDUMP_ENABLED + RegisterOGRPGDump(); +#endif +#ifdef OSM_ENABLED + /* Register before GPSBabel, that could recognize .osm file too */ + RegisterOGROSM(); +#endif +#ifdef GPSBABEL_ENABLED + RegisterOGRGPSBabel(); +#endif +#ifdef SUA_ENABLED + RegisterOGRSUA(); +#endif +#ifdef OPENAIR_ENABLED + RegisterOGROpenAir(); +#endif +#ifdef PDS_ENABLED + RegisterOGRPDS(); +#endif +#ifdef WFS_ENABLED + RegisterOGRWFS(); +#endif +#ifdef SOSI_ENABLED + RegisterOGRSOSI(); +#endif +#ifdef HTF_ENABLED + RegisterOGRHTF(); +#endif +#ifdef AERONAVFAA_ENABLED + RegisterOGRAeronavFAA(); +#endif +#ifdef GEOMEDIA_ENABLED + RegisterOGRGeomedia(); +#endif +#ifdef EDIGEO_ENABLED + RegisterOGREDIGEO(); +#endif +#ifdef GFT_ENABLED + RegisterOGRGFT(); +#endif +#ifdef GME_ENABLED + RegisterOGRGME(); +#endif +#ifdef SVG_ENABLED + RegisterOGRSVG(); +#endif +#ifdef COUCHDB_ENABLED + RegisterOGRCouchDB(); +#endif +#ifdef IDRISI_ENABLED + RegisterOGRIdrisi(); +#endif +#ifdef ARCGEN_ENABLED + RegisterOGRARCGEN(); +#endif +#ifdef SEGUKOOA_ENABLED + RegisterOGRSEGUKOOA(); +#endif +#ifdef SEGY_ENABLED + RegisterOGRSEGY(); +#endif +#ifdef FREEXL_ENABLED + RegisterOGRXLS(); +#endif +#ifdef ODS_ENABLED + RegisterOGRODS(); +#endif +#ifdef XLSX_ENABLED + RegisterOGRXLSX(); +#endif +#ifdef ELASTIC_ENABLED + RegisterOGRElastic(); +#endif +#ifdef WALK_ENABLED + RegisterOGRWalk(); +#endif +#ifdef CARTODB_ENABLED + RegisterOGRCartoDB(); +#endif +#ifdef SXF_ENABLED + RegisterOGRSXF(); +#endif +#ifdef SELAFIN_ENABLED + RegisterOGRSelafin(); +#endif + +/* Put TIGER and AVCBIN at end since they need poOpenInfo->GetSiblingFiles() */ +#ifdef TIGER_ENABLED + RegisterOGRTiger(); +#endif +#ifdef AVCBIN_ENABLED + RegisterOGRAVCBin(); + RegisterOGRAVCE00(); +#endif + +} /* OGRRegisterAll */ diff --git a/ogr/ogrsf_frmts.h b/ogr/ogrsf_frmts.h index e6eb5ea..5023b9f 100644 --- a/ogr/ogrsf_frmts.h +++ b/ogr/ogrsf_frmts.h @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrsf_frmts.h 18449 2010-01-07 09:09:09Z martinl $ + * $Id: ogrsf_frmts.h 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Classes related to format registration, and file opening. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2007-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -30,8 +31,10 @@ #ifndef _OGRSF_FRMTS_H_INCLUDED #define _OGRSF_FRMTS_H_INCLUDED +#include "cpl_progress.h" #include "ogr_feature.h" #include "ogr_featurestyle.h" +#include "gdal_priv.h" /** * \file ogrsf_frmts.h @@ -51,15 +54,24 @@ class OGRSFDriver; * */ -class CPL_DLL OGRLayer +/* Note: any virtual method added to this class must also be added in the */ +/* OGRLayerDecorator and OGRMutexedLayer classes. */ + +class CPL_DLL OGRLayer : public GDALMajorObject { protected: int m_bFilterIsEnvelope; OGRGeometry *m_poFilterGeom; + OGRPreparedGeometry *m_pPreparedFilterGeom; /* m_poFilterGeom compiled as a prepared geometry */ OGREnvelope m_sFilterEnvelope; + int m_iGeomFieldFilter; // specify the index on which the spatial + // filter is active. int FilterGeometry( OGRGeometry * ); + //int FilterGeometry( OGRGeometry *, OGREnvelope* psGeometryEnvelope); int InstallFilter( OGRGeometry * ); + + OGRErr GetExtentInternal(int iGeomField, OGREnvelope *psExtent, int bForce ); public: OGRLayer(); @@ -70,6 +82,11 @@ class CPL_DLL OGRLayer virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, double dfMaxX, double dfMaxY ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + virtual OGRErr SetAttributeFilter( const char * ); virtual void ResetReading() = 0; @@ -80,32 +97,35 @@ class CPL_DLL OGRLayer virtual OGRErr CreateFeature( OGRFeature *poFeature ); virtual OGRErr DeleteFeature( long nFID ); + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); virtual OGRFeatureDefn *GetLayerDefn() = 0; + virtual int FindFieldIndex( const char *pszFieldName, int bExactMatch ); - virtual OGRSpatialReference *GetSpatialRef() { return NULL; } + virtual OGRSpatialReference *GetSpatialRef(); virtual int GetFeatureCount( int bForce = TRUE ); virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, + int bForce = TRUE); virtual int TestCapability( const char * ) = 0; - virtual const char *GetInfo( const char * ); - virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poField, + int bApproxOK = TRUE ); virtual OGRErr SyncToDisk(); - OGRStyleTable *GetStyleTable(){ return m_poStyleTable; } - void SetStyleTableDirectly( OGRStyleTable *poStyleTable ) - { if ( m_poStyleTable ) delete m_poStyleTable; - m_poStyleTable = poStyleTable; } - void SetStyleTable(OGRStyleTable *poStyleTable) - { - if ( m_poStyleTable ) delete m_poStyleTable; - if ( poStyleTable ) - m_poStyleTable = poStyleTable->Clone(); - } + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); virtual OGRErr StartTransaction(); virtual OGRErr CommitTransaction(); @@ -114,12 +134,55 @@ class CPL_DLL OGRLayer virtual const char *GetFIDColumn(); virtual const char *GetGeometryColumn(); + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + OGRErr Intersection( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Union( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr SymDifference( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + OGRErr Identity( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Update( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Clip( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Erase( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + int Reference(); int Dereference(); int GetRefCount() const; GIntBig GetFeaturesRead(); - + + /* non virtual : conveniency wrapper for ReorderFields() */ + OGRErr ReorderField( int iOldFieldPos, int iNewFieldPos ); + + int AttributeFilterEvaluationNeedsGeometry(); + /* consider these private */ OGRErr InitializeIndexSupport( const char * ); OGRLayerAttrIndex *GetIndex() { return m_poAttrIndex; } @@ -127,6 +190,7 @@ class CPL_DLL OGRLayer protected: OGRStyleTable *m_poStyleTable; OGRFeatureQuery *m_poAttrQuery; + char *m_pszAttrQueryString; OGRLayerAttrIndex *m_poAttrIndex; int m_nRefCount; @@ -140,6 +204,8 @@ class CPL_DLL OGRLayer /************************************************************************/ /** + * LEGACY class. Use GDALDataset in your new code ! + * * This class represents a data source. A data source potentially * consists of many layers (OGRLayer). A data source normally consists * of one, or a related set of files, though the name doesn't have to be @@ -147,72 +213,18 @@ class CPL_DLL OGRLayer * * When an OGRDataSource is destroyed, all it's associated OGRLayers objects * are also destroyed. + * + * @deprecated */ -class CPL_DLL OGRDataSource +class CPL_DLL OGRDataSource : public GDALDataset { - friend class OGRSFDriverRegistrar; - - void *m_hMutex; - - public: - - OGRDataSource(); - virtual ~OGRDataSource(); - static void DestroyDataSource( OGRDataSource * ); +public: + OGRDataSource(); virtual const char *GetName() = 0; - virtual int GetLayerCount() = 0; - virtual OGRLayer *GetLayer(int) = 0; - virtual OGRLayer *GetLayerByName(const char *); - virtual OGRErr DeleteLayer(int); - - virtual int TestCapability( const char * ) = 0; - - virtual OGRLayer *CreateLayer( const char *pszName, - OGRSpatialReference *poSpatialRef = NULL, - OGRwkbGeometryType eGType = wkbUnknown, - char ** papszOptions = NULL ); - virtual OGRLayer *CopyLayer( OGRLayer *poSrcLayer, - const char *pszNewName, - char **papszOptions = NULL ); - - OGRStyleTable *GetStyleTable(){ return m_poStyleTable; } - void SetStyleTableDirectly( OGRStyleTable *poStyleTable ) - { if ( m_poStyleTable ) delete m_poStyleTable; - m_poStyleTable = poStyleTable; } - void SetStyleTable(OGRStyleTable *poStyleTable) - { - if ( m_poStyleTable ) delete m_poStyleTable; - if ( poStyleTable ) - m_poStyleTable = poStyleTable->Clone(); - } - - virtual OGRLayer * ExecuteSQL( const char *pszStatement, - OGRGeometry *poSpatialFilter, - const char *pszDialect ); - virtual void ReleaseResultSet( OGRLayer * poResultsSet ); - - virtual OGRErr SyncToDisk(); - - int Reference(); - int Dereference(); - int GetRefCount() const; - int GetSummaryRefCount() const; - OGRErr Release(); - - OGRSFDriver *GetDriver() const; - void SetDriver( OGRSFDriver *poDriver ); - - protected: - - OGRErr ProcessSQLCreateIndex( const char * ); - OGRErr ProcessSQLDropIndex( const char * ); - - OGRStyleTable *m_poStyleTable; - int m_nRefCount; - OGRSFDriver *m_poDriver; + static void DestroyDataSource( OGRDataSource * ); }; /************************************************************************/ @@ -220,15 +232,19 @@ class CPL_DLL OGRDataSource /************************************************************************/ /** + * LEGACY class. Use GDALDriver in your new code ! + * * Represents an operational format driver. * * One OGRSFDriver derived class will normally exist for each file format * registered for use, regardless of whether a file has or will be opened. * The list of available drivers is normally managed by the * OGRSFDriverRegistrar. + * + * @deprecated */ -class CPL_DLL OGRSFDriver +class CPL_DLL OGRSFDriver : public GDALDriver { public: virtual ~OGRSFDriver(); @@ -236,16 +252,12 @@ class CPL_DLL OGRSFDriver virtual const char *GetName() = 0; virtual OGRDataSource *Open( const char *pszName, int bUpdate=FALSE ) = 0; - - virtual int TestCapability( const char * ) = 0; + + virtual int TestCapability( const char *pszCap ) = 0; virtual OGRDataSource *CreateDataSource( const char *pszName, char ** = NULL ); virtual OGRErr DeleteDataSource( const char *pszName ); - - virtual OGRDataSource *CopyDataSource( OGRDataSource *poSrcDS, - const char *pszNewName, - char **papszOptions = NULL ); }; @@ -254,48 +266,43 @@ class CPL_DLL OGRSFDriver /************************************************************************/ /** + * LEGACY class. Use GDALDriverManager in your new code ! + * * Singleton manager for OGRSFDriver instances that will be used to try * and open datasources. Normally the registrar is populated with * standard drivers using the OGRRegisterAll() function and does not need * to be directly accessed. The driver registrar and all registered drivers * may be cleaned up on shutdown using OGRCleanupAll(). + * + * @deprecated */ class CPL_DLL OGRSFDriverRegistrar { - int nDrivers; - OGRSFDriver **papoDrivers; OGRSFDriverRegistrar(); + ~OGRSFDriverRegistrar(); - int nOpenDSCount; - char **papszOpenDSRawName; - OGRDataSource **papoOpenDS; - OGRSFDriver **papoOpenDSDriver; - GIntBig *panOpenDSPID; + static GDALDataset* OpenWithDriverArg(GDALDriver* poDriver, + GDALOpenInfo* poOpenInfo); + static GDALDataset* CreateVectorOnly( GDALDriver* poDriver, + const char * pszName, + char ** papszOptions ); + static CPLErr DeleteDataSource( GDALDriver* poDriver, + const char * pszName ); public: - ~OGRSFDriverRegistrar(); - static OGRSFDriverRegistrar *GetRegistrar(); - static OGRDataSource *Open( const char *pszName, int bUpdate=FALSE, - OGRSFDriver ** ppoDriver = NULL ); - - OGRDataSource *OpenShared( const char *pszName, int bUpdate=FALSE, - OGRSFDriver ** ppoDriver = NULL ); - OGRErr ReleaseDataSource( OGRDataSource * ); void RegisterDriver( OGRSFDriver * poDriver ); int GetDriverCount( void ); - OGRSFDriver *GetDriver( int iDriver ); - OGRSFDriver *GetDriverByName( const char * ); + GDALDriver *GetDriver( int iDriver ); + GDALDriver *GetDriverByName( const char * ); - int GetOpenDSCount() { return nOpenDSCount; } + int GetOpenDSCount(); OGRDataSource *GetOpenDS( int ); - - void AutoLoadDrivers(); }; /* -------------------------------------------------------------------- */ @@ -303,7 +310,9 @@ class CPL_DLL OGRSFDriverRegistrar /* -------------------------------------------------------------------- */ CPL_C_START void CPL_DLL OGRRegisterAll(); +void OGRRegisterAllInternal(); +void CPL_DLL RegisterOGRFileGDB(); void CPL_DLL RegisterOGRShape(); void CPL_DLL RegisterOGRNTF(); void CPL_DLL RegisterOGRFME(); @@ -314,11 +323,14 @@ void CPL_DLL RegisterOGRTAB(); void CPL_DLL RegisterOGRMIF(); void CPL_DLL RegisterOGROGDI(); void CPL_DLL RegisterOGRODBC(); +void CPL_DLL RegisterOGRWAsP(); void CPL_DLL RegisterOGRPG(); +void CPL_DLL RegisterOGRMSSQLSpatial(); void CPL_DLL RegisterOGRMySQL(); void CPL_DLL RegisterOGROCI(); void CPL_DLL RegisterOGRDGN(); void CPL_DLL RegisterOGRGML(); +void CPL_DLL RegisterOGRLIBKML(); void CPL_DLL RegisterOGRKML(); void CPL_DLL RegisterOGRGeoJSON(); void CPL_DLL RegisterOGRAVCBin(); @@ -335,6 +347,7 @@ void CPL_DLL RegisterOGRGRASS(); void CPL_DLL RegisterOGRPGeo(); void CPL_DLL RegisterOGRDXFDWG(); void CPL_DLL RegisterOGRDXF(); +void CPL_DLL RegisterOGRDWG(); void CPL_DLL RegisterOGRSDE(); void CPL_DLL RegisterOGRIDB(); void CPL_DLL RegisterOGRGMT(); @@ -342,13 +355,42 @@ void CPL_DLL RegisterOGRBNA(); void CPL_DLL RegisterOGRGPX(); void CPL_DLL RegisterOGRGeoconcept(); void CPL_DLL RegisterOGRIngres(); -void CPL_DLL RegisterOGRPCIDSK(); void CPL_DLL RegisterOGRXPlane(); void CPL_DLL RegisterOGRNAS(); void CPL_DLL RegisterOGRGeoRSS(); void CPL_DLL RegisterOGRGTM(); void CPL_DLL RegisterOGRVFK(); - +void CPL_DLL RegisterOGRPGDump(); +void CPL_DLL RegisterOGROSM(); +void CPL_DLL RegisterOGRGPSBabel(); +void CPL_DLL RegisterOGRSUA(); +void CPL_DLL RegisterOGROpenAir(); +void CPL_DLL RegisterOGRPDS(); +void CPL_DLL RegisterOGRWFS(); +void CPL_DLL RegisterOGRSOSI(); +void CPL_DLL RegisterOGRHTF(); +void CPL_DLL RegisterOGRAeronavFAA(); +void CPL_DLL RegisterOGRGeomedia(); +void CPL_DLL RegisterOGRMDB(); +void CPL_DLL RegisterOGREDIGEO(); +void CPL_DLL RegisterOGRGFT(); +void CPL_DLL RegisterOGRGME(); +void CPL_DLL RegisterOGRSVG(); +void CPL_DLL RegisterOGRCouchDB(); +void CPL_DLL RegisterOGRIdrisi(); +void CPL_DLL RegisterOGRARCGEN(); +void CPL_DLL RegisterOGRSEGUKOOA(); +void CPL_DLL RegisterOGRSEGY(); +void CPL_DLL RegisterOGRXLS(); +void CPL_DLL RegisterOGRODS(); +void CPL_DLL RegisterOGRXLSX(); +void CPL_DLL RegisterOGRElastic(); +void CPL_DLL RegisterOGRGeoPackage(); +void CPL_DLL RegisterOGRWalk(); +void CPL_DLL RegisterOGRCartoDB(); +void CPL_DLL RegisterOGRSXF(); +void CPL_DLL RegisterOGROpenFileGDB(); +void CPL_DLL RegisterOGRSelafin(); CPL_C_END diff --git a/ogr/ogrsfdriver.cpp b/ogr/ogrsfdriver.cpp index e5feb3f..514e121 100644 --- a/ogr/ogrsfdriver.cpp +++ b/ogr/ogrsfdriver.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrsfdriver.cpp 16319 2009-02-13 23:17:58Z rouault $ + * $Id: ogrsfdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The generic portions of the OGRSFDriver class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2009-2011, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,7 +32,7 @@ #include "ogr_api.h" #include "ogr_p.h" -CPL_CVSID("$Id: ogrsfdriver.cpp 16319 2009-02-13 23:17:58Z rouault $"); +CPL_CVSID("$Id: ogrsfdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); /************************************************************************/ /* ~OGRSFDriver() */ @@ -66,24 +67,9 @@ OGRDataSourceH OGR_Dr_CreateDataSource( OGRSFDriverH hDriver, { VALIDATE_POINTER1( hDriver, "OGR_Dr_CreateDataSource", NULL ); - OGRSFDriver* poDriver = (OGRSFDriver *) hDriver; - CPLAssert( NULL != poDriver ); + GDALDriver* poDriver = (GDALDriver*)hDriver; - OGRDataSource* poDS = NULL; - poDS = poDriver->CreateDataSource( pszName, papszOptions ); - - /* This fix is explained in Ticket #1223 */ - if( NULL != poDS ) - { - poDS->SetDriver( poDriver ); - CPLAssert( NULL != poDS->GetDriver() ); - } - else - { - CPLDebug( "OGR", "CreateDataSource operation failed. NULL pointer returned." ); - } - - return (OGRDataSourceH) poDS; + return (OGRDataSourceH) poDriver->Create( pszName, 0, 0, 0, GDT_Unknown, papszOptions ); } /************************************************************************/ @@ -111,7 +97,7 @@ OGRErr OGR_Dr_DeleteDataSource( OGRSFDriverH hDriver, VALIDATE_POINTER1( hDriver, "OGR_Dr_DeleteDataSource", OGRERR_INVALID_HANDLE ); - return ((OGRSFDriver *) hDriver)->DeleteDataSource( pszDataSource ); + return ((GDALDriver *) hDriver)->Delete( pszDataSource ); } /************************************************************************/ @@ -123,7 +109,7 @@ const char *OGR_Dr_GetName( OGRSFDriverH hDriver ) { VALIDATE_POINTER1( hDriver, "OGR_Dr_GetName", NULL ); - return ((OGRSFDriver *) hDriver)->GetName(); + return ((GDALDriver*)hDriver)->GetDescription(); } /************************************************************************/ @@ -136,12 +122,12 @@ OGRDataSourceH OGR_Dr_Open( OGRSFDriverH hDriver, const char *pszName, { VALIDATE_POINTER1( hDriver, "OGR_Dr_Open", NULL ); - OGRDataSource *poDS = ((OGRSFDriver *)hDriver)->Open( pszName, bUpdate ); - - if( poDS != NULL && poDS->GetDriver() == NULL ) - poDS->SetDriver( (OGRSFDriver *)hDriver ); - - return (OGRDataSourceH) poDS; + const char* const apszDrivers[] = { ((GDALDriver*)hDriver)->GetDescription(), + NULL }; + return (OGRDataSourceH)GDALOpenEx(pszName, + GDAL_OF_VECTOR | + ((bUpdate) ? GDAL_OF_UPDATE: 0), + apszDrivers, NULL, NULL); } /************************************************************************/ @@ -154,29 +140,48 @@ int OGR_Dr_TestCapability( OGRSFDriverH hDriver, const char *pszCap ) VALIDATE_POINTER1( hDriver, "OGR_Dr_TestCapability", 0 ); VALIDATE_POINTER1( pszCap, "OGR_Dr_TestCapability", 0 ); - return ((OGRSFDriver *) hDriver)->TestCapability( pszCap ); + GDALDriver* poDriver = (GDALDriver *) hDriver; + if( EQUAL(pszCap, ODrCCreateDataSource) ) + { + return poDriver->pfnCreate != NULL || + poDriver->pfnCreateVectorOnly != NULL; + } + else if( EQUAL(pszCap, ODrCDeleteDataSource) ) + { + return poDriver->pfnDelete != NULL || + poDriver->pfnDeleteDataSource != NULL; + } + else + return FALSE; } /************************************************************************/ -/* CopyDataSource() */ +/* OGR_Dr_CopyDataSource() */ /************************************************************************/ -OGRDataSource *OGRSFDriver::CopyDataSource( OGRDataSource *poSrcDS, - const char *pszNewName, - char **papszOptions ) - +OGRDataSourceH OGR_Dr_CopyDataSource( OGRSFDriverH hDriver, + OGRDataSourceH hSrcDS, + const char *pszNewName, + char **papszOptions ) + { - if( !TestCapability( ODrCCreateDataSource ) ) + VALIDATE_POINTER1( hDriver, "OGR_Dr_CopyDataSource", NULL ); + VALIDATE_POINTER1( hSrcDS, "OGR_Dr_CopyDataSource", NULL ); + VALIDATE_POINTER1( pszNewName, "OGR_Dr_CopyDataSource", NULL ); + + GDALDriver* poDriver = (GDALDriver*)hDriver; + if( !poDriver->GetMetadataItem( GDAL_DCAP_CREATE ) ) { CPLError( CE_Failure, CPLE_NotSupported, "%s driver does not support data source creation.", - GetName() ); + poDriver->GetDescription() ); return NULL; } - OGRDataSource *poODS; + GDALDataset *poSrcDS = (GDALDataset*) hSrcDS; + GDALDataset *poODS; - poODS = CreateDataSource( pszNewName, papszOptions ); + poODS = poDriver->Create( pszNewName, 0, 0, 0, GDT_Unknown, papszOptions ); if( poODS == NULL ) return NULL; @@ -193,25 +198,7 @@ OGRDataSource *OGRSFDriver::CopyDataSource( OGRDataSource *poSrcDS, poODS->CopyLayer( poLayer, poLayer->GetLayerDefn()->GetName(), papszOptions ); } - - return poODS; -} - -/************************************************************************/ -/* OGR_Dr_CopyDataSource() */ -/************************************************************************/ - -OGRDataSourceH OGR_Dr_CopyDataSource( OGRSFDriverH hDriver, - OGRDataSourceH hSrcDS, - const char *pszNewName, - char **papszOptions ) - -{ - VALIDATE_POINTER1( hDriver, "OGR_Dr_CopyDataSource", NULL ); - VALIDATE_POINTER1( hSrcDS, "OGR_Dr_CopyDataSource", NULL ); - return (OGRDataSourceH) - ((OGRSFDriver *) hDriver)->CopyDataSource( - (OGRDataSource *) hSrcDS, pszNewName, papszOptions ); + return (OGRDataSourceH)poODS; } diff --git a/ogr/ogrsfdriverregistrar.cpp b/ogr/ogrsfdriverregistrar.cpp index cfe17d1..d5612db 100644 --- a/ogr/ogrsfdriverregistrar.cpp +++ b/ogr/ogrsfdriverregistrar.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrsfdriverregistrar.cpp 18695 2010-02-01 17:23:44Z hobu $ + * $Id: ogrsfdriverregistrar.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRSFDriverRegistrar class implementation. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2012, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,16 +30,9 @@ #include "ogrsf_frmts.h" #include "ogr_api.h" -#include "ogr_p.h" -#include "cpl_multiproc.h" -CPL_CVSID("$Id: ogrsfdriverregistrar.cpp 18695 2010-02-01 17:23:44Z hobu $"); +CPL_CVSID("$Id: ogrsfdriverregistrar.cpp 27384 2014-05-24 12:28:12Z rouault $"); -static void *hDRMutex = NULL; -static OGRSFDriverRegistrar * volatile poRegistrar = NULL; - -static const char *pszUpdatableINST_DATA = -"__INST_DATA_TARGET: "; /************************************************************************/ /* OGRSFDriverRegistrar */ /************************************************************************/ @@ -54,42 +48,6 @@ static const char *pszUpdatableINST_DATA = OGRSFDriverRegistrar::OGRSFDriverRegistrar() { - CPLAssert( poRegistrar == NULL ); - nDrivers = 0; - papoDrivers = NULL; - - nOpenDSCount = 0; - papszOpenDSRawName = NULL; - papoOpenDS = NULL; - papoOpenDSDriver = NULL; - panOpenDSPID = NULL; - -/* -------------------------------------------------------------------- */ -/* We want to push a location to search for data files */ -/* supporting GDAL/OGR such as EPSG csv files, S-57 definition */ -/* files, and so forth. The static pszUpdateableINST_DATA */ -/* string can be updated within the shared library or */ -/* executable during an install to point installed data */ -/* directory. If it isn't burned in here then we use the */ -/* INST_DATA macro (setup at configure time) if */ -/* available. Otherwise we don't push anything and we hope */ -/* other mechanisms such as environment variables will have */ -/* been employed. */ -/* -------------------------------------------------------------------- */ - if( CPLGetConfigOption( "GDAL_DATA", NULL ) != NULL ) - { - CPLPushFinderLocation( CPLGetConfigOption( "GDAL_DATA", NULL ) ); - } - else if( pszUpdatableINST_DATA[19] != ' ' ) - { - CPLPushFinderLocation( pszUpdatableINST_DATA + 19 ); - } - else - { -#ifdef INST_DATA - CPLPushFinderLocation( INST_DATA ); -#endif - } } /************************************************************************/ @@ -99,140 +57,32 @@ OGRSFDriverRegistrar::OGRSFDriverRegistrar() OGRSFDriverRegistrar::~OGRSFDriverRegistrar() { - for( int i = 0; i < nDrivers; i++ ) - { - delete papoDrivers[i]; - } - - CPLFree( papoDrivers ); - papoDrivers = NULL; - - poRegistrar = NULL; } /************************************************************************/ -/* OGRCleanupAll() */ +/* GetRegistrar() */ /************************************************************************/ -/** - * \brief Cleanup all OGR related resources. - * - * This function will destroy the OGRSFDriverRegistrar along with all registered - * drivers, and then cleanup long lived OSR (OGRSpatialReference) and CPL - * resources. This may be called in an application when OGR services are - * no longer needed. It is not normally required, but by freeing all - * dynamically allocated memory it can make memory leak testing easier. - * - * In addition to destroying the OGRDriverRegistrar, this function also calls: - * - OSRCleanup() - * - CPLFinderClean() - * - VSICleanupFileManager() - * - CPLFreeConfig() - * - CPLCleanupTLS() - */ -void OGRCleanupAll() +OGRSFDriverRegistrar *OGRSFDriverRegistrar::GetRegistrar() { - { - // We don't want to hold the mutex while CPL level mutex services - // are being destroyed ... just long enough to avoid conflict while - // cleaning up OGR and OSR services. - CPLMutexHolderD( &hDRMutex ); - - if( poRegistrar != NULL ) - delete poRegistrar; - OSRCleanup(); - } - - CPLFinderClean(); - VSICleanupFileManager(); - CPLFreeConfig(); - CPLCleanupTLS(); + static OGRSFDriverRegistrar oSingleton; + return &oSingleton; } - /************************************************************************/ -/* GetRegistrar() */ +/* OGRCleanupAll() */ /************************************************************************/ /** - * \brief Fetch registrar. - * - * This static method should be used to fetch the singleton - * registrar. It will create a registrar if there is not already - * one in existance. + * \brief Cleanup all OGR related resources. * - * @return the current driver registrar. + * FIXME */ - -OGRSFDriverRegistrar *OGRSFDriverRegistrar::GetRegistrar() - -{ - if( poRegistrar == NULL ) - { - CPLMutexHolderD( &hDRMutex ); - - if( poRegistrar == NULL ) - poRegistrar = new OGRSFDriverRegistrar(); - } - - CPLAssert( NULL != poRegistrar ); - return poRegistrar; -} - -/************************************************************************/ -/* Open() */ -/************************************************************************/ - -OGRDataSource *OGRSFDriverRegistrar::Open( const char * pszName, - int bUpdate, - OGRSFDriver ** ppoDriver ) +void OGRCleanupAll() { - OGRDataSource *poDS; - - if( ppoDriver != NULL ) - *ppoDriver = NULL; - - GetRegistrar(); - - CPLErrorReset(); - - CPLAcquireMutex( hDRMutex, 0.1 ); - - for( int iDriver = 0; iDriver < poRegistrar->nDrivers; iDriver++ ) - { - OGRSFDriver *poDriver = poRegistrar->papoDrivers[iDriver]; - - CPLReleaseMutex( hDRMutex ); - - poDS = poDriver->Open( pszName, bUpdate ); - if( poDS != NULL ) - { - if( ppoDriver != NULL ) - *ppoDriver = poDriver; - - poDS->Reference(); - if( poDS->GetDriver() == NULL ) - poDS->m_poDriver = poDriver; - - CPLDebug( "OGR", "OGROpen(%s/%p) succeeded as %s.", - pszName, poDS, poDS->GetDriver()->GetName() ); - - return poDS; - } - - if( CPLGetLastErrorType() == CE_Failure ) - return NULL; - - CPLAcquireMutex( hDRMutex, 0.1 ); - } - - CPLReleaseMutex( hDRMutex ); - - CPLDebug( "OGR", "OGROpen(%s) failed.", pszName ); - - return NULL; + GDALDestroyDriverManager(); } /************************************************************************/ @@ -244,121 +94,12 @@ OGRDataSourceH OGROpen( const char *pszName, int bUpdate, { VALIDATE_POINTER1( pszName, "OGROpen", NULL ); - - if (poRegistrar) - return (OGRDataSourceH) - poRegistrar->Open( pszName, bUpdate, - (OGRSFDriver **) pahDriverList ); - - return NULL; -} - -/************************************************************************/ -/* OpenShared() */ -/************************************************************************/ - -OGRDataSource * -OGRSFDriverRegistrar::OpenShared( const char * pszName, int bUpdate, - OGRSFDriver ** ppoDriver ) - -{ - OGRDataSource *poDS; - - if( ppoDriver != NULL ) - *ppoDriver = NULL; - - CPLErrorReset(); - -/* -------------------------------------------------------------------- */ -/* First try finding an existing open dataset matching exactly */ -/* on the original datasource raw name used to open the */ -/* datasource. */ -/* */ -/* NOTE: It is an error, but currently we ignore the bUpdate, */ -/* and return whatever is open even if it is read-only and the */ -/* application requested update access. */ -/* -------------------------------------------------------------------- */ - { - int iDS; - CPLMutexHolderD( &hDRMutex ); - GIntBig nThisPID = CPLGetPID(); - - for( iDS = 0; iDS < nOpenDSCount; iDS++ ) - { - poDS = papoOpenDS[iDS]; - - if( strcmp( pszName, papszOpenDSRawName[iDS]) == 0 - && nThisPID == panOpenDSPID[iDS] ) - { - poDS->Reference(); - - if( ppoDriver != NULL ) - *ppoDriver = papoOpenDSDriver[iDS]; - return poDS; - } - } - -/* -------------------------------------------------------------------- */ -/* If that doesn't match, try matching on the name returned by */ -/* the datasource itself. */ -/* -------------------------------------------------------------------- */ - for( iDS = 0; iDS < nOpenDSCount; iDS++ ) - { - poDS = papoOpenDS[iDS]; - - if( strcmp( pszName, poDS->GetName()) == 0 - && nThisPID == panOpenDSPID[iDS] ) - { - poDS->Reference(); - - if( ppoDriver != NULL ) - *ppoDriver = papoOpenDSDriver[iDS]; - return poDS; - } - } - } - -/* -------------------------------------------------------------------- */ -/* We don't have the datasource. Open it normally. */ -/* -------------------------------------------------------------------- */ - OGRSFDriver *poTempDriver = NULL; - - poDS = Open( pszName, bUpdate, &poTempDriver ); - - if( poDS == NULL ) - return poDS; - -/* -------------------------------------------------------------------- */ -/* We don't have this datasource already. Grow our list to */ -/* hold the new datasource. */ -/* -------------------------------------------------------------------- */ - { - CPLMutexHolderD( &hDRMutex ); - - papszOpenDSRawName = (char **) - CPLRealloc( papszOpenDSRawName, sizeof(char*) * (nOpenDSCount+1) ); - papoOpenDS = (OGRDataSource **) - CPLRealloc( papoOpenDS, sizeof(char*) * (nOpenDSCount+1) ); - - papoOpenDSDriver = (OGRSFDriver **) - CPLRealloc( papoOpenDSDriver, sizeof(char*) * (nOpenDSCount+1) ); - - panOpenDSPID = (GIntBig *) - CPLRealloc( panOpenDSPID, sizeof(GIntBig) * (nOpenDSCount+1) ); - - papszOpenDSRawName[nOpenDSCount] = CPLStrdup( pszName ); - papoOpenDS[nOpenDSCount] = poDS; - papoOpenDSDriver[nOpenDSCount] = poTempDriver; - panOpenDSPID[nOpenDSCount] = CPLGetPID(); - - nOpenDSCount++; - } - - if( ppoDriver != NULL ) - *ppoDriver = poTempDriver; - - return poDS; + GDALDatasetH hDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | + ((bUpdate) ? GDAL_OF_UPDATE: 0), NULL, NULL, NULL); + if( hDS != NULL && pahDriverList != NULL ) + *pahDriverList = (OGRSFDriverH) GDALGetDatasetDriver(hDS); + return (OGRDataSourceH) hDS; } /************************************************************************/ @@ -371,119 +112,37 @@ OGRDataSourceH OGROpenShared( const char *pszName, int bUpdate, { VALIDATE_POINTER1( pszName, "OGROpenShared", NULL ); - OGRSFDriverRegistrar::GetRegistrar(); - return (OGRDataSourceH) - poRegistrar->OpenShared( pszName, bUpdate, - (OGRSFDriver **) pahDriverList ); + GDALDatasetH hDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | + ((bUpdate) ? GDAL_OF_UPDATE: 0) | GDAL_OF_SHARED, NULL, NULL, NULL); + if( hDS != NULL && pahDriverList != NULL ) + *pahDriverList = (OGRSFDriverH) GDALGetDatasetDriver(hDS); + return (OGRDataSourceH) hDS; } /************************************************************************/ -/* ReleaseDataSource() */ +/* OGRReleaseDataSource() */ /************************************************************************/ -OGRErr OGRSFDriverRegistrar::ReleaseDataSource( OGRDataSource * poDS ) +OGRErr OGRReleaseDataSource( OGRDataSourceH hDS ) { - { - CPLMutexHolderD( &hDRMutex ); - - int iDS; - - for( iDS = 0; iDS < nOpenDSCount; iDS++ ) - { - if( poDS == papoOpenDS[iDS] ) - break; - } - - if( iDS == nOpenDSCount ) - { - CPLDebug( "OGR", - "ReleaseDataSource(%s/%p) on unshared datasource!\n" - "Deleting directly.", - poDS->GetName(), poDS ); - delete poDS; - return OGRERR_FAILURE; - } - - if( poDS->GetRefCount() > 0 ) - poDS->Dereference(); - - if( poDS->GetRefCount() > 0 ) - { - CPLDebug( "OGR", - "ReleaseDataSource(%s/%p) ... just dereferencing.", - poDS->GetName(), poDS ); - return OGRERR_NONE; - } - - if( poDS->GetSummaryRefCount() > 0 ) - { - CPLDebug( "OGR", - "OGRSFDriverRegistrar::ReleaseDataSource(%s)\n" - "Datasource reference count is now zero, but some layers\n" - "are still referenced ... not closing datasource.", - poDS->GetName() ); - return OGRERR_FAILURE; - } - -/* -------------------------------------------------------------------- */ -/* We really want to close this file, and remove it from the */ -/* shared list. */ -/* -------------------------------------------------------------------- */ - CPLDebug( "OGR", - "ReleaseDataSource(%s/%p) dereferenced and now destroying.", - poDS->GetName(), poDS ); - - CPLFree( papszOpenDSRawName[iDS] ); - memmove( papszOpenDSRawName + iDS, papszOpenDSRawName + iDS + 1, - sizeof(char *) * (nOpenDSCount - iDS - 1) ); - memmove( papoOpenDS + iDS, papoOpenDS + iDS + 1, - sizeof(char *) * (nOpenDSCount - iDS - 1) ); - memmove( papoOpenDSDriver + iDS, papoOpenDSDriver + iDS + 1, - sizeof(char *) * (nOpenDSCount - iDS - 1) ); - memmove( panOpenDSPID + iDS, panOpenDSPID + iDS + 1, - sizeof(GIntBig) * (nOpenDSCount - iDS - 1) ); - - nOpenDSCount--; - - if( nOpenDSCount == 0 ) - { - CPLFree( papszOpenDSRawName ); - papszOpenDSRawName = NULL; - CPLFree( papoOpenDS ); - papoOpenDS = NULL; - CPLFree( papoOpenDSDriver ); - papoOpenDSDriver = NULL; - CPLFree( panOpenDSPID ); - panOpenDSPID = NULL; - } - } - -/* -------------------------------------------------------------------- */ -/* We are careful to only do the delete poDS after adjusting */ -/* the table, as if it is a virtual dataset, other removals may */ -/* happen in the meantime. We are also careful to do this */ -/* outside the mutex protected loop as destroying a dataset can */ -/* take quite a while. */ -/* -------------------------------------------------------------------- */ - delete poDS; + VALIDATE_POINTER1( hDS, "OGRReleaseDataSource", OGRERR_INVALID_HANDLE ); + GDALClose( (GDALDatasetH) hDS ); return OGRERR_NONE; } /************************************************************************/ -/* OGRReleaseDataSource() */ +/* GetOpenDSCount() */ /************************************************************************/ -OGRErr OGRReleaseDataSource( OGRDataSourceH hDS ) - +int OGRSFDriverRegistrar::GetOpenDSCount() { - VALIDATE_POINTER1( hDS, "OGRReleaseDataSource", OGRERR_INVALID_HANDLE ); - - OGRSFDriverRegistrar::GetRegistrar(); - return poRegistrar->ReleaseDataSource((OGRDataSource *) hDS); + CPLError(CE_Failure, CPLE_AppDefined, "Stub implementation in GDAL 2.0"); + return 0; } + /************************************************************************/ /* OGRGetOpenDSCount() */ /************************************************************************/ @@ -491,8 +150,7 @@ OGRErr OGRReleaseDataSource( OGRDataSourceH hDS ) int OGRGetOpenDSCount() { - OGRSFDriverRegistrar::GetRegistrar(); - return poRegistrar->GetOpenDSCount(); + return OGRSFDriverRegistrar::GetRegistrar()->GetOpenDSCount(); } /************************************************************************/ @@ -502,12 +160,8 @@ int OGRGetOpenDSCount() OGRDataSource *OGRSFDriverRegistrar::GetOpenDS( int iDS ) { - CPLMutexHolderD( &hDRMutex ); - - if( iDS < 0 || iDS >= nOpenDSCount ) - return NULL; - else - return papoOpenDS[iDS]; + CPLError(CE_Failure, CPLE_AppDefined, "Stub implementation in GDAL 2.0"); + return NULL; } /************************************************************************/ @@ -517,8 +171,50 @@ OGRDataSource *OGRSFDriverRegistrar::GetOpenDS( int iDS ) OGRDataSourceH OGRGetOpenDS( int iDS ) { - OGRSFDriverRegistrar::GetRegistrar(); - return (OGRDataSourceH) poRegistrar->GetOpenDS( iDS ); + return (OGRDataSourceH) OGRSFDriverRegistrar::GetRegistrar()->GetOpenDS( iDS ); +} + +/************************************************************************/ +/* OpenWithDriverArg() */ +/************************************************************************/ + +GDALDataset* OGRSFDriverRegistrar::OpenWithDriverArg(GDALDriver* poDriver, + GDALOpenInfo* poOpenInfo) +{ + OGRDataSource* poDS = (OGRDataSource*) + ((OGRSFDriver*)poDriver)->Open(poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update); + if( poDS != NULL ) + poDS->SetDescription( poDS->GetName() ); + return poDS; +} + +/************************************************************************/ +/* CreateVectorOnly() */ +/************************************************************************/ + +GDALDataset* OGRSFDriverRegistrar::CreateVectorOnly( GDALDriver* poDriver, + const char * pszName, + char ** papszOptions ) +{ + OGRDataSource* poDS = (OGRDataSource*) + ((OGRSFDriver*)poDriver)->CreateDataSource(pszName, papszOptions); + if( poDS != NULL && poDS->GetName() != NULL ) + poDS->SetDescription( poDS->GetName() ); + return poDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +CPLErr OGRSFDriverRegistrar::DeleteDataSource( GDALDriver* poDriver, + const char * pszName ) +{ + if( ((OGRSFDriver*)poDriver)->DeleteDataSource(pszName) == OGRERR_NONE ) + return CE_None; + else + return CE_Failure; } /************************************************************************/ @@ -528,51 +224,40 @@ OGRDataSourceH OGRGetOpenDS( int iDS ) void OGRSFDriverRegistrar::RegisterDriver( OGRSFDriver * poDriver ) { - CPLMutexHolderD( &hDRMutex ); - int iDriver; - -/* -------------------------------------------------------------------- */ -/* It has no effect to register a driver more than once. */ -/* -------------------------------------------------------------------- */ - for( iDriver = 0; iDriver < nDrivers; iDriver++ ) + GDALDriver* poGDALDriver = (GDALDriver*) GDALGetDriverByName( poDriver->GetName() ) ; + if( poGDALDriver == NULL) { - if( poDriver == papoDrivers[iDriver] ) - return; + poDriver->SetDescription( poDriver->GetName() ); + poDriver->SetMetadataItem("OGR_DRIVER", "YES"); + + if( poDriver->GetMetadataItem(GDAL_DMD_LONGNAME) == NULL ) + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, poDriver->GetName() ); - /* Same name but different pointer. Likely a second call to OGRRegisterAll() */ - /* We delete the new driver */ - if (EQUAL(poDriver->GetName(), papoDrivers[iDriver]->GetName())) + poDriver->pfnOpenWithDriverArg = OpenWithDriverArg; + + if( poDriver->TestCapability(ODrCCreateDataSource) ) { - delete poDriver; - return; + poDriver->SetMetadataItem( GDAL_DCAP_CREATE, "YES" ); + poDriver->pfnCreateVectorOnly = CreateVectorOnly; } - } + if( poDriver->TestCapability(ODrCDeleteDataSource) ) + { + poDriver->pfnDeleteDataSource = DeleteDataSource; + } + + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); -/* -------------------------------------------------------------------- */ -/* Skip and destroy drivers in the black list. */ -/* -------------------------------------------------------------------- */ - char** papszSkipDrivers = - CSLTokenizeStringComplex(CPLGetConfigOption("OGR_SKIP", ""), ",", FALSE, FALSE); - char** iter = papszSkipDrivers; - while(*iter) + GetGDALDriverManager()->RegisterDriver( poDriver ); + } + else { - if (strcmp(*iter, poDriver->GetName()) == 0) + if( poGDALDriver->GetMetadataItem("OGR_DRIVER") == NULL) { - CSLDestroy(papszSkipDrivers); - delete poDriver; - return; + CPLError(CE_Failure, CPLE_AppDefined, + "A non OGR driver is registered with the same name: %s", poDriver->GetName()); } - iter ++; + delete poDriver; } - CSLDestroy(papszSkipDrivers); - -/* -------------------------------------------------------------------- */ -/* Add to the end of the driver list. */ -/* -------------------------------------------------------------------- */ - papoDrivers = (OGRSFDriver **) - CPLRealloc( papoDrivers, (nDrivers+1) * sizeof(void*) ); - - papoDrivers[nDrivers++] = poDriver; } /************************************************************************/ @@ -583,9 +268,20 @@ void OGRRegisterDriver( OGRSFDriverH hDriver ) { VALIDATE_POINTER0( hDriver, "OGRRegisterDriver" ); + + GetGDALDriverManager()->RegisterDriver( (GDALDriver*)hDriver ); +} + +/************************************************************************/ +/* OGRDeregisterDriver() */ +/************************************************************************/ + +void OGRDeregisterDriver( OGRSFDriverH hDriver ) - OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( - (OGRSFDriver *) hDriver ); +{ + VALIDATE_POINTER0( hDriver, "OGRDeregisterDriver" ); + + GetGDALDriverManager()->DeregisterDriver( (GDALDriver*)hDriver ); } /************************************************************************/ @@ -595,7 +291,17 @@ void OGRRegisterDriver( OGRSFDriverH hDriver ) int OGRSFDriverRegistrar::GetDriverCount() { - return nDrivers; + /* We must be careful only to return drivers that are actual OGRSFDriver* */ + GDALDriverManager* poDriverManager = GetGDALDriverManager(); + int nTotal = poDriverManager->GetDriverCount(); + int nOGRDriverCount = 0; + for(int i=0;iGetDriver(i); + if( poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != NULL ) + nOGRDriverCount ++; + } + return nOGRDriverCount; } /************************************************************************/ @@ -605,25 +311,31 @@ int OGRSFDriverRegistrar::GetDriverCount() int OGRGetDriverCount() { - if (poRegistrar) - return poRegistrar->GetDriverCount(); - - return 0; + return OGRSFDriverRegistrar::GetRegistrar()->GetDriverCount(); } /************************************************************************/ /* GetDriver() */ /************************************************************************/ -OGRSFDriver *OGRSFDriverRegistrar::GetDriver( int iDriver ) +GDALDriver *OGRSFDriverRegistrar::GetDriver( int iDriver ) { - CPLMutexHolderD( &hDRMutex ); - - if( iDriver < 0 || iDriver >= nDrivers ) - return NULL; - else - return papoDrivers[iDriver]; + /* We must be careful only to return drivers that are actual OGRSFDriver* */ + GDALDriverManager* poDriverManager = GetGDALDriverManager(); + int nTotal = poDriverManager->GetDriverCount(); + int nOGRDriverCount = 0; + for(int i=0;iGetDriver(i); + if( poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != NULL ) + { + if( nOGRDriverCount == iDriver ) + return poDriver; + nOGRDriverCount ++; + } + } + return NULL; } /************************************************************************/ @@ -633,28 +345,25 @@ OGRSFDriver *OGRSFDriverRegistrar::GetDriver( int iDriver ) OGRSFDriverH OGRGetDriver( int iDriver ) { - VALIDATE_POINTER1( poRegistrar, "OGRGetDriver", NULL ); - - return (OGRSFDriverH) poRegistrar->GetDriver( iDriver ); + return (OGRSFDriverH) OGRSFDriverRegistrar::GetRegistrar()->GetDriver( iDriver ); } /************************************************************************/ /* GetDriverByName() */ /************************************************************************/ -OGRSFDriver *OGRSFDriverRegistrar::GetDriverByName( const char * pszName ) +GDALDriver *OGRSFDriverRegistrar::GetDriverByName( const char * pszName ) { - CPLMutexHolderD( &hDRMutex ); - - for( int i = 0; i < nDrivers; i++ ) - { - if( papoDrivers[i] != NULL - && EQUAL(papoDrivers[i]->GetName(),pszName) ) - return papoDrivers[i]; - } - - return NULL; + GDALDriverManager* poDriverManager = GetGDALDriverManager(); + GDALDriver* poGDALDriver = + poDriverManager->GetDriverByName(CPLSPrintf("OGR_%s", pszName)); + if( poGDALDriver == NULL ) + poGDALDriver = poDriverManager->GetDriverByName(pszName); + if( poGDALDriver == NULL || + poGDALDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + return NULL; + return poGDALDriver; } /************************************************************************/ @@ -669,151 +378,3 @@ OGRSFDriverH OGRGetDriverByName( const char *pszName ) return (OGRSFDriverH) OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName( pszName ); } - -/************************************************************************/ -/* AutoLoadDrivers() */ -/************************************************************************/ - -/** - * \brief Auto-load GDAL drivers from shared libraries. - * - * This function will automatically load drivers from shared libraries. It - * searches the "driver path" for .so (or .dll) files that start with the - * prefix "ogr_X.so". It then tries to load them and then tries to call - * a function within them called RegisterOGRX() where the 'X' is the same - * as the remainder of the shared library basename, or failing that to - * call GDALRegisterMe(). - * - * There are a few rules for the driver path. If the GDAL_DRIVER_PATH - * environment variable it set, it is taken to be a list of directories to - * search separated by colons on unix, or semi-colons on Windows. - * - * If that is not set the following defaults are used: - * - *
      - *
    • Linux/Unix: /lib/gdalplugins is searched or - * /usr/local/lib/gdalplugins if the install prefix is not known. - *
    • MacOSX: /PlugIns is searched, or /usr/local/lib/gdalplugins if - * the install prefix is not known. Also, the framework directory - * /Library/Application Support/GDAL/PlugIns is searched. - *
    • Win32: /lib/gdalplugins if the prefix is known (normally it - * is not), otherwise the gdalplugins subdirectory of the directory containing - * the currently running executable is used. - *
    - */ - -void OGRSFDriverRegistrar::AutoLoadDrivers() - -{ - char **papszSearchPath = NULL; - const char *pszGDAL_DRIVER_PATH = - CPLGetConfigOption( "OGR_DRIVER_PATH", NULL ); - - if( pszGDAL_DRIVER_PATH == NULL ) - pszGDAL_DRIVER_PATH = - CPLGetConfigOption( "GDAL_DRIVER_PATH", NULL ); - -/* -------------------------------------------------------------------- */ -/* Where should we look for stuff? */ -/* -------------------------------------------------------------------- */ - if( pszGDAL_DRIVER_PATH != NULL ) - { -#ifdef WIN32 - papszSearchPath = - CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ";", TRUE, FALSE ); -#else - papszSearchPath = - CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ":", TRUE, FALSE ); -#endif - } - else - { -#ifdef GDAL_PREFIX - papszSearchPath = CSLAddString( papszSearchPath, - #ifdef MACOSX_FRAMEWORK - GDAL_PREFIX "/PlugIns"); - #else - GDAL_PREFIX "/lib/gdalplugins" ); - #endif -#else - char szExecPath[1024]; - - if( CPLGetExecPath( szExecPath, sizeof(szExecPath) ) ) - { - char szPluginDir[sizeof(szExecPath)+50]; - strcpy( szPluginDir, CPLGetDirname( szExecPath ) ); - strcat( szPluginDir, "\\gdalplugins\\" ); - papszSearchPath = CSLAddString( papszSearchPath, szPluginDir ); - } - else - { - papszSearchPath = CSLAddString( papszSearchPath, - "/usr/local/lib/gdalplugins" ); - } -#endif - -#ifdef MACOSX_FRAMEWORK -#define num2str(x) str(x) -#define str(x) #x - papszSearchPath = CSLAddString( papszSearchPath, - "/Library/Application Support/GDAL/" - num2str(GDAL_VERSION_MAJOR) "." - num2str(GDAL_VERSION_MINOR) "PlugIns" ); -#endif - - } - -/* -------------------------------------------------------------------- */ -/* Scan each directory looking for files starting with gdal_ */ -/* -------------------------------------------------------------------- */ - for( int iDir = 0; iDir < CSLCount(papszSearchPath); iDir++ ) - { - char **papszFiles = CPLReadDir( papszSearchPath[iDir] ); - - for( int iFile = 0; iFile < CSLCount(papszFiles); iFile++ ) - { - char *pszFuncName; - const char *pszFilename; - const char *pszExtension = CPLGetExtension( papszFiles[iFile] ); - void *pRegister; - - if( !EQUALN(papszFiles[iFile],"ogr_",4) ) - continue; - - if( !EQUAL(pszExtension,"dll") - && !EQUAL(pszExtension,"so") - && !EQUAL(pszExtension,"dylib") ) - continue; - - pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1); - sprintf( pszFuncName, "RegisterOGR%s", - CPLGetBasename(papszFiles[iFile]) + 4 ); - - pszFilename = - CPLFormFilename( papszSearchPath[iDir], - papszFiles[iFile], NULL ); - - pRegister = CPLGetSymbol( pszFilename, pszFuncName ); - if( pRegister == NULL ) - { - strcpy( pszFuncName, "GDALRegisterMe" ); - pRegister = CPLGetSymbol( pszFilename, pszFuncName ); - } - - if( pRegister != NULL ) - { - CPLDebug( "OGR", "Auto register %s using %s.", - pszFilename, pszFuncName ); - - ((void (*)()) pRegister)(); - } - - CPLFree( pszFuncName ); - } - - CSLDestroy( papszFiles ); - } - - CSLDestroy( papszSearchPath ); -} - diff --git a/ogr/ogrspatialreference.cpp b/ogr/ogrspatialreference.cpp index 4f164ad..5f7e498 100644 --- a/ogr/ogrspatialreference.cpp +++ b/ogr/ogrspatialreference.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrspatialreference.cpp 18544 2010-01-14 06:47:29Z warmerdam $ + * $Id: ogrspatialreference.cpp 27044 2014-03-16 23:41:27Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRSpatialReference class. @@ -7,6 +7,7 @@ * ****************************************************************************** * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -33,7 +34,7 @@ #include "cpl_http.h" #include "cpl_atomic_ops.h" -CPL_CVSID("$Id: ogrspatialreference.cpp 18544 2010-01-14 06:47:29Z warmerdam $"); +CPL_CVSID("$Id: ogrspatialreference.cpp 27044 2014-03-16 23:41:27Z rouault $"); // The current opinion is that WKT longitudes like central meridian // should be relative to greenwich, not the prime meridian in use. @@ -45,7 +46,7 @@ CPL_CVSID("$Id: ogrspatialreference.cpp 18544 2010-01-14 06:47:29Z warmerdam $") /* OGRPrintDouble() */ /************************************************************************/ -static void OGRPrintDouble( char * pszStrBuf, double dfValue ) +void OGRPrintDouble( char * pszStrBuf, double dfValue ) { sprintf( pszStrBuf, "%.16g", dfValue ); @@ -414,7 +415,7 @@ void OGRSpatialReference::SetRoot( OGR_SRSNode * poNewRoot ) * more specific. * * @param pszNodePath the name of the node to search for. May contain multiple - * components such as "GEOGCS|UNITS". + * components such as "GEOGCS|UNIT". * * @return a pointer to the node found, or NULL if none. */ @@ -428,7 +429,10 @@ OGR_SRSNode *OGRSpatialReference::GetAttrNode( const char * pszNodePath ) papszPathTokens = CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE); if( CSLCount( papszPathTokens ) < 1 ) + { + CSLDestroy(papszPathTokens); return NULL; + } poNode = GetRoot(); for( int i = 0; poNode != NULL && papszPathTokens[i] != NULL; i++ ) @@ -548,6 +552,22 @@ OGRSpatialReferenceH CPL_STDCALL OSRClone( OGRSpatialReferenceH hSRS ) return (OGRSpatialReferenceH) ((OGRSpatialReference *) hSRS)->Clone(); } +/************************************************************************/ +/* dumpReadable() */ +/* */ +/* Dump pretty wkt to stdout, mostly for debugging. */ +/************************************************************************/ + +void OGRSpatialReference::dumpReadable() + +{ + char *pszPrettyWkt = NULL; + + exportToPrettyWkt( &pszPrettyWkt, FALSE ); + printf( "%s\n", pszPrettyWkt ); + CPLFree( pszPrettyWkt ); +} + /************************************************************************/ /* exportToPrettyWkt() */ /************************************************************************/ @@ -701,7 +721,27 @@ OGRErr OGRSpatialReference::importFromWkt( char ** ppszInput ) poRoot = new OGR_SRSNode(); - return poRoot->importFromWkt( ppszInput ); + OGRErr eErr = poRoot->importFromWkt( ppszInput ); + if (eErr != OGRERR_NONE) + return eErr; + +/* -------------------------------------------------------------------- */ +/* The following seems to try and detect and unconsumed */ +/* VERTCS[] coordinate system definition (ESRI style) and to */ +/* import and attach it to the existing root. Likely we will */ +/* need to extend this somewhat to bring it into an acceptable */ +/* OGRSpatialReference organization at some point. */ +/* -------------------------------------------------------------------- */ + if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS")) + { + if(((*ppszInput)[0]) == ',') + (*ppszInput)++; + OGR_SRSNode *poNewChild = new OGR_SRSNode(); + poRoot->AddChild( poNewChild ); + return poNewChild->importFromWkt( ppszInput ); + } + + return eErr; } /************************************************************************/ @@ -736,7 +776,7 @@ OGRErr OSRImportFromWkt( OGRSpatialReferenceH hSRS, char **ppszInput ) * This method does the same as the C function OSRSetAttrValue(). * * @param pszNodePath full path to attribute to be set. For instance - * "PROJCS|GEOGCS|UNITS". + * "PROJCS|GEOGCS|UNIT". * * @param pszNewNodeValue value to be assigned to node, such as "meter". * This may be NULL if you just want to force creation of the intermediate @@ -842,7 +882,7 @@ OGRErr OGRSpatialReference::SetNode( const char *pszNodePath, /** * \brief Set the angular units for the geographic coordinate system. * - * This method creates a UNITS subnode with the specified values as a + * This method creates a UNIT subnode with the specified values as a * child of the GEOGCS node. * * This method does the same as the C function OSRSetAngularUnits(). @@ -877,6 +917,8 @@ OGRErr OGRSpatialReference::SetAngularUnits( const char * pszUnitsName, if( poCS->FindChild( "UNIT" ) >= 0 ) { poUnits = poCS->GetChild( poCS->FindChild( "UNIT" ) ); + if (poUnits->GetChildCount() < 2) + return OGRERR_FAILURE; poUnits->GetChild(0)->SetValue( pszUnitsName ); poUnits->GetChild(1)->SetValue( szValue ); } @@ -985,7 +1027,7 @@ double OSRGetAngularUnits( OGRSpatialReferenceH hSRS, char ** ppszName ) /** * \brief Set the linear units for the projection. * - * This method creates a UNITS subnode with the specified values as a + * This method creates a UNIT subnode with the specified values as a * child of the PROJCS or LOCAL_CS node. It works the same as the * SetLinearUnits() method, but it also updates all existing linear * projection parameter values from the old units to the new units. @@ -1058,6 +1100,7 @@ OGRErr OSRSetLinearUnitsAndUpdateParameters( OGRSpatialReferenceH hSRS, return ((OGRSpatialReference *) hSRS)-> SetLinearUnitsAndUpdateParameters( pszUnits, dfInMeters ); } + /************************************************************************/ /* SetLinearUnits() */ /************************************************************************/ @@ -1065,8 +1108,8 @@ OGRErr OSRSetLinearUnitsAndUpdateParameters( OGRSpatialReferenceH hSRS, /** * \brief Set the linear units for the projection. * - * This method creates a UNITS subnode with the specified values as a - * child of the PROJCS or LOCAL_CS node. + * This method creates a UNIT subnode with the specified values as a + * child of the PROJCS, GEOCCS or LOCAL_CS node. * * This method does the same as the C function OSRSetLinearUnits(). * @@ -1084,6 +1127,60 @@ OGRErr OSRSetLinearUnitsAndUpdateParameters( OGRSpatialReferenceH hSRS, OGRErr OGRSpatialReference::SetLinearUnits( const char * pszUnitsName, double dfInMeters ) +{ + return SetTargetLinearUnits( NULL, pszUnitsName, dfInMeters ); +} + +/************************************************************************/ +/* OSRSetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This function is the same as OGRSpatialReference::SetLinearUnits() + */ +OGRErr OSRSetLinearUnits( OGRSpatialReferenceH hSRS, + const char * pszUnits, double dfInMeters ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLinearUnits", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLinearUnits( pszUnits, + dfInMeters ); +} + +/************************************************************************/ +/* SetTargetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This method creates a UNIT subnode with the specified values as a + * child of the target node. + * + * This method does the same as the C function OSRSetTargetLinearUnits(). + * + * @param pszTargetKey the keyword to set the linear units for. ie. "PROJCS" or "VERT_CS" + * + * @param pszUnitsName the units name to be used. Some preferred units + * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT + * and SRS_UL_US_FOOT. + * + * @param dfInMeters the value to multiple by a length in the indicated + * units to transform to meters. Some standard conversion factors can + * be found in ogr_srs_api.h. + * + * @return OGRERR_NONE on success. + * + * @since OGR 1.9.0 + */ + +OGRErr OGRSpatialReference::SetTargetLinearUnits( const char *pszTargetKey, + const char * pszUnitsName, + double dfInMeters ) + { OGR_SRSNode *poCS; OGR_SRSNode *poUnits; @@ -1091,9 +1188,19 @@ OGRErr OGRSpatialReference::SetLinearUnits( const char * pszUnitsName, bNormInfoSet = FALSE; - poCS = GetAttrNode( "PROJCS" ); - if( poCS == NULL ) - poCS = GetAttrNode( "LOCAL_CS" ); + if( pszTargetKey == NULL ) + { + poCS = GetAttrNode( "PROJCS" ); + + if( poCS == NULL ) + poCS = GetAttrNode( "LOCAL_CS" ); + if( poCS == NULL ) + poCS = GetAttrNode( "GEOCCS" ); + if( poCS == NULL && IsVertical() ) + poCS = GetAttrNode( "VERT_CS" ); + } + else + poCS = GetAttrNode( pszTargetKey ); if( poCS == NULL ) return OGRERR_FAILURE; @@ -1106,6 +1213,8 @@ OGRErr OGRSpatialReference::SetLinearUnits( const char * pszUnitsName, if( poCS->FindChild( "UNIT" ) >= 0 ) { poUnits = poCS->GetChild( poCS->FindChild( "UNIT" ) ); + if (poUnits->GetChildCount() < 2) + return OGRERR_FAILURE; poUnits->GetChild(0)->SetValue( pszUnitsName ); poUnits->GetChild(1)->SetValue( szValue ); if( poUnits->FindChild( "AUTHORITY" ) != -1 ) @@ -1128,18 +1237,21 @@ OGRErr OGRSpatialReference::SetLinearUnits( const char * pszUnitsName, /************************************************************************/ /** - * \brief Set the linear units for the projection. + * \brief Set the linear units for the target node. * - * This function is the same as OGRSpatialReference::SetLinearUnits() + * This function is the same as OGRSpatialReference::SetTargetLinearUnits() + * + * @since OGR 1.9.0 */ -OGRErr OSRSetLinearUnits( OGRSpatialReferenceH hSRS, - const char * pszUnits, double dfInMeters ) +OGRErr OSRSetTargetLinearUnits( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, + const char * pszUnits, double dfInMeters ) { - VALIDATE_POINTER1( hSRS, "OSRSetLinearUnits", CE_Failure ); + VALIDATE_POINTER1( hSRS, "OSRSetTargetLinearUnits", CE_Failure ); - return ((OGRSpatialReference *) hSRS)->SetLinearUnits( pszUnits, - dfInMeters ); + return ((OGRSpatialReference *) hSRS)-> + SetTargetLinearUnits( pszTargetKey, pszUnits, dfInMeters ); } /************************************************************************/ @@ -1150,8 +1262,8 @@ OGRErr OSRSetLinearUnits( OGRSpatialReferenceH hSRS, * \brief Fetch linear projection units. * * If no units are available, a value of "Meters" and 1.0 will be assumed. - * This method only checks directly under the PROJCS or LOCAL_CS node for - * units. + * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node + * for units. * * This method does the same thing as the C function OSRGetLinearUnits()/ * @@ -1167,10 +1279,68 @@ OGRErr OSRSetLinearUnits( OGRSpatialReferenceH hSRS, double OGRSpatialReference::GetLinearUnits( char ** ppszName ) const { - const OGR_SRSNode *poCS = GetAttrNode( "PROJCS" ); + return GetTargetLinearUnits( NULL, ppszName ); +} - if( poCS == NULL ) - poCS = GetAttrNode( "LOCAL_CS" ); +/************************************************************************/ +/* OSRGetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Fetch linear projection units. + * + * This function is the same as OGRSpatialReference::GetLinearUnits() + */ +double OSRGetLinearUnits( OGRSpatialReferenceH hSRS, char ** ppszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetLinearUnits", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetLinearUnits( ppszName ); +} + +/************************************************************************/ +/* GetTargetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Fetch linear units for target. + * + * If no units are available, a value of "Meters" and 1.0 will be assumed. + * + * This method does the same thing as the C function OSRGetTargetLinearUnits()/ + * + * @param pszTargetKey the key to look on. ie. "PROJCS" or "VERT_CS". + * @param ppszName a pointer to be updated with the pointer to the + * units name. The returned value remains internal to the OGRSpatialReference + * and shouldn't be freed, or modified. It may be invalidated on the next + * OGRSpatialReference call. + * + * @return the value to multiply by linear distances to transform them to + * meters. + * + * @since OGR 1.9.0 + */ + +double OGRSpatialReference::GetTargetLinearUnits( const char *pszTargetKey, + char ** ppszName ) const + +{ + const OGR_SRSNode *poCS; + + if( pszTargetKey == NULL ) + { + poCS = GetAttrNode( "PROJCS" ); + + if( poCS == NULL ) + poCS = GetAttrNode( "LOCAL_CS" ); + if( poCS == NULL ) + poCS = GetAttrNode( "GEOCCS" ); + if( poCS == NULL && IsVertical() ) + poCS = GetAttrNode( "VERT_CS" ); + } + else + poCS = GetAttrNode( pszTargetKey ); if( ppszName != NULL ) *ppszName = (char*) "unknown"; @@ -1196,20 +1366,25 @@ double OGRSpatialReference::GetLinearUnits( char ** ppszName ) const } /************************************************************************/ -/* OSRGetLinearUnits() */ +/* OSRGetTargetLinearUnits() */ /************************************************************************/ /** * \brief Fetch linear projection units. * - * This function is the same as OGRSpatialReference::GetLinearUnits() + * This function is the same as OGRSpatialReference::GetTargetLinearUnits() + * + * @since OGR 1.9.0 */ -double OSRGetLinearUnits( OGRSpatialReferenceH hSRS, char ** ppszName ) +double OSRGetTargetLinearUnits( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, + char ** ppszName ) { - VALIDATE_POINTER1( hSRS, "OSRGetLinearUnits", 0 ); + VALIDATE_POINTER1( hSRS, "OSRGetTargetLinearUnits", 0 ); - return ((OGRSpatialReference *) hSRS)->GetLinearUnits( ppszName ); + return ((OGRSpatialReference *) hSRS)->GetTargetLinearUnits( pszTargetKey, + ppszName ); } /************************************************************************/ @@ -1331,19 +1506,36 @@ OGRSpatialReference::SetGeogCS( const char * pszGeogName, { bNormInfoSet = FALSE; +/* -------------------------------------------------------------------- */ +/* For a geocentric coordinate system we want to set the datum */ +/* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */ +/* temporary srs and use the copy method which has special */ +/* handling for GEOCCS. */ +/* -------------------------------------------------------------------- */ + if( IsGeocentric() ) + { + OGRSpatialReference oGCS; + + oGCS.SetGeogCS( pszGeogName, pszDatumName, pszSpheroidName, + dfSemiMajor, dfInvFlattening, + pszPMName, dfPMOffset, + pszAngularUnits, dfConvertToRadians ); + return CopyGeogCSFrom( &oGCS ); + } + /* -------------------------------------------------------------------- */ /* Do we already have a GEOGCS? If so, blow it away so it can */ /* be properly replaced. */ /* -------------------------------------------------------------------- */ if( GetAttrNode( "GEOGCS" ) != NULL ) { - OGR_SRSNode *poPROJCS; + OGR_SRSNode *poCS; if( EQUAL(GetRoot()->GetValue(),"GEOGCS") ) Clear(); - else if( (poPROJCS = GetAttrNode( "PROJCS" )) != NULL - && poPROJCS->FindChild( "GEOGCS" ) != -1 ) - poPROJCS->DestroyChild( poPROJCS->FindChild( "GEOGCS" ) ); + else if( (poCS = GetAttrNode( "PROJCS" )) != NULL + && poCS->FindChild( "GEOGCS" ) != -1 ) + poCS->DestroyChild( poCS->FindChild( "GEOGCS" ) ); else return OGRERR_FAILURE; } @@ -1532,16 +1724,16 @@ OGRErr OGRSpatialReference::SetWellKnownGeogCS( const char * pszName ) /* -------------------------------------------------------------------- */ char *pszWKT = NULL; - if( EQUAL(pszName, "WGS84") || EQUAL(pszName,"CRS84") ) + if( EQUAL(pszName, "WGS84") || EQUAL(pszName,"CRS84") || EQUAL(pszName,"CRS:84") ) pszWKT = (char* ) SRS_WKT_WGS84; else if( EQUAL(pszName, "WGS72") ) pszWKT = (char* ) "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],TOWGS84[0,0,4.5,0,0,0.554,0.2263],AUTHORITY[\"EPSG\",\"6322\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4322\"]]"; - else if( EQUAL(pszName, "NAD27") || EQUAL(pszName, "CRS27") ) - pszWKT = (char* ) "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke 1866\",6378206.4,294.978698213898,AUTHORITY[\"EPSG\",\"7008\"]],TOWGS84[-3,142,183,0,0,0,0],AUTHORITY[\"EPSG\",\"6267\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4267\"]]"; + else if( EQUAL(pszName, "NAD27") || EQUAL(pszName, "CRS27") || EQUAL(pszName,"CRS:27") ) + pszWKT = (char* ) "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke 1866\",6378206.4,294.978698213898,AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4267\"]]"; - else if( EQUAL(pszName, "NAD83") || EQUAL(pszName,"CRS83") ) + else if( EQUAL(pszName, "NAD83") || EQUAL(pszName,"CRS83") || EQUAL(pszName,"CRS:83") ) pszWKT = (char* ) "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4269\"]]"; else @@ -1602,6 +1794,30 @@ OGRErr OGRSpatialReference::CopyGeogCSFrom( bNormInfoSet = FALSE; +/* -------------------------------------------------------------------- */ +/* Handle geocentric coordinate systems specially. We just */ +/* want to copy the DATUM and PRIMEM nodes. */ +/* -------------------------------------------------------------------- */ + if( IsGeocentric() ) + { + if( GetRoot()->FindChild( "DATUM" ) != -1 ) + GetRoot()->DestroyChild( GetRoot()->FindChild( "DATUM" ) ); + if( GetRoot()->FindChild( "PRIMEM" ) != -1 ) + GetRoot()->DestroyChild( GetRoot()->FindChild( "PRIMEM" ) ); + + const OGR_SRSNode *poDatum = poSrcSRS->GetAttrNode( "DATUM" ); + const OGR_SRSNode *poPrimeM = poSrcSRS->GetAttrNode( "PRIMEM" ); + + if( poDatum == NULL || poPrimeM == NULL ) + return OGRERR_FAILURE; + + poRoot->InsertChild( poDatum->Clone(), 1 ); + poRoot->InsertChild( poPrimeM->Clone(), 2 ); + + return OGRERR_NONE; + + } + /* -------------------------------------------------------------------- */ /* Do we already have a GEOGCS? If so, blow it away so it can */ /* be properly replaced. */ @@ -1680,6 +1896,7 @@ OGRErr OSRCopyGeogCSFrom( OGRSpatialReferenceH hSRS, * WGS84 or WGS72. *
  • WKT (directly or in a file) in ESRI format should be prefixed with * ESRI:: to trigger an automatic morphFromESRI(). + *
  • "IGNF:xxx" - "+init=IGNF:xxx" passed on to importFromProj4(). * * * It is expected that this method will be extended in the future to support @@ -1717,7 +1934,9 @@ OGRErr OGRSpatialReference::SetFromUserInput( const char * pszDefinition ) /* -------------------------------------------------------------------- */ if( EQUALN(pszDefinition,"PROJCS",6) || EQUALN(pszDefinition,"GEOGCS",6) - || EQUALN(pszDefinition,"COMPD_CS",6) + || EQUALN(pszDefinition,"COMPD_CS",8) + || EQUALN(pszDefinition,"GEOCCS",6) + || EQUALN(pszDefinition,"VERT_CS",7) || EQUALN(pszDefinition,"LOCAL_CS",8) ) { err = importFromWkt( (char **) &pszDefinition ); @@ -1727,22 +1946,67 @@ OGRErr OGRSpatialReference::SetFromUserInput( const char * pszDefinition ) return err; } - if( EQUALN(pszDefinition,"EPSG:",5) ) - return importFromEPSG( atoi(pszDefinition+5) ); + if( EQUALN(pszDefinition,"EPSG:",5) + || EQUALN(pszDefinition,"EPSGA:",6) ) + { + OGRErr eStatus; + + if( EQUALN(pszDefinition,"EPSG:",5) ) + eStatus = importFromEPSG( atoi(pszDefinition+5) ); + + else /* if( EQUALN(pszDefinition,"EPSGA:",6) ) */ + eStatus = importFromEPSGA( atoi(pszDefinition+6) ); + + // Do we want to turn this into a compound definition + // with a vertical datum? + if( eStatus == OGRERR_NONE && strchr( pszDefinition, '+' ) != NULL ) + { + OGRSpatialReference oVertSRS; + + eStatus = oVertSRS.importFromEPSG( + atoi(strchr(pszDefinition,'+')+1) ); + if( eStatus == OGRERR_NONE ) + { + OGR_SRSNode *poHorizSRS = GetRoot()->Clone(); + + Clear(); - if( EQUALN(pszDefinition,"EPSGA:",6) ) - return importFromEPSGA( atoi(pszDefinition+6) ); + CPLString osName = poHorizSRS->GetChild(0)->GetValue(); + osName += " + "; + osName += oVertSRS.GetRoot()->GetChild(0)->GetValue(); + + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( poHorizSRS ); + GetRoot()->AddChild( oVertSRS.GetRoot()->Clone() ); + } + + return eStatus; + } + else + return eStatus; + } - if( EQUALN(pszDefinition,"urn:ogc:def:crs:",16) - || EQUALN(pszDefinition,"urn:x-ogc:def:crs:",18) ) + if( EQUALN(pszDefinition,"urn:ogc:def:crs:",16) + || EQUALN(pszDefinition,"urn:ogc:def:crs,crs:",20) + || EQUALN(pszDefinition,"urn:x-ogc:def:crs:",18) + || EQUALN(pszDefinition,"urn:opengis:crs:",16) + || EQUALN(pszDefinition,"urn:opengis:def:crs:",20)) return importFromURN( pszDefinition ); + if( EQUALN(pszDefinition,"http://opengis.net/def/crs",26) + || EQUALN(pszDefinition,"http://www.opengis.net/def/crs",30) + || EQUALN(pszDefinition,"www.opengis.net/def/crs",23)) + return importFromCRSURL( pszDefinition ); + if( EQUALN(pszDefinition,"AUTO:",5) ) return importFromWMSAUTO( pszDefinition ); if( EQUALN(pszDefinition,"OGC:",4) ) // WMS/WCS OGC codes like OGC:CRS84 return SetWellKnownGeogCS( pszDefinition+4 ); + if( EQUALN(pszDefinition,"CRS:",4) ) + return SetWellKnownGeogCS( pszDefinition ); + if( EQUALN(pszDefinition,"DICT:",5) && strstr(pszDefinition,",") ) { @@ -1773,10 +2037,27 @@ OGRErr OGRSpatialReference::SetFromUserInput( const char * pszDefinition ) || strstr(pszDefinition,"+init") != NULL ) return importFromProj4( pszDefinition ); + if( EQUALN(pszDefinition,"IGNF:", 5) ) + { + char* pszProj4Str = (char*) CPLMalloc(6 + strlen(pszDefinition) + 1); + strcpy(pszProj4Str, "+init="); + strcat(pszProj4Str, pszDefinition); + err = importFromProj4( pszProj4Str ); + CPLFree(pszProj4Str); + + return err; + } + if( EQUALN(pszDefinition,"http://",7) ) { return importFromUrl (pszDefinition); } + + if( EQUAL(pszDefinition,"osgb:BNG") ) + { + return importFromEPSG(27700); + } + /* -------------------------------------------------------------------- */ /* Try to open it as a file. */ /* -------------------------------------------------------------------- */ @@ -1960,6 +2241,78 @@ OGRErr OSRImportFromUrl( OGRSpatialReferenceH hSRS, const char *pszUrl ) return ((OGRSpatialReference *) hSRS)->importFromUrl( pszUrl ); } +/************************************************************************/ +/* importFromURNPart() */ +/************************************************************************/ +OGRErr OGRSpatialReference::importFromURNPart(const char* pszAuthority, + const char* pszCode, + const char* pszURN) +{ + +/* -------------------------------------------------------------------- */ +/* Is this an EPSG code? Note that we import it with EPSG */ +/* preferred axis ordering for geographic coordinate systems! */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszAuthority,"EPSG",4) ) + return importFromEPSGA( atoi(pszCode) ); + +/* -------------------------------------------------------------------- */ +/* Is this an IAU code? Lets try for the IAU2000 dictionary. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszAuthority,"IAU",3) ) + return importFromDict( "IAU2000.wkt", pszCode ); + +/* -------------------------------------------------------------------- */ +/* Is this an OGC code? */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszAuthority,"OGC",3) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s has unrecognised authority.", + pszURN ); + return OGRERR_FAILURE; + } + + if( EQUALN(pszCode,"CRS84",5) ) + return SetWellKnownGeogCS( pszCode ); + else if( EQUALN(pszCode,"CRS83",5) ) + return SetWellKnownGeogCS( pszCode ); + else if( EQUALN(pszCode,"CRS27",5) ) + return SetWellKnownGeogCS( pszCode ); + +/* -------------------------------------------------------------------- */ +/* Handle auto codes. We need to convert from format */ +/* AUTO42001:99:8888 to format AUTO:42001,99,8888. */ +/* -------------------------------------------------------------------- */ + else if( EQUALN(pszCode,"AUTO",4) ) + { + char szWMSAuto[100]; + int i; + + if( strlen(pszCode) > sizeof(szWMSAuto)-2 ) + return OGRERR_FAILURE; + + strcpy( szWMSAuto, "AUTO:" ); + strcpy( szWMSAuto + 5, pszCode + 4 ); + for( i = 5; szWMSAuto[i] != '\0'; i++ ) + { + if( szWMSAuto[i] == ':' ) + szWMSAuto[i] = ','; + } + + return importFromWMSAUTO( szWMSAuto ); + } + +/* -------------------------------------------------------------------- */ +/* Not a recognise OGC item. */ +/* -------------------------------------------------------------------- */ + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s value not supported.", + pszURN ); + + return OGRERR_FAILURE; +} + /************************************************************************/ /* importFromURN() */ /* */ @@ -1985,12 +2338,18 @@ OGRErr OSRImportFromUrl( OGRSpatialReferenceH hSRS, const char *pszUrl ) OGRErr OGRSpatialReference::importFromURN( const char *pszURN ) { - const char *pszCur = pszURN + 16; + const char *pszCur; if( EQUALN(pszURN,"urn:ogc:def:crs:",16) ) pszCur = pszURN + 16; + else if( EQUALN(pszURN,"urn:ogc:def:crs,crs:",20) ) + pszCur = pszURN + 20; else if( EQUALN(pszURN,"urn:x-ogc:def:crs:",18) ) pszCur = pszURN + 18; + else if( EQUALN(pszURN,"urn:opengis:crs:",16) ) + pszCur = pszURN + 16; + else if( EQUALN(pszURN,"urn:opengis:def:crs:",20) ) + pszCur = pszURN + 20; else { CPLError( CE_Failure, CPLE_AppDefined, @@ -2010,7 +2369,7 @@ OGRErr OGRSpatialReference::importFromURN( const char *pszURN ) /* -------------------------------------------------------------------- */ /* Find code (ignoring version) out of string like: */ /* */ -/* authority:version:code */ +/* authority:[version]:code */ /* -------------------------------------------------------------------- */ const char *pszAuthority = pszCur; @@ -2021,75 +2380,242 @@ OGRErr OGRSpatialReference::importFromURN( const char *pszURN ) pszCur++; // skip version + const char* pszBeforeVersion = pszCur; while( *pszCur != ':' && *pszCur ) pszCur++; if( *pszCur == ':' ) pszCur++; + else + /* We come here in the case, the content to parse is authority:code (instead of authority::code) */ + /* which is probably illegal according to http://www.opengeospatial.org/ogcUrnPolicy */ + /* but such content is found for example in what is returned by GeoServer */ + pszCur = pszBeforeVersion; const char *pszCode = pszCur; -/* -------------------------------------------------------------------- */ -/* Is this an EPSG code? Note that we import it with EPSG */ -/* preferred axis ordering for geographic coordinate systems! */ -/* -------------------------------------------------------------------- */ - if( EQUALN(pszAuthority,"EPSG:",5) ) - return importFromEPSGA( atoi(pszCode) ); + const char* pszComma = strchr(pszCur, ','); + if (pszComma == NULL) + return importFromURNPart(pszAuthority, pszCode, pszURN); -/* -------------------------------------------------------------------- */ -/* Is this an IAU code? Lets try for the IAU2000 dictionary. */ -/* -------------------------------------------------------------------- */ - if( EQUALN(pszAuthority,"IAU",3) ) - return importFromDict( "IAU2000.wkt", pszCode ); -/* -------------------------------------------------------------------- */ -/* Is this an OGC code? */ -/* -------------------------------------------------------------------- */ - if( !EQUALN(pszAuthority,"OGC:",4) ) + /* There's a second part with the vertical SRS */ + pszCur = pszComma + 1; + if (strncmp(pszCur, "crs:", 4) != 0) { - CPLError( CE_Failure, CPLE_AppDefined, - "URN %s has unrecognised authority.", - pszURN ); + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s not a supported format.", pszURN ); return OGRERR_FAILURE; } - if( EQUALN(pszCode,"CRS84",5) ) - return SetWellKnownGeogCS( pszCode ); - else if( EQUALN(pszCode,"CRS83",5) ) - return SetWellKnownGeogCS( pszCode ); - else if( EQUALN(pszCode,"CRS27",5) ) - return SetWellKnownGeogCS( pszCode ); + pszCur += 4; -/* -------------------------------------------------------------------- */ -/* Handle auto codes. We need to convert from format */ -/* AUTO42001:99:8888 to format AUTO:42001,99,8888. */ -/* -------------------------------------------------------------------- */ - else if( EQUALN(pszCode,"AUTO",4) ) + char* pszFirstCode = CPLStrdup(pszCode); + pszFirstCode[pszComma - pszCode] = '\0'; + OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN); + CPLFree(pszFirstCode); + + // Do we want to turn this into a compound definition + // with a vertical datum? + if( eStatus == OGRERR_NONE ) { - char szWMSAuto[100]; - int i; + OGRSpatialReference oVertSRS; + + /* -------------------------------------------------------------------- */ + /* Find code (ignoring version) out of string like: */ + /* */ + /* authority:[version]:code */ + /* -------------------------------------------------------------------- */ + pszAuthority = pszCur; + + // skip authority + while( *pszCur != ':' && *pszCur ) + pszCur++; + if( *pszCur == ':' ) + pszCur++; + + // skip version + pszBeforeVersion = pszCur; + while( *pszCur != ':' && *pszCur ) + pszCur++; + if( *pszCur == ':' ) + pszCur++; + else + pszCur = pszBeforeVersion; - if( strlen(pszCode) > sizeof(szWMSAuto)-2 ) - return OGRERR_FAILURE; + pszCode = pszCur; - strcpy( szWMSAuto, "AUTO:" ); - strcpy( szWMSAuto + 5, pszCode + 4 ); - for( i = 5; szWMSAuto[i] != '\0'; i++ ) + eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN); + if( eStatus == OGRERR_NONE ) { - if( szWMSAuto[i] == ':' ) - szWMSAuto[i] = ','; + OGR_SRSNode *poHorizSRS = GetRoot()->Clone(); + + Clear(); + + CPLString osName = poHorizSRS->GetChild(0)->GetValue(); + osName += " + "; + osName += oVertSRS.GetRoot()->GetChild(0)->GetValue(); + + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( poHorizSRS ); + GetRoot()->AddChild( oVertSRS.GetRoot()->Clone() ); } - return importFromWMSAUTO( szWMSAuto ); + return eStatus; + } + else + return eStatus; +} + +/************************************************************************/ +/* importFromCRSURL() */ +/* */ +/* See OGC Best Practice document 11-135 for details. */ +/************************************************************************/ + +/** + * \brief Initialize from OGC URL. + * + * Initializes this spatial reference from a coordinate system defined + * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice + * paper 11-135. Currently EPSG and OGC authority values are supported, + * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88). + * + * This method is also supported through SetFromUserInput() which can + * normally be used for URLs. + * + * @param pszURL the URL string. + * + * @return OGRERR_NONE on success or an error code. + */ + +OGRErr OGRSpatialReference::importFromCRSURL( const char *pszURL ) + +{ + const char *pszCur; + + if( EQUALN(pszURL,"http://opengis.net/def/crs",26) ) + pszCur = pszURL + 26; + else if( EQUALN(pszURL,"http://www.opengis.net/def/crs",30) ) + pszCur = pszURL + 30; + else if( EQUALN(pszURL,"www.opengis.net/def/crs",23) ) + pszCur = pszURL + 23; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "URL %s not a supported format.", pszURL ); + return OGRERR_FAILURE; } /* -------------------------------------------------------------------- */ -/* Not a recognise OGC item. */ +/* Clear any existing definition. */ /* -------------------------------------------------------------------- */ - CPLError( CE_Failure, CPLE_AppDefined, - "URN %s value not supported.", - pszURN ); + if( GetRoot() != NULL ) + { + delete poRoot; + poRoot = NULL; + } - return OGRERR_FAILURE; + if( EQUALN(pszCur, "-compound?1=", 12) ) + { +/* -------------------------------------------------------------------- */ +/* It's a compound CRS, of the form: */ +/* */ +/* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */ +/* -------------------------------------------------------------------- */ + pszCur += 12; + + // extract each component CRS URL + int iComponentUrl = 2; + + CPLString osName = ""; + Clear(); + + while (iComponentUrl != -1) + { + char searchStr[5]; + sprintf(searchStr, "&%d=", iComponentUrl); + + const char* pszUrlEnd = strstr(pszCur, searchStr); + + // figure out the next component URL + char* pszComponentUrl; + + if( pszUrlEnd ) + { + size_t nLen = pszUrlEnd - pszCur; + pszComponentUrl = (char*) CPLMalloc(nLen + 1); + strncpy(pszComponentUrl, pszCur, nLen); + pszComponentUrl[nLen] = '\0'; + + ++iComponentUrl; + pszCur += nLen + strlen(searchStr); + } + else + { + if( iComponentUrl == 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Compound CRS URLs must have at least two component CRSs." ); + return OGRERR_FAILURE; + } + else + { + pszComponentUrl = CPLStrdup(pszCur); + // no more components + iComponentUrl = -1; + } + } + + OGRSpatialReference oComponentSRS; + OGRErr eStatus = oComponentSRS.importFromCRSURL( pszComponentUrl ); + + CPLFree(pszComponentUrl); + pszComponentUrl = NULL; + + if( eStatus == OGRERR_NONE ) + { + if( osName.length() != 0 ) + { + osName += " + "; + } + osName += oComponentSRS.GetRoot()->GetValue(); + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( oComponentSRS.GetRoot()->Clone() ); + } + else + return eStatus; + } + + + return OGRERR_NONE; + } + else + { +/* -------------------------------------------------------------------- */ +/* It's a normal CRS URL, of the form: */ +/* */ +/* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */ +/* -------------------------------------------------------------------- */ + ++pszCur; + const char *pszAuthority = pszCur; + + // skip authority + while( *pszCur != '/' && *pszCur ) + pszCur++; + if( *pszCur == '/' ) + pszCur++; + + + // skip version + while( *pszCur != '/' && *pszCur ) + pszCur++; + if( *pszCur == '/' ) + pszCur++; + + const char *pszCode = pszCur; + + return importFromURNPart( pszAuthority, pszCode, pszURL ); + } } /************************************************************************/ @@ -2395,7 +2921,7 @@ double OSRGetSemiMinor( OGRSpatialReferenceH hSRS, OGRErr *pnErr ) * * This method is the same as the C function OSRSetLocalCS(). * - * This method is will ensure a LOCAL_CS node is created as the root, + * This method will ensure a LOCAL_CS node is created as the root, * and set the provided name on it. It must be used before SetLinearUnits(). * * @param pszName the user visible name to assign. Not used as a key. @@ -2410,34 +2936,312 @@ OGRErr OGRSpatialReference::SetLocalCS( const char * pszName ) if( poCS == NULL && GetRoot() != NULL ) { - CPLDebug( "OGR", - "OGRSpatialReference::SetLocalCS(%s) failed.\n" - "It appears an incompatible root node (%s) already exists.\n", - pszName, GetRoot()->GetValue() ); + CPLDebug( "OGR", + "OGRSpatialReference::SetLocalCS(%s) failed.\n" + "It appears an incompatible root node (%s) already exists.\n", + pszName, GetRoot()->GetValue() ); + return OGRERR_FAILURE; + } + else + { + SetNode( "LOCAL_CS", pszName ); + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* OSRSetLocalCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible LOCAL_CS name. + * + * This function is the same as OGRSpatialReference::SetLocalCS() + */ +OGRErr OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char * pszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLocalCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLocalCS( pszName ); +} + +/************************************************************************/ +/* SetGeocCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible GEOCCS name. + * + * This method is the same as the C function OSRSetGeocCS(). + + * This method will ensure a GEOCCS node is created as the root, + * and set the provided name on it. If used on a GEOGCS coordinate system, + * the DATUM and PRIMEM nodes from the GEOGCS will be tarnsferred over to + * the GEOGCS. + * + * @param pszName the user visible name to assign. Not used as a key. + * + * @return OGRERR_NONE on success. + * + * @since OGR 1.9.0 + */ + +OGRErr OGRSpatialReference::SetGeocCS( const char * pszName ) + +{ + OGR_SRSNode *poGeogCS = NULL; + OGR_SRSNode *poGeocCS = GetAttrNode( "GEOCCS" ); + + if( poRoot != NULL && EQUAL(poRoot->GetValue(),"GEOGCS") ) + { + poGeogCS = poRoot; + poRoot = NULL; + } + + if( poGeocCS == NULL && GetRoot() != NULL ) + { + CPLDebug( "OGR", + "OGRSpatialReference::SetGeocCS(%s) failed.\n" + "It appears an incompatible root node (%s) already exists.\n", + pszName, GetRoot()->GetValue() ); + return OGRERR_FAILURE; + } + + SetNode( "GEOCCS", pszName ); + + if( poGeogCS != NULL ) + { + OGR_SRSNode *poDatum = poGeogCS->GetNode( "DATUM" ); + OGR_SRSNode *poPRIMEM = poGeogCS->GetNode( "PRIMEM" ); + if ( poDatum != NULL && poPRIMEM != NULL ) + { + poRoot->InsertChild( poDatum->Clone(), 1 ); + poRoot->InsertChild( poPRIMEM->Clone(), 2 ); + } + delete poGeogCS; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGeocCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible PROJCS name. + * + * This function is the same as OGRSpatialReference::SetGeocCS() + * + * @since OGR 1.9.0 + */ +OGRErr OSRSetGeocCS( OGRSpatialReferenceH hSRS, const char * pszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGeocCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGeocCS( pszName ); +} + +/************************************************************************/ +/* SetVertCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible VERT_CS name. + * + * This method is the same as the C function OSRSetVertCS(). + + * This method will ensure a VERT_CS node is created if needed. If the + * existing coordinate system is GEOGCS or PROJCS rooted, then it will be + * turned into a COMPD_CS. + * + * @param pszVertCSName the user visible name of the vertical coordinate + * system. Not used as a key. + * + * @param pszVertDatumName the user visible name of the vertical datum. It + * is helpful if this matches the EPSG name. + * + * @param nVertDatumType the OGC vertical datum type, usually 2005. + * + * @return OGRERR_NONE on success. + * + * @since OGR 1.9.0 + */ + +OGRErr OGRSpatialReference::SetVertCS( const char * pszVertCSName, + const char * pszVertDatumName, + int nVertDatumType ) + +{ +/* -------------------------------------------------------------------- */ +/* Handle the case where we want to make a compound coordinate */ +/* system. */ +/* -------------------------------------------------------------------- */ + if( IsProjected() || IsGeographic() ) + { + OGR_SRSNode *poNewRoot = new OGR_SRSNode( "COMPD_CS" ); + poNewRoot->AddChild( poRoot ); + poRoot = poNewRoot; + } + + else if( GetAttrNode( "VERT_CS" ) == NULL ) + Clear(); + +/* -------------------------------------------------------------------- */ +/* If we already have a VERT_CS, wipe and recreate the root */ +/* otherwise create the VERT_CS now. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poVertCS = GetAttrNode( "VERT_CS" ); + + if( poVertCS != NULL ) + { + poVertCS->ClearChildren(); + } + else + { + poVertCS = new OGR_SRSNode( "VERT_CS" ); + if( poRoot != NULL && EQUAL(poRoot->GetValue(),"COMPD_CS") ) + { + poRoot->AddChild( poVertCS ); + } + else + SetRoot( poVertCS ); + } + +/* -------------------------------------------------------------------- */ +/* Set the name, datumname, and type. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poVertDatum; + + poVertCS->AddChild( new OGR_SRSNode( pszVertCSName ) ); + + poVertDatum = new OGR_SRSNode( "VERT_DATUM" ); + poVertCS->AddChild( poVertDatum ); + + poVertDatum->AddChild( new OGR_SRSNode( pszVertDatumName ) ); + + CPLString osVertDatumType; + osVertDatumType.Printf( "%d", nVertDatumType ); + poVertDatum->AddChild( new OGR_SRSNode( osVertDatumType ) ); + + // add default axis node. + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Up" ) ); + poAxis->AddChild( new OGR_SRSNode( "UP" ) ); + + poVertCS->AddChild( poAxis ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetVertCS() */ +/************************************************************************/ + +/** + * \brief Setup the vertical coordinate system. + * + * This function is the same as OGRSpatialReference::SetVertCS() + * + * @since OGR 1.9.0 + */ +OGRErr OSRSetVertCS( OGRSpatialReferenceH hSRS, + const char * pszVertCSName, + const char * pszVertDatumName, + int nVertDatumType ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetVertCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetVertCS( pszVertCSName, + pszVertDatumName, + nVertDatumType ); +} + +/************************************************************************/ +/* SetCompoundCS() */ +/************************************************************************/ + +/** + * \brief Setup a compound coordinate system. + * + * This method is the same as the C function OSRSetCompoundCS(). + + * This method is replace the current SRS with a COMPD_CS coordinate system + * consisting of the passed in horizontal and vertical coordinate systems. + * + * @param pszName the name of the compound coordinate system. + * + * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS). + * + * @param poVertSRS the vertical SRS (VERT_CS). + * + * @return OGRERR_NONE on success. + */ + +OGRErr +OGRSpatialReference::SetCompoundCS( const char *pszName, + const OGRSpatialReference *poHorizSRS, + const OGRSpatialReference *poVertSRS ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify these are legal horizontal and vertical coordinate */ +/* systems. */ +/* -------------------------------------------------------------------- */ + if( !poVertSRS->IsVertical() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SetCompoundCS() fails, vertical component is not VERT_CS." ); return OGRERR_FAILURE; } - else + if( !poHorizSRS->IsProjected() + && !poHorizSRS->IsGeographic() ) { - SetNode( "LOCAL_CS", pszName ); - return OGRERR_NONE; + CPLError( CE_Failure, CPLE_AppDefined, + "SetCompoundCS() fails, horizontal component is not PROJCS or GEOGCS." ); + return OGRERR_FAILURE; } + +/* -------------------------------------------------------------------- */ +/* Replace with compound srs. */ +/* -------------------------------------------------------------------- */ + Clear(); + + poRoot = new OGR_SRSNode( "COMPD_CS" ); + poRoot->AddChild( new OGR_SRSNode( pszName ) ); + poRoot->AddChild( poHorizSRS->GetRoot()->Clone() ); + poRoot->AddChild( poVertSRS->GetRoot()->Clone() ); + + return OGRERR_NONE; } /************************************************************************/ -/* OSRSetLocalCS() */ +/* OSRSetCompoundCS() */ /************************************************************************/ /** - * \brief Set the user visible LOCAL_CS name. + * \brief Setup a compound coordinate system. * - * This function is the same as OGRSpatialReference::SetLocalCS() + * This function is the same as OGRSpatialReference::SetCompoundCS() */ -OGRErr OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char * pszName ) +OGRErr OSRSetCompoundCS( OGRSpatialReferenceH hSRS, + const char *pszName, + OGRSpatialReferenceH hHorizSRS, + OGRSpatialReferenceH hVertSRS ) { - VALIDATE_POINTER1( hSRS, "OSRSetLocalCS", CE_Failure ); + VALIDATE_POINTER1( hSRS, "OSRSetCompoundCS", CE_Failure ); + VALIDATE_POINTER1( hHorizSRS, "OSRSetCompoundCS", CE_Failure ); + VALIDATE_POINTER1( hVertSRS, "OSRSetCompoundCS", CE_Failure ); - return ((OGRSpatialReference *) hSRS)->SetLocalCS( pszName ); + return ((OGRSpatialReference *) hSRS)-> + SetCompoundCS( pszName, + (OGRSpatialReference *) hHorizSRS, + (OGRSpatialReference *) hVertSRS ); } /************************************************************************/ @@ -2449,7 +3253,7 @@ OGRErr OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char * pszName ) * * This method is the same as the C function OSRSetProjCS(). * - * This method is will ensure a PROJCS node is created as the root, + * This method will ensure a PROJCS node is created as the root, * and set the provided name on it. If used on a GEOGCS coordinate system, * the GEOGCS node will be demoted to be a child of the new PROJCS root. * @@ -2559,6 +3363,8 @@ OGRErr OSRSetProjection( OGRSpatialReferenceH hSRS, const char * pszProjection ) { + VALIDATE_POINTER1( hSRS, "OSRSetProjection", CE_Failure ); + return ((OGRSpatialReference *) hSRS)->SetProjection( pszProjection ); } @@ -3600,6 +4406,30 @@ OGRErr OSRSetGH( OGRSpatialReferenceH hSRS, dfFalseEasting, dfFalseNorthing ); } +/************************************************************************/ +/* SetIGH() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetIGH() + +{ + SetProjection( SRS_PT_IGH ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetIGH() */ +/************************************************************************/ + +OGRErr OSRSetIGH( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetIGH", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetIGH(); +} + /************************************************************************/ /* SetGEOS() */ /************************************************************************/ @@ -3669,6 +4499,8 @@ OGRErr OSRSetGaussSchreiberTMercator( OGRSpatialReferenceH hSRS, double dfFalseNorthing ) { + VALIDATE_POINTER1( hSRS, "OSRSetGaussSchreiberTMercator", CE_Failure ); + return ((OGRSpatialReference *) hSRS)->SetGaussSchreiberTMercator( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); @@ -3710,6 +4542,76 @@ OGRErr OSRSetGnomonic( OGRSpatialReferenceH hSRS, dfFalseEasting, dfFalseNorthing ); } +/************************************************************************/ +/* SetHOMAC() */ +/************************************************************************/ + +/** + * \brief Set an Hotine Oblique Mercator Azimuth Center projection using + * azimuth angle. + * + * This projection corresponds to EPSG projection method 9815, also + * sometimes known as hotine oblique mercator (variant B). + * + * This method does the same thing as the C function OSRSetHOMAC(). + * + * @param dfCenterLat Latitude of the projection origin. + * @param dfCenterLong Longitude of the projection origin. + * @param dfAzimuth Azimuth, measured clockwise from North, of the projection + * centerline. + * @param dfRectToSkew ?. + * @param dfScale Scale factor applies to the projection origin. + * @param dfFalseEasting False easting. + * @param dfFalseNorthing False northing. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetHOMAC( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_AZIMUTH, dfAzimuth ); + SetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, dfRectToSkew ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetHOMAC() */ +/************************************************************************/ + +/** + * \brief Set an Oblique Mercator projection using azimuth angle. + * + * This is the same as the C++ method OGRSpatialReference::SetHOMAC() + */ +OGRErr OSRSetHOMAC( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetHOMAC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetHOMAC( + dfCenterLat, dfCenterLong, + dfAzimuth, dfRectToSkew, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + /************************************************************************/ /* SetHOM() */ /************************************************************************/ @@ -3717,6 +4619,9 @@ OGRErr OSRSetGnomonic( OGRSpatialReferenceH hSRS, /** * \brief Set a Hotine Oblique Mercator projection using azimuth angle. * + * This projection corresponds to EPSG projection method 9812, also + * sometimes known as hotine oblique mercator (variant A).. + * * This method does the same thing as the C function OSRSetHOM(). * * @param dfCenterLat Latitude of the projection origin. @@ -4668,6 +5573,10 @@ OGRErr OSRSetUTM( OGRSpatialReferenceH hSRS, int nZone, int bNorth ) * * This is the same as the C function OSRGetUTMZone(). * + * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a + * zone which is negative in the southern hemisphere instead of having the + * pbNorth flag used in the C and C++ interface. + * * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or * FALSE if southern. * @@ -4703,9 +5612,9 @@ int OGRSpatialReference::GetUTMZone( int * pbNorth ) const double dfCentralMeridian = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0); - double dfZone = (dfCentralMeridian+183) / 6.0 + 0.000000001; + double dfZone = ( dfCentralMeridian + 186.0 ) / 6.0; - if( ABS(dfZone - (int) dfZone) > 0.00001 + if( ABS(dfZone - (int) dfZone - 0.5 ) > 0.00001 || dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ) return 0; @@ -5026,6 +5935,8 @@ const char *OSRGetAuthorityName( OGRSpatialReferenceH hSRS, * portion remains (normally PROJCS, GEOGCS or LOCAL_CS). * * If this is not a compound coordinate system then nothing is changed. + * + * @since OGR 1.8.0 */ OGRErr OGRSpatialReference::StripVertical() @@ -5110,6 +6021,44 @@ OGRErr OSRStripCTParms( OGRSpatialReferenceH hSRS ) return ((OGRSpatialReference *) hSRS)->StripCTParms( NULL ); } +/************************************************************************/ +/* IsCompound() */ +/************************************************************************/ + +/** + * \brief Check if coordinate system is compound. + * + * This method is the same as the C function OSRIsCompound(). + * + * @return TRUE if this is rooted with a COMPD_CS node. + */ + +int OGRSpatialReference::IsCompound() const + +{ + if( poRoot == NULL ) + return FALSE; + + return EQUAL(poRoot->GetValue(),"COMPD_CS"); +} + +/************************************************************************/ +/* OSRIsCompound() */ +/************************************************************************/ + +/** + * \brief Check if the coordinate system is compound. + * + * This function is the same as OGRSpatialReference::IsCompound(). + */ +int OSRIsCompound( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsCompound", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsCompound(); +} + /************************************************************************/ /* IsProjected() */ /************************************************************************/ @@ -5153,6 +6102,51 @@ int OSRIsProjected( OGRSpatialReferenceH hSRS ) return ((OGRSpatialReference *) hSRS)->IsProjected(); } +/************************************************************************/ +/* IsGeocentric() */ +/************************************************************************/ + +/** + * \brief Check if geocentric coordinate system. + * + * This method is the same as the C function OSRIsGeocentric(). + * + * @return TRUE if this contains a GEOCCS node indicating a it is a + * geocentric coordinate system. + * + * @since OGR 1.9.0 + */ + +int OGRSpatialReference::IsGeocentric() const + +{ + if( poRoot == NULL ) + return FALSE; + + if( EQUAL(poRoot->GetValue(),"GEOCCS") ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* OSRIsGeocentric() */ +/************************************************************************/ +/** + * \brief Check if geocentric coordinate system. + * + * This function is the same as OGRSpatialReference::IsGeocentric(). + * + * @since OGR 1.9.0 + */ +int OSRIsGeocentric( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsGeocentric", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsGeocentric(); +} + /************************************************************************/ /* IsGeographic() */ /************************************************************************/ @@ -5235,6 +6229,53 @@ int OSRIsLocal( OGRSpatialReferenceH hSRS ) return ((OGRSpatialReference *) hSRS)->IsLocal(); } +/************************************************************************/ +/* IsVertical() */ +/************************************************************************/ + +/** + * \brief Check if vertical coordinate system. + * + * This method is the same as the C function OSRIsVertical(). + * + * @return TRUE if this contains a VERT_CS node indicating a it is a + * vertical coordinate system. + * + * @since OGR 1.8.0 + */ + +int OGRSpatialReference::IsVertical() const + +{ + if( poRoot == NULL ) + return FALSE; + + if( EQUAL(poRoot->GetValue(),"VERT_CS") ) + return TRUE; + else if( EQUAL(poRoot->GetValue(),"COMPD_CS") ) + return GetAttrNode( "VERT_CS" ) != NULL; + else + return FALSE; +} + +/************************************************************************/ +/* OSRIsVertical() */ +/************************************************************************/ +/** + * \brief Check if vertical coordinate system. + * + * This function is the same as OGRSpatialReference::IsVertical(). + * + * @since OGR 1.8.0 + */ +int OSRIsVertical( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsVertical", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsVertical(); +} + /************************************************************************/ /* CloneGeogCS() */ /************************************************************************/ @@ -5250,6 +6291,35 @@ OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const const OGR_SRSNode *poGeogCS; OGRSpatialReference * poNewSRS; +/* -------------------------------------------------------------------- */ +/* We have to reconstruct the GEOGCS node for geocentric */ +/* coordinate systems. */ +/* -------------------------------------------------------------------- */ + if( IsGeocentric() ) + { + const OGR_SRSNode *poDatum = GetAttrNode( "DATUM" ); + const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" ); + OGR_SRSNode *poGeogCS; + + if( poDatum == NULL || poPRIMEM == NULL ) + return NULL; + + poGeogCS = new OGR_SRSNode( "GEOGCS" ); + poGeogCS->AddChild( new OGR_SRSNode( "unnamed" ) ); + poGeogCS->AddChild( poDatum->Clone() ); + poGeogCS->AddChild( poPRIMEM->Clone() ); + + poNewSRS = new OGRSpatialReference(); + poNewSRS->SetRoot( poGeogCS ); + + poNewSRS->SetAngularUnits( "degree", CPLAtof(SRS_UA_DEGREE_CONV) ); + + return poNewSRS; + } + +/* -------------------------------------------------------------------- */ +/* For all others we just search the tree, and duplicate. */ +/* -------------------------------------------------------------------- */ poGeogCS = GetAttrNode( "GEOGCS" ); if( poGeogCS == NULL ) return NULL; @@ -5307,6 +6377,21 @@ int OGRSpatialReference::IsSameGeogCS( const OGRSpatialReference *poOther ) cons && !EQUAL(pszThisValue,pszOtherValue) ) return FALSE; +/* -------------------------------------------------------------------- */ +/* Do the datum TOWGS84 values match if present? */ +/* -------------------------------------------------------------------- */ + double adfTOWGS84[7], adfOtherTOWGS84[7]; + int i; + + this->GetTOWGS84( adfTOWGS84, 7 ); + poOther->GetTOWGS84( adfOtherTOWGS84, 7 ); + + for( i = 0; i < 7; i++ ) + { + if( fabs(adfTOWGS84[i] - adfOtherTOWGS84[i]) > 0.00001 ) + return FALSE; + } + /* -------------------------------------------------------------------- */ /* Do the prime meridians match? If missing assume a value of zero.*/ /* -------------------------------------------------------------------- */ @@ -5324,11 +6409,11 @@ int OGRSpatialReference::IsSameGeogCS( const OGRSpatialReference *poOther ) cons /* -------------------------------------------------------------------- */ /* Do the units match? */ /* -------------------------------------------------------------------- */ - pszThisValue = this->GetAttrValue( "GEOGCS|UNITS", 1 ); + pszThisValue = this->GetAttrValue( "GEOGCS|UNIT", 1 ); if( pszThisValue == NULL ) pszThisValue = SRS_UA_DEGREE_CONV; - pszOtherValue = poOther->GetAttrValue( "GEOGCS|UNITS", 1 ); + pszOtherValue = poOther->GetAttrValue( "GEOGCS|UNIT", 1 ); if( pszOtherValue == NULL ) pszOtherValue = SRS_UA_DEGREE_CONV; @@ -5373,6 +6458,71 @@ int OSRIsSameGeogCS( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 ) (OGRSpatialReference *) hSRS2 ); } +/************************************************************************/ +/* IsSameVertCS() */ +/************************************************************************/ + +/** + * \brief Do the VertCS'es match? + * + * This method is the same as the C function OSRIsSameVertCS(). + * + * @param poOther the SRS being compared against. + * + * @return TRUE if they are the same or FALSE otherwise. + */ + +int OGRSpatialReference::IsSameVertCS( const OGRSpatialReference *poOther ) const + +{ + const char *pszThisValue, *pszOtherValue; + +/* -------------------------------------------------------------------- */ +/* Does the datum name match? */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "VERT_DATUM" ); + pszOtherValue = poOther->GetAttrValue( "VERT_DATUM" ); + + if( pszThisValue == NULL || pszOtherValue == NULL + || !EQUAL(pszThisValue, pszOtherValue) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do the units match? */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "VERT_CS|UNIT", 1 ); + if( pszThisValue == NULL ) + pszThisValue = "1.0"; + + pszOtherValue = poOther->GetAttrValue( "VERT_CS|UNIT", 1 ); + if( pszOtherValue == NULL ) + pszOtherValue = "1.0"; + + if( ABS(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* OSRIsSameVertCS() */ +/************************************************************************/ + +/** + * \brief Do the VertCS'es match? + * + * This function is the same as OGRSpatialReference::IsSameVertCS(). + */ +int OSRIsSameVertCS( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 ) + +{ + VALIDATE_POINTER1( hSRS1, "OSRIsSameVertCS", 0 ); + VALIDATE_POINTER1( hSRS2, "OSRIsSameVertCS", 0 ); + + return ((OGRSpatialReference *) hSRS1)->IsSameVertCS( + (OGRSpatialReference *) hSRS2 ); +} + /************************************************************************/ /* IsSame() */ /************************************************************************/ @@ -5451,6 +6601,12 @@ int OGRSpatialReference::IsSame( const OGRSpatialReference * poOtherSRS ) const } } +/* -------------------------------------------------------------------- */ +/* Compare vertical coordinate system. */ +/* -------------------------------------------------------------------- */ + if( IsVertical() && !IsSameVertCS( poOtherSRS ) ) + return FALSE; + return TRUE; } @@ -5781,14 +6937,17 @@ OGRErr OGRSpatialReference::Fixup() { /* -------------------------------------------------------------------- */ -/* Ensure linear units defaulted to METER if missing for PROJCS */ -/* or LOCAL_CS. */ +/* Ensure linear units defaulted to METER if missing for PROJCS, */ +/* GEOCCS or LOCAL_CS. */ /* -------------------------------------------------------------------- */ const OGR_SRSNode *poCS = GetAttrNode( "PROJCS" ); if( poCS == NULL ) poCS = GetAttrNode( "LOCAL_CS" ); + if( poCS == NULL ) + poCS = GetAttrNode( "GEOCCS" ); + if( poCS != NULL && poCS->FindChild( "UNIT" ) == -1 ) SetLinearUnits( SRS_UL_METER, 1.0 ); @@ -5957,6 +7116,7 @@ void OSRCleanup( void ) { CleanupESRIDatumMappingTable(); CSVDeaccess( NULL ); + OCTCleanupProjMutex(); } /************************************************************************/ @@ -5983,7 +7143,7 @@ void OSRCleanup( void ) const char * OGRSpatialReference::GetAxis( const char *pszTargetKey, int iAxis, - OGRAxisOrientation *peOrientation ) + OGRAxisOrientation *peOrientation ) const { if( peOrientation != NULL ) @@ -6044,6 +7204,12 @@ OGRSpatialReference::GetAxis( const char *pszTargetKey, int iAxis, *peOrientation = OAO_South; else if( EQUAL(pszOrientation,"WEST") ) *peOrientation = OAO_West; + else if( EQUAL(pszOrientation,"UP") ) + *peOrientation = OAO_Up; + else if( EQUAL(pszOrientation,"DOWN") ) + *peOrientation = OAO_Down; + else if( EQUAL(pszOrientation,"OTHER") ) + *peOrientation = OAO_Other; else { CPLDebug( "OSR", "Unrecognised orientation value '%s'.", @@ -6096,6 +7262,12 @@ const char *OSRAxisEnumToName( OGRAxisOrientation eOrientation ) return "SOUTH"; if( eOrientation == OAO_West ) return "WEST"; + if( eOrientation == OAO_Up ) + return "UP"; + if( eOrientation == OAO_Down ) + return "DOWN"; + if( eOrientation == OAO_Other ) + return "OTHER"; return "UNKNOWN"; } diff --git a/ogr/ogrsurface.cpp b/ogr/ogrsurface.cpp index 08cf8f6..657da28 100644 --- a/ogr/ogrsurface.cpp +++ b/ogr/ogrsurface.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrsurface.cpp 16574 2009-03-14 13:09:10Z rouault $ + * $Id: ogrsurface.cpp 19399 2010-04-13 18:35:24Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: The OGRSurface class. @@ -44,22 +44,6 @@ * system in use. */ -/** - * \fn OGRErr OGRSurface::Centroid( OGRPoint * poPoint ) const; - * - * \brief Compute and return centroid of surface. - * The centroid is not necessarily - * within the geometry. - * - * This method relates to the SFCOM ISurface::get_Centroid() method. - * - * NOTE: Only implemented when GEOS included in build. - * - * @param poPoint point to be set with the centroid location. - * - * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. - */ - /** * \fn OGRErr OGRSurface::PointOnSurface( OGRPoint * poPoint ) const; * diff --git a/ogr/ogrunionlayer.cpp b/ogr/ogrunionlayer.cpp new file mode 100644 index 0000000..4e7c723 --- /dev/null +++ b/ogr/ogrunionlayer.cpp @@ -0,0 +1,1272 @@ +/****************************************************************************** + * $Id: ogrunionlayer.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRUnionLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrunionlayer.h" +#include "ogrwarpedlayer.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrunionlayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::OGRUnionLayerGeomFieldDefn(const char* pszName, + OGRwkbGeometryType eType) : + OGRGeomFieldDefn(pszName, eType), bGeomTypeSet(FALSE), bSRSSet(FALSE) +{ +} + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::OGRUnionLayerGeomFieldDefn( + OGRGeomFieldDefn* poSrc) : + OGRGeomFieldDefn(poSrc->GetNameRef(), poSrc->GetType()), + bGeomTypeSet(FALSE), bSRSSet(FALSE) +{ + SetSpatialRef(poSrc->GetSpatialRef()); +} + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::OGRUnionLayerGeomFieldDefn( + OGRUnionLayerGeomFieldDefn* poSrc) : + OGRGeomFieldDefn(poSrc->GetNameRef(), poSrc->GetType()), + bGeomTypeSet(poSrc->bGeomTypeSet), bSRSSet(poSrc->bSRSSet) +{ + SetSpatialRef(poSrc->GetSpatialRef()); + sStaticEnvelope = poSrc->sStaticEnvelope; +} + +/************************************************************************/ +/* ~OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::~OGRUnionLayerGeomFieldDefn() +{ +} + +/************************************************************************/ +/* OGRUnionLayer() */ +/************************************************************************/ + +OGRUnionLayer::OGRUnionLayer( const char* pszName, + int nSrcLayersIn, + OGRLayer** papoSrcLayersIn, + int bTakeLayerOwnership ) +{ + CPLAssert(nSrcLayersIn > 0); + + SetDescription( pszName ); + + osName = pszName; + nSrcLayers = nSrcLayersIn; + papoSrcLayers = papoSrcLayersIn; + bHasLayerOwnership = bTakeLayerOwnership; + + poFeatureDefn = NULL; + nFields = 0; + papoFields = NULL; + nGeomFields = 0; + papoGeomFields = NULL; + eFieldStrategy = FIELD_UNION_ALL_LAYERS; + + bPreserveSrcFID = FALSE; + + nFeatureCount = -1; + + iCurLayer = -1; + pszAttributeFilter = NULL; + nNextFID = 0; + panMap = NULL; + papszIgnoredFields = NULL; + bAttrFilterPassThroughValue = -1; + poGlobalSRS = NULL; + + pabModifiedLayers = (int*)CPLCalloc(sizeof(int), nSrcLayers); + pabCheckIfAutoWrap = (int*)CPLCalloc(sizeof(int), nSrcLayers); +} + +/************************************************************************/ +/* ~OGRUnionLayer() */ +/************************************************************************/ + +OGRUnionLayer::~OGRUnionLayer() +{ + int i; + + if( bHasLayerOwnership ) + { + for(i = 0; i < nSrcLayers; i++) + delete papoSrcLayers[i]; + } + CPLFree(papoSrcLayers); + + for(i = 0; i < nFields; i++) + delete papoFields[i]; + CPLFree(papoFields); + for(i = 0; i < nGeomFields; i++) + delete papoGeomFields[i]; + CPLFree(papoGeomFields); + + CPLFree(pszAttributeFilter); + CPLFree(panMap); + CSLDestroy(papszIgnoredFields); + CPLFree(pabModifiedLayers); + CPLFree(pabCheckIfAutoWrap); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + if( poGlobalSRS != NULL ) + poGlobalSRS->Release(); +} + +/************************************************************************/ +/* SetFields() */ +/************************************************************************/ + +void OGRUnionLayer::SetFields(FieldUnionStrategy eFieldStrategyIn, + int nFieldsIn, + OGRFieldDefn** papoFieldsIn, + int nGeomFieldsIn, + OGRUnionLayerGeomFieldDefn** papoGeomFieldsIn) +{ + CPLAssert(nFields == 0); + CPLAssert(poFeatureDefn == NULL); + + eFieldStrategy = eFieldStrategyIn; + if( nFieldsIn ) + { + nFields = nFieldsIn; + papoFields = (OGRFieldDefn** )CPLMalloc(nFields * sizeof(OGRFieldDefn*)); + for(int i=0;i 0 ) + { + papoGeomFields = (OGRUnionLayerGeomFieldDefn** )CPLMalloc( + nGeomFields * sizeof(OGRUnionLayerGeomFieldDefn*)); + for(int i=0;iGetType() != poSrcFieldDefn->GetType() ) + { + if( poSrcFieldDefn->GetType() == OFTReal && + poFieldDefn->GetType() == OFTInteger) + poFieldDefn->SetType(OFTReal); + else + poFieldDefn->SetType(OFTString); + } + + if( poFieldDefn->GetWidth() != poSrcFieldDefn->GetWidth() || + poFieldDefn->GetPrecision() != poSrcFieldDefn->GetPrecision() ) + { + poFieldDefn->SetWidth(0); + poFieldDefn->SetPrecision(0); + } +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRUnionLayer::GetLayerDefn() +{ + if( poFeatureDefn != NULL ) + return poFeatureDefn; + + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + + int iCompareFirstIndex = 0; + if( osSourceLayerFieldName.size() ) + { + OGRFieldDefn oField(osSourceLayerFieldName, OFTString); + poFeatureDefn->AddFieldDefn(&oField); + iCompareFirstIndex = 1; + } + + if( eFieldStrategy == FIELD_SPECIFIED ) + { + int i; + for(i = 0; i < nFields; i++) + poFeatureDefn->AddFieldDefn(papoFields[i]); + for(i = 0; i < nGeomFields; i++) + { + poFeatureDefn->AddGeomFieldDefn(new OGRUnionLayerGeomFieldDefn(papoGeomFields[i]), FALSE); + OGRUnionLayerGeomFieldDefn* poGeomFieldDefn = + (OGRUnionLayerGeomFieldDefn* ) poFeatureDefn->GetGeomFieldDefn(i); + + if( poGeomFieldDefn->bGeomTypeSet == FALSE || + poGeomFieldDefn->bSRSSet == FALSE ) + { + for(int iLayer = 0; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + int nIndex = + poSrcFeatureDefn->GetGeomFieldIndex(poGeomFieldDefn->GetNameRef()); + if( nIndex >= 0 ) + { + OGRGeomFieldDefn* poSrcGeomFieldDefn = + poSrcFeatureDefn->GetGeomFieldDefn(nIndex); + if( poGeomFieldDefn->bGeomTypeSet == FALSE ) + { + poGeomFieldDefn->bGeomTypeSet = TRUE; + poGeomFieldDefn->SetType(poSrcGeomFieldDefn->GetType()); + } + if( poGeomFieldDefn->bSRSSet == FALSE ) + { + poGeomFieldDefn->bSRSSet = TRUE; + poGeomFieldDefn->SetSpatialRef(poSrcGeomFieldDefn->GetSpatialRef()); + if( i == 0 && poGlobalSRS == NULL ) + { + poGlobalSRS = poSrcGeomFieldDefn->GetSpatialRef(); + if( poGlobalSRS != NULL ) + poGlobalSRS->Reference(); + } + } + break; + } + } + } + } + } + else if( eFieldStrategy == FIELD_FROM_FIRST_LAYER ) + { + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[0]->GetLayerDefn(); + int i; + for(i = 0; i < poSrcFeatureDefn->GetFieldCount(); i++) + poFeatureDefn->AddFieldDefn(poSrcFeatureDefn->GetFieldDefn(i)); + for(i = 0; nGeomFields != - 1 && i < poSrcFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poFldDefn = poSrcFeatureDefn->GetGeomFieldDefn(i); + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(poFldDefn), FALSE); + } + } + else if (eFieldStrategy == FIELD_UNION_ALL_LAYERS ) + { + if( nGeomFields == 1 ) + { + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(papoGeomFields[0]), FALSE); + } + + for(int iLayer = 0; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + + /* Add any field that is found in the source layers */ + int i; + for(i = 0; i < poSrcFeatureDefn->GetFieldCount(); i++) + { + OGRFieldDefn* poSrcFieldDefn = poSrcFeatureDefn->GetFieldDefn(i); + int nIndex = + poFeatureDefn->GetFieldIndex(poSrcFieldDefn->GetNameRef()); + if( nIndex < 0 ) + poFeatureDefn->AddFieldDefn(poSrcFieldDefn); + else + { + OGRFieldDefn* poFieldDefn = + poFeatureDefn->GetFieldDefn(nIndex); + MergeFieldDefn(poFieldDefn, poSrcFieldDefn); + } + } + + for(i = 0; nGeomFields != - 1 && i < poSrcFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poSrcFieldDefn = poSrcFeatureDefn->GetGeomFieldDefn(i); + int nIndex = + poFeatureDefn->GetGeomFieldIndex(poSrcFieldDefn->GetNameRef()); + if( nIndex < 0 ) + { + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(poSrcFieldDefn), FALSE); + if( poFeatureDefn->GetGeomFieldCount() == 1 && nGeomFields == 0 && + GetSpatialRef() != NULL ) + { + OGRUnionLayerGeomFieldDefn* poGeomFieldDefn = + (OGRUnionLayerGeomFieldDefn* ) poFeatureDefn->GetGeomFieldDefn(0); + poGeomFieldDefn->bSRSSet = TRUE; + poGeomFieldDefn->SetSpatialRef(GetSpatialRef()); + } + } + else + { + if( nIndex == 0 && nGeomFields == 1 ) + { + OGRUnionLayerGeomFieldDefn* poGeomFieldDefn = + (OGRUnionLayerGeomFieldDefn* ) poFeatureDefn->GetGeomFieldDefn(0); + if( poGeomFieldDefn->bGeomTypeSet == FALSE ) + { + poGeomFieldDefn->bGeomTypeSet = TRUE; + poGeomFieldDefn->SetType(poSrcFieldDefn->GetType()); + } + if( poGeomFieldDefn->bSRSSet == FALSE ) + { + poGeomFieldDefn->bSRSSet = TRUE; + poGeomFieldDefn->SetSpatialRef(poSrcFieldDefn->GetSpatialRef()); + } + } + /* TODO: merge type, SRS, extent ? */ + } + } + } + } + else if (eFieldStrategy == FIELD_INTERSECTION_ALL_LAYERS ) + { + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[0]->GetLayerDefn(); + int i; + for(i = 0; i < poSrcFeatureDefn->GetFieldCount(); i++) + poFeatureDefn->AddFieldDefn(poSrcFeatureDefn->GetFieldDefn(i)); + for(i = 0; i < poSrcFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poFldDefn = poSrcFeatureDefn->GetGeomFieldDefn(i); + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(poFldDefn), FALSE); + } + + /* Remove any field that is not found in the source layers */ + for(int iLayer = 1; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + for(i = iCompareFirstIndex; i < poFeatureDefn->GetFieldCount();) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(i); + int nSrcIndex = poSrcFeatureDefn->GetFieldIndex( + poFieldDefn->GetNameRef()); + if( nSrcIndex < 0 ) + { + poFeatureDefn->DeleteFieldDefn(i); + } + else + { + OGRFieldDefn* poSrcFieldDefn = + poSrcFeatureDefn->GetFieldDefn(nSrcIndex); + MergeFieldDefn(poFieldDefn, poSrcFieldDefn); + + i ++; + } + } + for(i = 0; i < poFeatureDefn->GetGeomFieldCount();) + { + OGRGeomFieldDefn* poFieldDefn = poFeatureDefn->GetGeomFieldDefn(i); + int nSrcIndex = poSrcFeatureDefn->GetGeomFieldIndex( + poFieldDefn->GetNameRef()); + if( nSrcIndex < 0 ) + { + poFeatureDefn->DeleteGeomFieldDefn(i); + } + else + { + /* TODO: merge type, SRS, extent ? */ + + i ++; + } + } + } + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRUnionLayer::GetGeomType() +{ + if( nGeomFields < 0 ) + return wkbNone; + if( nGeomFields >= 1 && papoGeomFields[0]->bGeomTypeSet ) + { + return papoGeomFields[0]->GetType(); + } + + return OGRLayer::GetGeomType(); +} + +/************************************************************************/ +/* SetSpatialFilterToSourceLayer() */ +/************************************************************************/ + +void OGRUnionLayer::SetSpatialFilterToSourceLayer(OGRLayer* poSrcLayer) +{ + if( m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount() ) + { + int iSrcGeomField = poSrcLayer->GetLayerDefn()->GetGeomFieldIndex( + GetLayerDefn()->GetGeomFieldDefn(m_iGeomFieldFilter)->GetNameRef()); + if( iSrcGeomField >= 0 ) + { + poSrcLayer->SetSpatialFilter(iSrcGeomField, m_poFilterGeom); + } + else + { + poSrcLayer->SetSpatialFilter(NULL); + } + } + else + { + poSrcLayer->SetSpatialFilter(NULL); + } +} + +/************************************************************************/ +/* ConfigureActiveLayer() */ +/************************************************************************/ + +void OGRUnionLayer::ConfigureActiveLayer() +{ + AutoWarpLayerIfNecessary(iCurLayer); + ApplyAttributeFilterToSrcLayer(iCurLayer); + SetSpatialFilterToSourceLayer(papoSrcLayers[iCurLayer]); + papoSrcLayers[iCurLayer]->ResetReading(); + + /* Establish map */ + OGRFeatureDefn* poFeatureDefn = GetLayerDefn(); + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[iCurLayer]->GetLayerDefn(); + CPLFree(panMap); + panMap = (int*) CPLMalloc(poSrcFeatureDefn->GetFieldCount() * sizeof(int)); + for(int i=0; i < poSrcFeatureDefn->GetFieldCount(); i++) + { + OGRFieldDefn* poSrcFieldDefn = poSrcFeatureDefn->GetFieldDefn(i); + if( CSLFindString(papszIgnoredFields, + poSrcFieldDefn->GetNameRef() ) == -1 ) + { + panMap[i] = + poFeatureDefn->GetFieldIndex(poSrcFieldDefn->GetNameRef()); + } + else + { + panMap[i] = -1; + } + } + + if( papoSrcLayers[iCurLayer]->TestCapability(OLCIgnoreFields) ) + { + char** papszIter = papszIgnoredFields; + char** papszFieldsSrc = NULL; + while ( papszIter != NULL && *papszIter != NULL ) + { + const char* pszFieldName = *papszIter; + if ( EQUAL(pszFieldName, "OGR_GEOMETRY") || + EQUAL(pszFieldName, "OGR_STYLE") || + poSrcFeatureDefn->GetFieldIndex(pszFieldName) >= 0 || + poSrcFeatureDefn->GetGeomFieldIndex(pszFieldName) >= 0 ) + { + papszFieldsSrc = CSLAddString(papszFieldsSrc, pszFieldName); + } + papszIter++; + } + + /* Attribute fields */ + int* panSrcFieldsUsed = (int*) CPLCalloc(sizeof(int), + poSrcFeatureDefn->GetFieldCount()); + for(int iField = 0; + iField < poFeatureDefn->GetFieldCount(); iField++) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + int iSrcField = + poSrcFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()); + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + for(int iSrcField = 0; + iSrcField < poSrcFeatureDefn->GetFieldCount(); iSrcField ++) + { + if( !panSrcFieldsUsed[iSrcField] ) + { + OGRFieldDefn *poSrcDefn = + poSrcFeatureDefn->GetFieldDefn( iSrcField ); + papszFieldsSrc = + CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + CPLFree(panSrcFieldsUsed); + + /* geometry fields now */ + panSrcFieldsUsed = (int*) CPLCalloc(sizeof(int), + poSrcFeatureDefn->GetGeomFieldCount()); + for(int iField = 0; + iField < poFeatureDefn->GetGeomFieldCount(); iField++) + { + OGRGeomFieldDefn* poFieldDefn = poFeatureDefn->GetGeomFieldDefn(iField); + int iSrcField = + poSrcFeatureDefn->GetGeomFieldIndex(poFieldDefn->GetNameRef()); + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + for(int iSrcField = 0; + iSrcField < poSrcFeatureDefn->GetGeomFieldCount(); iSrcField ++) + { + if( !panSrcFieldsUsed[iSrcField] ) + { + OGRGeomFieldDefn *poSrcDefn = + poSrcFeatureDefn->GetGeomFieldDefn( iSrcField ); + papszFieldsSrc = + CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + CPLFree(panSrcFieldsUsed); + + papoSrcLayers[iCurLayer]->SetIgnoredFields((const char**)papszFieldsSrc); + + CSLDestroy(papszFieldsSrc); + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRUnionLayer::ResetReading() +{ + iCurLayer = 0; + ConfigureActiveLayer(); + nNextFID = 0; +} + +/************************************************************************/ +/* AutoWarpLayerIfNecessary() */ +/************************************************************************/ + +void OGRUnionLayer::AutoWarpLayerIfNecessary(int iLayer) +{ + if( !pabCheckIfAutoWrap[iLayer] ) + { + pabCheckIfAutoWrap[iLayer] = TRUE; + + for(int i=0; iGetGeomFieldCount();i++) + { + OGRSpatialReference* poSRS = GetLayerDefn()->GetGeomFieldDefn(i)->GetSpatialRef(); + if( poSRS != NULL ) + poSRS->Reference(); + + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[iLayer]->GetLayerDefn(); + int iSrcGeomField = poSrcFeatureDefn->GetGeomFieldIndex( + GetLayerDefn()->GetGeomFieldDefn(i)->GetNameRef()); + if( iSrcGeomField >= 0 ) + { + OGRSpatialReference* poSRS2 = + poSrcFeatureDefn->GetGeomFieldDefn(iSrcGeomField)->GetSpatialRef(); + + if( (poSRS == NULL && poSRS2 != NULL) || + (poSRS != NULL && poSRS2 == NULL) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "SRS of geometry field '%s' layer %s not consistant with UnionLayer SRS", + GetLayerDefn()->GetGeomFieldDefn(i)->GetNameRef(), + papoSrcLayers[iLayer]->GetName()); + } + else if (poSRS != NULL && poSRS2 != NULL && + poSRS != poSRS2 && !poSRS->IsSame(poSRS2)) + { + CPLDebug("VRT", "SRS of geometry field '%s' layer %s not consistant with UnionLayer SRS. " + "Trying auto warping", + GetLayerDefn()->GetGeomFieldDefn(i)->GetNameRef(), + papoSrcLayers[iLayer]->GetName()); + OGRCoordinateTransformation* poCT = + OGRCreateCoordinateTransformation( poSRS2, poSRS ); + OGRCoordinateTransformation* poReversedCT = (poCT != NULL) ? + OGRCreateCoordinateTransformation( poSRS, poSRS2 ) : NULL; + if( poCT != NULL && poReversedCT != NULL ) + papoSrcLayers[iLayer] = new OGRWarpedLayer( + papoSrcLayers[iLayer], iSrcGeomField, TRUE, poCT, poReversedCT); + } + } + + if( poSRS != NULL ) + poSRS->Release(); + } + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRUnionLayer::GetNextFeature() +{ + if( poFeatureDefn == NULL ) GetLayerDefn(); + if( iCurLayer < 0 ) + ResetReading(); + + if( iCurLayer == nSrcLayers ) + return NULL; + + while(TRUE) + { + OGRFeature* poSrcFeature = papoSrcLayers[iCurLayer]->GetNextFeature(); + if( poSrcFeature == NULL ) + { + iCurLayer ++; + if( iCurLayer < nSrcLayers ) + { + ConfigureActiveLayer(); + continue; + } + else + break; + } + + OGRFeature* poFeature = TranslateFromSrcLayer(poSrcFeature); + delete poSrcFeature; + + if( (m_poFilterGeom == NULL || + FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) && + (m_poAttrQuery == NULL || + m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + + delete poFeature; + } + return NULL; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRUnionLayer::GetFeature( long nFeatureId ) +{ + OGRFeature* poFeature = NULL; + + if( !bPreserveSrcFID ) + { + poFeature = OGRLayer::GetFeature(nFeatureId); + } + else + { + int iGeomFieldFilterSave = m_iGeomFieldFilter; + OGRGeometry* poGeomSave = m_poFilterGeom; + m_poFilterGeom = NULL; + SetSpatialFilter(NULL); + + for(int i=0;iGetFeature(nFeatureId); + if( poSrcFeature != NULL ) + { + poFeature = TranslateFromSrcLayer(poSrcFeature); + delete poSrcFeature; + + break; + } + } + + SetSpatialFilter(iGeomFieldFilterSave, poGeomSave); + delete poGeomSave; + + ResetReading(); + } + + return poFeature; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::CreateFeature( OGRFeature* poFeature ) +{ + if( osSourceLayerFieldName.size() == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported when SourceLayerFieldName is not set"); + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() != OGRNullFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported when FID is set"); + return OGRERR_FAILURE; + } + + if( !poFeature->IsFieldSet(0) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported when '%s' field is not set", + osSourceLayerFieldName.c_str()); + return OGRERR_FAILURE; + } + + const char* pszSrcLayerName = poFeature->GetFieldAsString(0); + for(int i=0;iGetName()) == 0) + { + pabModifiedLayers[i] = TRUE; + + OGRFeature* poSrcFeature = + new OGRFeature(papoSrcLayers[i]->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature, TRUE); + OGRErr eErr = papoSrcLayers[i]->CreateFeature(poSrcFeature); + if( eErr == OGRERR_NONE ) + poFeature->SetFID(poSrcFeature->GetFID()); + delete poSrcFeature; + return eErr; + } + } + + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported : '%s' source layer does not exist", + pszSrcLayerName); + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* SetFeature() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SetFeature( OGRFeature* poFeature ) +{ + if( !bPreserveSrcFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when PreserveSrcFID is OFF"); + return OGRERR_FAILURE; + } + + if( osSourceLayerFieldName.size() == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when SourceLayerFieldName is not set"); + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when FID is not set"); + return OGRERR_FAILURE; + } + + if( !poFeature->IsFieldSet(0) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when '%s' field is not set", + osSourceLayerFieldName.c_str()); + return OGRERR_FAILURE; + } + + const char* pszSrcLayerName = poFeature->GetFieldAsString(0); + for(int i=0;iGetName()) == 0) + { + pabModifiedLayers[i] = TRUE; + + OGRFeature* poSrcFeature = + new OGRFeature(papoSrcLayers[i]->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature, TRUE); + poSrcFeature->SetFID(poFeature->GetFID()); + OGRErr eErr = papoSrcLayers[i]->SetFeature(poSrcFeature); + delete poSrcFeature; + return eErr; + } + } + + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported : '%s' source layer does not exist", + pszSrcLayerName); + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRUnionLayer::GetSpatialRef() +{ + if( nGeomFields < 0 ) + return NULL; + if( nGeomFields >= 1 && + papoGeomFields[0]->bSRSSet ) + return papoGeomFields[0]->GetSpatialRef(); + + if( poGlobalSRS == NULL ) + { + poGlobalSRS = papoSrcLayers[0]->GetSpatialRef(); + if( poGlobalSRS != NULL ) + poGlobalSRS->Reference(); + } + return poGlobalSRS; +} + +/************************************************************************/ +/* GetAttrFilterPassThroughValue() */ +/************************************************************************/ + +int OGRUnionLayer::GetAttrFilterPassThroughValue() +{ + if( m_poAttrQuery == NULL ) + return TRUE; + + if( bAttrFilterPassThroughValue >= 0) + return bAttrFilterPassThroughValue; + + char** papszUsedFields = m_poAttrQuery->GetUsedFields(); + int bRet = TRUE; + + for(int iLayer = 0; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + char** papszIter = papszUsedFields; + while( papszIter != NULL && *papszIter != NULL ) + { + int bIsSpecial = FALSE; + for(int i = 0; i < SPECIAL_FIELD_COUNT; i++) + { + if( EQUAL(*papszIter, SpecialFieldNames[i]) ) + { + bIsSpecial = TRUE; + break; + } + } + if( !bIsSpecial && + poSrcFeatureDefn->GetFieldIndex(*papszIter) < 0 ) + { + bRet = FALSE; + break; + } + papszIter ++; + } + } + + CSLDestroy(papszUsedFields); + + bAttrFilterPassThroughValue = bRet; + + return bRet; +} + +/************************************************************************/ +/* ApplyAttributeFilterToSrcLayer() */ +/************************************************************************/ + +void OGRUnionLayer::ApplyAttributeFilterToSrcLayer(int iSubLayer) +{ + CPLAssert(iSubLayer >= 0 && iSubLayer < nSrcLayers); + + if( GetAttrFilterPassThroughValue() ) + papoSrcLayers[iSubLayer]->SetAttributeFilter(pszAttributeFilter); + else + papoSrcLayers[iSubLayer]->SetAttributeFilter(NULL); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +int OGRUnionLayer::GetFeatureCount( int bForce ) +{ + if (nFeatureCount >= 0 && + m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + return nFeatureCount; + } + + if( !GetAttrFilterPassThroughValue() ) + return OGRLayer::GetFeatureCount(bForce); + + int nRet = 0; + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + ApplyAttributeFilterToSrcLayer(i); + SetSpatialFilterToSourceLayer(papoSrcLayers[i]); + nRet += papoSrcLayers[i]->GetFeatureCount(bForce); + } + ResetReading(); + return nRet; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SetAttributeFilter( const char * pszAttributeFilterIn ) +{ + if( pszAttributeFilterIn == NULL && pszAttributeFilter == NULL) + return OGRERR_NONE; + if( pszAttributeFilterIn != NULL && pszAttributeFilter != NULL && + strcmp(pszAttributeFilterIn, pszAttributeFilter) == 0) + return OGRERR_NONE; + + if( poFeatureDefn == NULL ) GetLayerDefn(); + + bAttrFilterPassThroughValue = -1; + + OGRErr eErr = OGRLayer::SetAttributeFilter(pszAttributeFilterIn); + if( eErr != OGRERR_NONE ) + return eErr; + + CPLFree(pszAttributeFilter); + pszAttributeFilter = pszAttributeFilterIn ? + CPLStrdup(pszAttributeFilterIn) : NULL; + + if( iCurLayer >= 0 && iCurLayer < nSrcLayers) + ApplyAttributeFilterToSrcLayer(iCurLayer); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRUnionLayer::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap, OLCFastFeatureCount ) ) + { + if( nFeatureCount >= 0 && + m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + return TRUE; + + if( !GetAttrFilterPassThroughValue() ) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + ApplyAttributeFilterToSrcLayer(i); + SetSpatialFilterToSourceLayer(papoSrcLayers[i]); + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCFastGetExtent ) ) + { + if( nGeomFields >= 1 && + papoGeomFields[0]->sStaticEnvelope.IsInit() ) + return TRUE; + + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCFastSpatialFilter) ) + { + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + ApplyAttributeFilterToSrcLayer(i); + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCStringsAsUTF8) ) + { + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCRandomRead ) ) + { + if( !bPreserveSrcFID ) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCRandomWrite ) ) + { + if( !bPreserveSrcFID || osSourceLayerFieldName.size() == 0) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCSequentialWrite ) ) + { + if( osSourceLayerFieldName.size() == 0) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCIgnoreFields) ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( iGeomField >= 0 && iGeomField < nGeomFields && + papoGeomFields[iGeomField]->sStaticEnvelope.IsInit() ) + { + memcpy(psExtent, &papoGeomFields[iGeomField]->sStaticEnvelope, sizeof(OGREnvelope)); + return OGRERR_NONE; + } + + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return OGRERR_FAILURE; + } + + int bInit = FALSE; + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + int iSrcGeomField = papoSrcLayers[i]->GetLayerDefn()->GetGeomFieldIndex( + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetNameRef()); + if( iSrcGeomField >= 0 ) + { + if( !bInit ) + { + if( papoSrcLayers[i]->GetExtent(iSrcGeomField, psExtent, bForce) == OGRERR_NONE ) + bInit = TRUE; + } + else + { + OGREnvelope sExtent; + if( papoSrcLayers[i]->GetExtent(iSrcGeomField, &sExtent, bForce) == OGRERR_NONE ) + { + psExtent->Merge(sExtent); + } + } + } + } + return (bInit) ? OGRERR_NONE : OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::GetExtent( OGREnvelope *psExtent, int bForce ) +{ + return GetExtent(0, psExtent, bForce); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRUnionLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) +{ + SetSpatialFilter(0, poGeomIn); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRUnionLayer::SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ) +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + if( poGeom != NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + } + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeom ) ) + ResetReading(); + + if( iCurLayer >= 0 && iCurLayer < nSrcLayers) + { + SetSpatialFilterToSourceLayer(papoSrcLayers[iCurLayer]); + } +} + + +/************************************************************************/ +/* TranslateFromSrcLayer() */ +/************************************************************************/ + +OGRFeature* OGRUnionLayer::TranslateFromSrcLayer(OGRFeature* poSrcFeature) +{ + CPLAssert(panMap != NULL); + CPLAssert(iCurLayer >= 0 && iCurLayer < nSrcLayers); + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFrom(poSrcFeature, panMap, TRUE); + + if( osSourceLayerFieldName.size() && + !poFeatureDefn->GetFieldDefn(0)->IsIgnored() ) + { + poFeature->SetField(0, papoSrcLayers[iCurLayer]->GetName()); + } + + for(int i=0;iGetGeomFieldCount();i++) + { + if( poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored() ) + poFeature->SetGeomFieldDirectly(i, NULL); + else + { + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( + poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef()); + } + } + } + + if( bPreserveSrcFID ) + poFeature->SetFID(poSrcFeature->GetFID()); + else + poFeature->SetFID(nNextFID ++); + return poFeature; +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SetIgnoredFields( const char **papszFields ) +{ + OGRErr eErr = OGRLayer::SetIgnoredFields(papszFields); + if( eErr != OGRERR_NONE ) + return eErr; + + CSLDestroy(papszIgnoredFields); + papszIgnoredFields = papszFields ? CSLDuplicate((char**)papszFields) : NULL; + + return eErr; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SyncToDisk() +{ + for(int i = 0; i < nSrcLayers; i++) + { + if (pabModifiedLayers[i]) + { + papoSrcLayers[i]->SyncToDisk(); + pabModifiedLayers[i] = FALSE; + } + } + + return OGRERR_NONE; +} diff --git a/ogr/ogrunionlayer.h b/ogr/ogrunionlayer.h new file mode 100644 index 0000000..5a93946 --- /dev/null +++ b/ogr/ogrunionlayer.h @@ -0,0 +1,153 @@ +/****************************************************************************** + * $Id: ogrunionlayer.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRUnionLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRUNIONLAYER_H_INCLUDED +#define _OGRUNIONLAYER_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn */ +/************************************************************************/ + +class OGRUnionLayerGeomFieldDefn: public OGRGeomFieldDefn +{ + public: + + int bGeomTypeSet; + int bSRSSet; + OGREnvelope sStaticEnvelope; + + OGRUnionLayerGeomFieldDefn(const char* pszName, OGRwkbGeometryType eType); + OGRUnionLayerGeomFieldDefn(OGRGeomFieldDefn* poSrc); + OGRUnionLayerGeomFieldDefn(OGRUnionLayerGeomFieldDefn* poSrc); + ~OGRUnionLayerGeomFieldDefn(); +}; + +/************************************************************************/ +/* OGRUnionLayer */ +/************************************************************************/ + +typedef enum +{ + FIELD_FROM_FIRST_LAYER, + FIELD_UNION_ALL_LAYERS, + FIELD_INTERSECTION_ALL_LAYERS, + FIELD_SPECIFIED, +} FieldUnionStrategy; + +class OGRUnionLayer : public OGRLayer +{ + protected: + CPLString osName; + int nSrcLayers; + OGRLayer **papoSrcLayers; + int bHasLayerOwnership; + + OGRFeatureDefn *poFeatureDefn; + int nFields; + OGRFieldDefn **papoFields; + int nGeomFields; + OGRUnionLayerGeomFieldDefn **papoGeomFields; + FieldUnionStrategy eFieldStrategy; + CPLString osSourceLayerFieldName; + + int bPreserveSrcFID; + + int nFeatureCount; + + int iCurLayer; + char *pszAttributeFilter; + int nNextFID; + int *panMap; + char **papszIgnoredFields; + int bAttrFilterPassThroughValue; + int *pabModifiedLayers; + int *pabCheckIfAutoWrap; + OGRSpatialReference *poGlobalSRS; + + void AutoWarpLayerIfNecessary(int iSubLayer); + OGRFeature *TranslateFromSrcLayer(OGRFeature* poSrcFeature); + void ApplyAttributeFilterToSrcLayer(int iSubLayer); + int GetAttrFilterPassThroughValue(); + void ConfigureActiveLayer(); + void SetSpatialFilterToSourceLayer(OGRLayer* poSrcLayer); + + public: + OGRUnionLayer( const char* pszName, + int nSrcLayers, /* must be >= 1 */ + OGRLayer** papoSrcLayers, /* array itself ownership always transfered, layer ownership depending on bTakeLayerOwnership */ + int bTakeLayerOwnership); + + virtual ~OGRUnionLayer(); + + /* All the following non virtual methods must be called just after the constructor */ + /* and before any virtual method */ + void SetFields(FieldUnionStrategy eFieldStrategy, + int nFields, + OGRFieldDefn** papoFields, /* duplicated by the method */ + int nGeomFields, /* maybe -1 to explicitely disable geometry fields */ + OGRUnionLayerGeomFieldDefn** papoGeomFields /* duplicated by the method */); + void SetSourceLayerFieldName(const char* pszSourceLayerFieldName); + void SetPreserveSrcFID(int bPreserveSrcFID); + void SetFeatureCount(int nFeatureCount); + virtual const char *GetName() { return osName.c_str(); } + virtual OGRwkbGeometryType GetGeomType(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( long nFeatureId ); + + virtual OGRErr CreateFeature( OGRFeature* poFeature ); + + virtual OGRErr SetFeature( OGRFeature* poFeature ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + + virtual void SetSpatialFilter( OGRGeometry * poGeomIn ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + virtual OGRErr SyncToDisk(); +}; + +#endif // _OGRUNIONLAYER_H_INCLUDED diff --git a/ogr/ogrutils.cpp b/ogr/ogrutils.cpp index f0971e2..e598cc1 100644 --- a/ogr/ogrutils.cpp +++ b/ogr/ogrutils.cpp @@ -1,5 +1,5 @@ /****************************************************************************** - * $Id: ogrutils.cpp 17696 2009-09-26 15:56:39Z rouault $ + * $Id: ogrutils.cpp 27384 2014-05-24 12:28:12Z rouault $ * * Project: OpenGIS Simple Features Reference Implementation * Purpose: Utility functions for OGR classes, including some related to @@ -8,6 +8,7 @@ * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -28,6 +29,9 @@ * DEALINGS IN THE SOFTWARE. ****************************************************************************/ +#include "cpl_conv.h" +#include "cpl_vsi.h" + #include #include "ogr_geometry.h" @@ -37,41 +41,122 @@ # include "ogrsf_frmts.h" #endif /* OGR_ENABLED */ -CPL_CVSID("$Id: ogrutils.cpp 17696 2009-09-26 15:56:39Z rouault $"); +CPL_CVSID("$Id: ogrutils.cpp 27384 2014-05-24 12:28:12Z rouault $"); /************************************************************************/ -/* OGRTrimExtraZeros() */ +/* OGRFormatDouble() */ /************************************************************************/ -static void OGRTrimExtraZeros( char *pszTarget ) - +void OGRFormatDouble( char *pszBuffer, int nBufferLen, double dfVal, char chDecimalSep, int nPrecision ) { - int i = 0; - - while( pszTarget[i] != '\0' ) - i++; - -/* -------------------------------------------------------------------- */ -/* Trim trailing 000001's as they are likely roundoff error. */ -/* -------------------------------------------------------------------- */ - if( i > 10 - && pszTarget[i-1] == '1' - && pszTarget[i-2] == '0' - && pszTarget[i-3] == '0' - && pszTarget[i-4] == '0' - && pszTarget[i-5] == '0' - && pszTarget[i-6] == '0' ) + int i; + int nTruncations = 0; + char szFormat[16]; + sprintf(szFormat, "%%.%df", nPrecision); + + int ret = snprintf(pszBuffer, nBufferLen, szFormat, dfVal); + /* Windows CRT doesn't conform with C99 and return -1 when buffer is truncated */ + if (ret >= nBufferLen || ret == -1) { - pszTarget[--i] = '\0'; + snprintf(pszBuffer, nBufferLen, "%s", "too_big"); + return; } -/* -------------------------------------------------------------------- */ -/* Trim trailing zeros. */ -/* -------------------------------------------------------------------- */ - while( i > 2 && pszTarget[i-1] == '0' && pszTarget[i-2] != '.' ) + while(nPrecision > 0) { - pszTarget[--i] = '\0'; + i = 0; + int nCountBeforeDot = 0; + int iDotPos = -1; + while( pszBuffer[i] != '\0' ) + { + if ((pszBuffer[i] == '.' || pszBuffer[i] == ',') && chDecimalSep != '\0') + { + iDotPos = i; + pszBuffer[i] = chDecimalSep; + } + else if (iDotPos < 0 && pszBuffer[i] != '-') + nCountBeforeDot ++; + i++; + } + + /* -------------------------------------------------------------------- */ + /* Trim trailing 00000x's as they are likely roundoff error. */ + /* -------------------------------------------------------------------- */ + if( i > 10 && iDotPos >=0 ) + { + if (/* && pszBuffer[i-1] == '1' &&*/ + pszBuffer[i-2] == '0' + && pszBuffer[i-3] == '0' + && pszBuffer[i-4] == '0' + && pszBuffer[i-5] == '0' + && pszBuffer[i-6] == '0' ) + { + pszBuffer[--i] = '\0'; + } + else if( i - 8 > iDotPos && /* pszBuffer[i-1] == '1' */ + /* && pszBuffer[i-2] == '0' && */ + (nCountBeforeDot >= 4 || pszBuffer[i-3] == '0') + && (nCountBeforeDot >= 5 || pszBuffer[i-4] == '0') + && (nCountBeforeDot >= 6 || pszBuffer[i-5] == '0') + && (nCountBeforeDot >= 7 || pszBuffer[i-6] == '0') + && (nCountBeforeDot >= 8 || pszBuffer[i-7] == '0') + && pszBuffer[i-8] == '0' + && pszBuffer[i-9] == '0') + { + i -= 8; + pszBuffer[i] = '\0'; + } + } + + /* -------------------------------------------------------------------- */ + /* Trim trailing zeros. */ + /* -------------------------------------------------------------------- */ + while( i > 2 && pszBuffer[i-1] == '0' && pszBuffer[i-2] != '.' ) + { + pszBuffer[--i] = '\0'; + } + + /* -------------------------------------------------------------------- */ + /* Detect trailing 99999X's as they are likely roundoff error. */ + /* -------------------------------------------------------------------- */ + if( i > 10 && + iDotPos >= 0 && + nPrecision + nTruncations >= 15) + { + if (/*pszBuffer[i-1] == '9' && */ + pszBuffer[i-2] == '9' + && pszBuffer[i-3] == '9' + && pszBuffer[i-4] == '9' + && pszBuffer[i-5] == '9' + && pszBuffer[i-6] == '9' ) + { + nPrecision --; + nTruncations ++; + sprintf(szFormat, "%%.%df", nPrecision); + snprintf(pszBuffer, nBufferLen, szFormat, dfVal); + continue; + } + else if (i - 9 > iDotPos && /*pszBuffer[i-1] == '9' && */ + /*pszBuffer[i-2] == '9' && */ + (nCountBeforeDot >= 4 || pszBuffer[i-3] == '9') + && (nCountBeforeDot >= 5 || pszBuffer[i-4] == '9') + && (nCountBeforeDot >= 6 || pszBuffer[i-5] == '9') + && (nCountBeforeDot >= 7 || pszBuffer[i-6] == '9') + && (nCountBeforeDot >= 8 || pszBuffer[i-7] == '9') + && pszBuffer[i-8] == '9' + && pszBuffer[i-9] == '9') + { + nPrecision --; + nTruncations ++; + sprintf(szFormat, "%%.%df", nPrecision); + snprintf(pszBuffer, nBufferLen, szFormat, dfVal); + continue; + } + } + + break; } + } /************************************************************************/ @@ -89,62 +174,71 @@ void OGRMakeWktCoordinate( char *pszTarget, double x, double y, double z, int nDimension ) { - const size_t bufSize = 400; - const size_t maxTargetSize= 75; /* Assumed max length of the target buffer. */ + const size_t bufSize = 75; + const size_t maxTargetSize = 75; /* Assumed max length of the target buffer. */ char szX[bufSize]; char szY[bufSize]; char szZ[bufSize]; - memset( szX, '\0', bufSize ); - memset( szY, '\0', bufSize ); - memset( szZ, '\0', bufSize ); + szZ[0] = '\0'; - if( x == (int) x && y == (int) y && z == (int) z ) + int nLenX, nLenY; + + if( x == (int) x && y == (int) y ) { snprintf( szX, bufSize, "%d", (int) x ); - snprintf( szY, bufSize, " %d", (int) y ); + snprintf( szY, bufSize, "%d", (int) y ); } else { - snprintf( szX, bufSize, "%.15f", x ); - OGRTrimExtraZeros( szX ); - - snprintf( szY, bufSize, " %.15f", y ); - OGRTrimExtraZeros( szY ); + OGRFormatDouble( szX, bufSize, x, '.' ); + OGRFormatDouble( szY, bufSize, y, '.' ); } + nLenX = strlen(szX); + nLenY = strlen(szY); + if( nDimension == 3 ) { if( z == (int) z ) { - snprintf( szZ, bufSize, " %d", (int) z ); + snprintf( szZ, bufSize, "%d", (int) z ); } else { - snprintf( szZ, bufSize, " %.15f", z ); - OGRTrimExtraZeros( szZ ); + OGRFormatDouble( szZ, bufSize, z, '.' ); } } - if( strlen(szX) + strlen(szY) + strlen(szZ) > maxTargetSize ) + if( nLenX + 1 + nLenY + ((nDimension == 3) ? (1 + strlen(szZ)) : 0) >= maxTargetSize ) { - strcpy( szX, "0" ); - strcpy( szY, " 0" ); - if( nDimension == 3 ) - strcpy( szZ, " 0" ); - #ifdef DEBUG CPLDebug( "OGR", "Yow! Got this big result in OGRMakeWktCoordinate()\n" "%s %s %s", szX, szY, szZ ); #endif + if( nDimension == 3 ) + strcpy( pszTarget, "0 0 0"); + else + strcpy( pszTarget, "0 0"); + } + else + { + memcpy( pszTarget, szX, nLenX ); + pszTarget[nLenX] = ' '; + memcpy( pszTarget + nLenX + 1, szY, nLenY ); + if (nDimension == 3) + { + pszTarget[nLenX + 1 + nLenY] = ' '; + strcpy( pszTarget + nLenX + 1 + nLenY + 1, szZ ); + } + else + { + pszTarget[nLenX + 1 + nLenY] = '\0'; + } } - - strcpy( pszTarget, szX ); - strcat( pszTarget, szY ); - strcat( pszTarget, szZ ); } /************************************************************************/ @@ -287,8 +381,8 @@ const char * OGRWktReadPoints( const char * pszInput, /* -------------------------------------------------------------------- */ /* Add point to list. */ /* -------------------------------------------------------------------- */ - (*ppaoPoints)[*pnPointsRead].x = atof(szTokenX); - (*ppaoPoints)[*pnPointsRead].y = atof(szTokenY); + (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX); + (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY); /* -------------------------------------------------------------------- */ /* Do we have a Z coordinate? */ @@ -302,10 +396,12 @@ const char * OGRWktReadPoints( const char * pszInput, *ppadfZ = (double *) CPLCalloc(sizeof(double),*pnMaxPoints); } - (*ppadfZ)[*pnPointsRead] = atof(szDelim); - + (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim); + pszInput = OGRWktReadToken( pszInput, szDelim ); } + else if ( *ppadfZ != NULL ) + (*ppadfZ)[*pnPointsRead] = 0.0; (*pnPointsRead)++; @@ -391,10 +487,15 @@ void OGRFree( void * pMemory ) * options for all OGR commandline utilities. It takes care of the following * commandline options: * + * --version: report version of GDAL in use. + * --license: report GDAL license info. + * --format [format]: report details of one format driver. * --formats: report all format drivers configured. * --optfile filename: expand an option file into the argument list. * --config key value: set system configuration option. * --debug [on/off/value]: set debug level. + * --pause: Pause for user input (allows time to attach debugger) + * --locale [locale]: Install a locale using setlocale() (debugging) * --help-general: report detailed help on general options. * * The argument array is replaced "in place" and should be freed with @@ -411,7 +512,8 @@ void OGRFree( void * pMemory ) * exit( -argc ); * * @param nArgc number of values in the argument list. - * @param Pointer to the argument list array (will be updated in place). + * @param ppapszArgv pointer to the argument list array (will be updated in place). + * @param nOptions unused. * * @return updated nArgc argument count. Return of 0 requests terminate * without error, return of -1 requests exit with error code. @@ -420,219 +522,8 @@ void OGRFree( void * pMemory ) int OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions ) { - char **papszReturn = NULL; - int iArg; - char **papszArgv = *ppapszArgv; - (void) nOptions; - -/* -------------------------------------------------------------------- */ -/* Preserve the program name. */ -/* -------------------------------------------------------------------- */ - papszReturn = CSLAddString( papszReturn, papszArgv[0] ); - -/* ==================================================================== */ -/* Loop over all arguments. */ -/* ==================================================================== */ - for( iArg = 1; iArg < nArgc; iArg++ ) - { - -/* -------------------------------------------------------------------- */ -/* --config */ -/* -------------------------------------------------------------------- */ - if( EQUAL(papszArgv[iArg],"--config") ) - { - if( iArg + 2 >= nArgc ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "--config option given without a key and value argument." ); - CSLDestroy( papszReturn ); - return -1; - } - - CPLSetConfigOption( papszArgv[iArg+1], papszArgv[iArg+2] ); - - iArg += 2; - } - -/* -------------------------------------------------------------------- */ -/* --mempreload */ -/* -------------------------------------------------------------------- */ - else if( EQUAL(papszArgv[iArg],"--mempreload") ) - { - int i; - - if( iArg + 1 >= nArgc ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "--mempreload option given without directory path."); - CSLDestroy( papszReturn ); - return -1; - } - - char **papszFiles = CPLReadDir( papszArgv[iArg+1] ); - if( CSLCount(papszFiles) == 0 ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "--mempreload given invalid or empty directory."); - CSLDestroy( papszReturn ); - return -1; - } - - for( i = 0; papszFiles[i] != NULL; i++ ) - { - CPLString osOldPath, osNewPath; - - if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") ) - continue; - - osOldPath = CPLFormFilename( papszArgv[iArg+1], - papszFiles[i], NULL ); - osNewPath.Printf( "/vsimem/%s", papszFiles[i] ); - - CPLDebug( "VSI", "Preloading %s to %s.", - osOldPath.c_str(), osNewPath.c_str() ); - - if( CPLCopyFile( osNewPath, osOldPath ) != 0 ) - return -1; - } - - CSLDestroy( papszFiles ); - iArg += 1; - } - -/* -------------------------------------------------------------------- */ -/* --debug */ -/* -------------------------------------------------------------------- */ - else if( EQUAL(papszArgv[iArg],"--debug") ) - { - if( iArg + 1 >= nArgc ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "--debug option given without debug level." ); - CSLDestroy( papszReturn ); - return -1; - } - - CPLSetConfigOption( "CPL_DEBUG", papszArgv[iArg+1] ); - iArg += 1; - } - -/* -------------------------------------------------------------------- */ -/* --optfile */ -/* */ -/* Annoyingly the options inserted by --optfile will *not* be */ -/* processed properly if they are general options. */ -/* -------------------------------------------------------------------- */ - else if( EQUAL(papszArgv[iArg],"--optfile") ) - { - const char *pszLine; - FILE *fpOptFile; - - if( iArg + 1 >= nArgc ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "--optfile option given without filename." ); - CSLDestroy( papszReturn ); - return -1; - } - - fpOptFile = VSIFOpen( papszArgv[iArg+1], "rb" ); - - if( fpOptFile == NULL ) - { - CPLError( CE_Failure, CPLE_AppDefined, - "Unable to open optfile '%s'.\n%s", - papszArgv[iArg+1], VSIStrerror( errno ) ); - CSLDestroy( papszReturn ); - return -1; - } - - while( (pszLine = CPLReadLine( fpOptFile )) != NULL ) - { - char **papszTokens; - int i; - - if( pszLine[0] == '#' || strlen(pszLine) == 0 ) - continue; - - papszTokens = CSLTokenizeString( pszLine ); - for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++) - papszReturn = CSLAddString( papszReturn, papszTokens[i] ); - CSLDestroy( papszTokens ); - } - - VSIFClose( fpOptFile ); - - iArg += 1; - } - -/* -------------------------------------------------------------------- */ -/* --formats */ -/* -------------------------------------------------------------------- */ -#ifdef OGR_ENABLED - else if( EQUAL(papszArgv[iArg], "--formats") ) - { - int iDr; - - printf( "Supported Formats:\n" ); - - OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar(); - - for( iDr = 0; iDr < poR->GetDriverCount(); iDr++ ) - { - OGRSFDriver *poDriver = poR->GetDriver(iDr); - - if( poDriver->TestCapability( ODrCCreateDataSource ) ) - printf( " -> \"%s\" (read/write)\n", - poDriver->GetName() ); - else - printf( " -> \"%s\" (readonly)\n", - poDriver->GetName() ); - } - - CSLDestroy( papszReturn ); - return 0; - } -#endif /* OGR_ENABLED */ - -/* -------------------------------------------------------------------- */ -/* --locale */ -/* -------------------------------------------------------------------- */ - else if( EQUAL(papszArgv[iArg],"--locale") && iArg < nArgc-1 ) - { - setlocale( LC_ALL, papszArgv[++iArg] ); - } - -/* -------------------------------------------------------------------- */ -/* --help-general */ -/* -------------------------------------------------------------------- */ - else if( EQUAL(papszArgv[iArg],"--help-general") ) - { - printf( "Generic GDAL/OGR utility command options:\n" ); -#ifdef OGR_ENABLED - printf( " --formats: report all configured format drivers.\n" ); -#endif /* OGR_ENABLED */ - printf( " --optfile filename: expand an option file into the argument list.\n" ); - printf( " --config key value: set system configuration option.\n" ); - printf( " --debug [on/off/value]: set debug level.\n" ); - printf( " --help-general: report detailed help on general options.\n" ); - CSLDestroy( papszReturn ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* carry through unrecognised options. */ -/* -------------------------------------------------------------------- */ - else - { - papszReturn = CSLAddString( papszReturn, papszArgv[iArg] ); - } - } - - *ppapszArgv = papszReturn; - - return CSLCount( *ppapszArgv ); + return GDALGeneralCmdLineProcessor( nArgc, ppapszArgv, GDAL_OF_VECTOR ); } /************************************************************************/ @@ -690,7 +581,14 @@ int OGRParseDate( const char *pszInput, OGRField *psField, int nOptions ) if( strstr(pszInput,"-") != NULL || strstr(pszInput,"/") != NULL ) { - psField->Date.Year = (GInt16)atoi(pszInput); + int nYear = atoi(pszInput); + if( nYear != (GInt16)nYear ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Years < -32768 or > 32767 are not supported"); + return FALSE; + } + psField->Date.Year = (GInt16)nYear; if( psField->Date.Year < 100 && psField->Date.Year >= 30 ) psField->Date.Year += 1900; else if( psField->Date.Year < 30 && psField->Date.Year >= 0 ) @@ -854,6 +752,13 @@ int OGRParseXMLDateTime( const char* pszXMLDateTime, TZ = 0; bRet = TRUE; } + /* Date is expressed as a UTC date with only year:month:day */ + else if (sscanf(pszXMLDateTime, "%04d-%02d-%02d", &year, &month, &day) == 3) + { + TZ = 0; + bRet = TRUE; + } + if (bRet) { if (pnYear) *pnYear = year; @@ -1093,6 +998,48 @@ char* OGRGetXML_UTF8_EscapedString(const char* pszString) return pszEscaped; } +/************************************************************************/ +/* OGRCompareDate() */ +/************************************************************************/ + +int OGRCompareDate( OGRField *psFirstTuple, + OGRField *psSecondTuple ) +{ + /* FIXME? : We ignore TZFlag */ + + if (psFirstTuple->Date.Year < psSecondTuple->Date.Year) + return -1; + else if (psFirstTuple->Date.Year > psSecondTuple->Date.Year) + return 1; + + if (psFirstTuple->Date.Month < psSecondTuple->Date.Month) + return -1; + else if (psFirstTuple->Date.Month > psSecondTuple->Date.Month) + return 1; + + if (psFirstTuple->Date.Day < psSecondTuple->Date.Day) + return -1; + else if (psFirstTuple->Date.Day > psSecondTuple->Date.Day) + return 1; + + if (psFirstTuple->Date.Hour < psSecondTuple->Date.Hour) + return -1; + else if (psFirstTuple->Date.Hour > psSecondTuple->Date.Hour) + return 1; + + if (psFirstTuple->Date.Minute < psSecondTuple->Date.Minute) + return -1; + else if (psFirstTuple->Date.Minute > psSecondTuple->Date.Minute) + return 1; + + if (psFirstTuple->Date.Second < psSecondTuple->Date.Second) + return -1; + else if (psFirstTuple->Date.Second > psSecondTuple->Date.Second) + return 1; + + return 0; +} + /************************************************************************/ /* OGRFastAtof() */ /************************************************************************/ @@ -1112,6 +1059,7 @@ double OGRCallAtofOnShortString(const char* pszStr) while(*p == '+' || *p == '-' || (*p >= '0' && *p <= '9') || + *p == '.' || (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')) { szTemp[nCounter++] = *(p++); @@ -1191,3 +1139,104 @@ double OGRFastAtof(const char* pszStr) } } } + +/** + * Check that panPermutation is a permutation of [0,nSize-1]. + * @param panPermutation an array of nSize elements. + * @param nSize size of the array. + * @return OGRERR_NONE if panPermutation is a permutation of [0,nSize-1]. + * @since OGR 1.9.0 + */ +OGRErr OGRCheckPermutation(int* panPermutation, int nSize) +{ + OGRErr eErr = OGRERR_NONE; + int* panCheck = (int*)CPLCalloc(nSize, sizeof(int)); + int i; + for(i=0;i= nSize) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Bad value for element %d", i); + eErr = OGRERR_FAILURE; + break; + } + if (panCheck[panPermutation[i]] != 0) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Array is not a permutation of [0,%d]", + nSize - 1); + eErr = OGRERR_FAILURE; + break; + } + panCheck[panPermutation[i]] = 1; + } + CPLFree(panCheck); + return eErr; +} + + +OGRErr OGRReadWKBGeometryType( unsigned char * pabyData, OGRwkbGeometryType *peGeometryType, OGRBoolean *pbIs3D ) +{ + if ( ! (peGeometryType && pbIs3D) ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Get the byte order byte. */ +/* -------------------------------------------------------------------- */ + OGRwkbByteOrder eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData); + if (!( eByteOrder == wkbXDR || eByteOrder == wkbNDR )) + return OGRERR_CORRUPT_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the geometry feature type. For now we assume that */ +/* geometry type is between 0 and 255 so we only have to fetch */ +/* one byte. */ +/* -------------------------------------------------------------------- */ + int bIs3D = FALSE; + int iRawType; + + memcpy(&iRawType, pabyData + 1, 4); + if ( OGR_SWAP(eByteOrder)) + { + CPL_SWAP32PTR(&iRawType); + } + + /* Old-style OGC z-bit is flipped? */ + if ( wkb25DBit & iRawType ) + { + /* Clean off top 3 bytes */ + iRawType &= 0x000000FF; + bIs3D = TRUE; + } + + /* ISO SQL/MM style Z types (between 1001 and 1007)? */ + if ( iRawType >= 1001 && iRawType <= 1007 ) + { + /* Remove the ISO padding */ + iRawType -= 1000; + bIs3D = TRUE; + } + + /* Sometimes the Z flag is in the 2nd byte? */ + if ( iRawType & (wkb25DBit >> 16) ) + { + /* Clean off top 3 bytes */ + iRawType &= 0x000000FF; + bIs3D = TRUE; + } + + /* Nothing left but (hopefully) basic 2D types */ + + /* What if what we have is still out of range? */ + if ( iRawType < 1 || iRawType > (int)wkbGeometryCollection ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported WKB type %d", iRawType); + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + *pbIs3D = bIs3D; + *peGeometryType = (OGRwkbGeometryType)iRawType; + + return OGRERR_NONE; +} diff --git a/ogr/ogrwarpedlayer.cpp b/ogr/ogrwarpedlayer.cpp new file mode 100644 index 0000000..107f5da --- /dev/null +++ b/ogr/ogrwarpedlayer.cpp @@ -0,0 +1,572 @@ +/****************************************************************************** + * $Id: ogrwarpedlayer.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRWarpedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwarpedlayer.h" + +CPL_CVSID("$Id: ogrwarpedlayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRWarpedLayer() */ +/************************************************************************/ + +OGRWarpedLayer::OGRWarpedLayer( OGRLayer* poDecoratedLayer, + int iGeomField, + int bTakeOwnership, + OGRCoordinateTransformation* poCT, + OGRCoordinateTransformation* poReversedCT ) : + OGRLayerDecorator(poDecoratedLayer, + bTakeOwnership), + m_iGeomField(iGeomField), + m_poCT(poCT), + m_poReversedCT(poReversedCT) +{ + CPLAssert(poCT != NULL); + SetDescription( poDecoratedLayer->GetDescription() ); + + m_poFeatureDefn = NULL; + + if( m_poCT->GetTargetCS() != NULL ) + { + m_poSRS = m_poCT->GetTargetCS(); + m_poSRS->Reference(); + } + else + m_poSRS = NULL; +} + +/************************************************************************/ +/* ~OGRWarpedLayer() */ +/************************************************************************/ + +OGRWarpedLayer::~OGRWarpedLayer() +{ + if( m_poFeatureDefn != NULL ) + m_poFeatureDefn->Release(); + if( m_poSRS != NULL ) + m_poSRS->Release(); + delete m_poCT; + delete m_poReversedCT; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + SetSpatialFilter( 0, poGeom ); +} + +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + OGRLayer::SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ) +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeom ) ) + ResetReading(); + + if( m_iGeomFieldFilter == m_iGeomField ) + { + if( poGeom == NULL || m_poReversedCT == NULL ) + { + m_poDecoratedLayer->SetSpatialFilter(m_iGeomFieldFilter, + NULL); + } + else + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + if( CPLIsInf(sEnvelope.MinX) && CPLIsInf(sEnvelope.MinY) && + CPLIsInf(sEnvelope.MaxX) && CPLIsInf(sEnvelope.MaxY) ) + { + m_poDecoratedLayer->SetSpatialFilterRect(m_iGeomFieldFilter, + sEnvelope.MinX, + sEnvelope.MinY, + sEnvelope.MaxX, + sEnvelope.MaxY); + } + else if( ReprojectEnvelope(&sEnvelope, m_poReversedCT) ) + { + m_poDecoratedLayer->SetSpatialFilterRect(m_iGeomFieldFilter, + sEnvelope.MinX, + sEnvelope.MinY, + sEnvelope.MaxX, + sEnvelope.MaxY); + } + else + { + m_poDecoratedLayer->SetSpatialFilter(m_iGeomFieldFilter, + NULL); + } + } + } + else + { + m_poDecoratedLayer->SetSpatialFilter(m_iGeomFieldFilter, + poGeom); + } +} + +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + OGRLayer::SetSpatialFilterRect(iGeomField, dfMinX, dfMinY, dfMaxX, dfMaxY); +} + + +/************************************************************************/ +/* SrcFeatureToWarpedFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::SrcFeatureToWarpedFeature(OGRFeature* poSrcFeature) +{ + OGRFeature* poFeature = new OGRFeature(GetLayerDefn()); + poFeature->SetFrom(poSrcFeature); + poFeature->SetFID(poSrcFeature->GetFID()); + + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(m_iGeomField); + if( poGeom == NULL ) + return poFeature; + + if( poGeom->transform(m_poCT) != OGRERR_NONE ) + { + delete poFeature->StealGeometry(m_iGeomField); + } + + return poFeature; +} + + +/************************************************************************/ +/* WarpedFeatureToSrcFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::WarpedFeatureToSrcFeature(OGRFeature* poFeature) +{ + OGRFeature* poSrcFeature = new OGRFeature(m_poDecoratedLayer->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature); + poSrcFeature->SetFID(poFeature->GetFID()); + + OGRGeometry* poGeom = poSrcFeature->GetGeomFieldRef(m_iGeomField); + if( poGeom != NULL ) + { + if( m_poReversedCT == NULL ) + { + delete poSrcFeature; + return NULL; + } + + if( poGeom->transform(m_poReversedCT) != OGRERR_NONE ) + { + delete poSrcFeature; + return NULL; + } + } + + return poSrcFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::GetNextFeature() +{ + while(TRUE) + { + OGRFeature* poFeature = m_poDecoratedLayer->GetNextFeature(); + if( poFeature == NULL ) + return NULL; + + OGRFeature* poFeatureNew = SrcFeatureToWarpedFeature(poFeature); + delete poFeature; + + OGRGeometry* poGeom = poFeatureNew->GetGeomFieldRef(m_iGeomField); + if( m_poFilterGeom != NULL && !FilterGeometry( poGeom ) ) + { + delete poFeatureNew; + continue; + } + + return poFeatureNew; + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::GetFeature( long nFID ) +{ + OGRFeature* poFeature = m_poDecoratedLayer->GetFeature(nFID); + if( poFeature != NULL ) + { + OGRFeature* poFeatureNew = SrcFeatureToWarpedFeature(poFeature); + delete poFeature; + poFeature = poFeatureNew; + } + return poFeature; +} + +/************************************************************************/ +/* SetFeature() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::SetFeature( OGRFeature *poFeature ) +{ + OGRErr eErr; + + OGRFeature* poFeatureNew = WarpedFeatureToSrcFeature(poFeature); + if( poFeatureNew == NULL ) + return OGRERR_FAILURE; + + eErr = m_poDecoratedLayer->SetFeature(poFeatureNew); + + delete poFeatureNew; + + return eErr; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::CreateFeature( OGRFeature *poFeature ) +{ + OGRErr eErr; + + OGRFeature* poFeatureNew = WarpedFeatureToSrcFeature(poFeature); + if( poFeatureNew == NULL ) + return OGRERR_FAILURE; + + eErr = m_poDecoratedLayer->CreateFeature(poFeatureNew); + + delete poFeatureNew; + + return eErr; +} + + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRWarpedLayer::GetLayerDefn() +{ + if( m_poFeatureDefn != NULL ) + return m_poFeatureDefn; + + m_poFeatureDefn = m_poDecoratedLayer->GetLayerDefn()->Clone(); + m_poFeatureDefn->Reference(); + if( m_poFeatureDefn->GetGeomFieldCount() > 0 ) + m_poFeatureDefn->GetGeomFieldDefn(m_iGeomField)->SetSpatialRef(m_poSRS); + + return m_poFeatureDefn; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRWarpedLayer::GetSpatialRef() +{ + if( m_iGeomField == 0 ) + return m_poSRS; + else + return OGRLayer::GetSpatialRef(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +int OGRWarpedLayer::GetFeatureCount( int bForce ) +{ + if( m_poFilterGeom == NULL ) + return m_poDecoratedLayer->GetFeatureCount(bForce); + + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + return GetExtent(0, psExtent, bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( iGeomField == m_iGeomField ) + { + if( sStaticEnvelope.IsInit() ) + { + memcpy(psExtent, &sStaticEnvelope, sizeof(OGREnvelope)); + return OGRERR_NONE; + } + + OGREnvelope sExtent; + OGRErr eErr = m_poDecoratedLayer->GetExtent(m_iGeomField, &sExtent, bForce); + if( eErr != OGRERR_NONE ) + return eErr; + + if( ReprojectEnvelope(&sExtent, m_poCT) ) + { + memcpy(psExtent, &sExtent, sizeof(OGREnvelope)); + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; + } + else + return m_poDecoratedLayer->GetExtent(iGeomField, psExtent, bForce); +} + +/************************************************************************/ +/* TransformAndUpdateBBAndReturnX() */ +/************************************************************************/ + +static double TransformAndUpdateBBAndReturnX( + OGRCoordinateTransformation* poCT, + double dfX, double dfY, + double& dfMinX, double& dfMinY, double& dfMaxX, double& dfMaxY) +{ + int bSuccess; + poCT->TransformEx( 1, &dfX, &dfY, NULL, &bSuccess ); + if( bSuccess ) + { + if( dfX < dfMinX ) dfMinX = dfX; + if( dfY < dfMinY ) dfMinY = dfY; + if( dfX > dfMaxX ) dfMaxX = dfX; + if( dfY > dfMaxY ) dfMaxY = dfY; + return dfX; + } + else + return 0.0; +} + +/************************************************************************/ +/* FindXDiscontinuity() */ +/************************************************************************/ + +static void FindXDiscontinuity(OGRCoordinateTransformation* poCT, + double dfX1, double dfX2, double dfY, + double& dfMinX, double& dfMinY, double& dfMaxX, double& dfMaxY, + int nRecLevel = 0) +{ + double dfXMid = (dfX1 + dfX2) / 2; + + double dfWrkX1 = TransformAndUpdateBBAndReturnX(poCT, dfX1, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY); + double dfWrkXMid = TransformAndUpdateBBAndReturnX(poCT, dfXMid, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY); + double dfWrkX2 = TransformAndUpdateBBAndReturnX(poCT, dfX2, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY); + + double dfDX1 = dfWrkXMid - dfWrkX1; + double dfDX2 = dfWrkX2 - dfWrkXMid; + + if( dfDX1 * dfDX2 < 0 && nRecLevel < 30) + { + FindXDiscontinuity(poCT, dfX1, dfXMid, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY, nRecLevel + 1); + FindXDiscontinuity(poCT, dfXMid, dfX2, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY, nRecLevel + 1); + } +} + +/************************************************************************/ +/* ReprojectEnvelope() */ +/************************************************************************/ + +int OGRWarpedLayer::ReprojectEnvelope( OGREnvelope* psEnvelope, + OGRCoordinateTransformation* poCT ) +{ +#define NSTEP 20 + double dfXStep = (psEnvelope->MaxX - psEnvelope->MinX) / NSTEP; + double dfYStep = (psEnvelope->MaxY - psEnvelope->MinY) / NSTEP; + int i, j; + double *padfX, *padfY; + int* pabSuccess; + + padfX = (double*) VSIMalloc((NSTEP + 1) * (NSTEP + 1) * sizeof(double)); + padfY = (double*) VSIMalloc((NSTEP + 1) * (NSTEP + 1) * sizeof(double)); + pabSuccess = (int*) VSIMalloc((NSTEP + 1) * (NSTEP + 1) * sizeof(int)); + if( padfX == NULL || padfY == NULL || pabSuccess == NULL) + { + VSIFree(padfX); + VSIFree(padfY); + VSIFree(pabSuccess); + return FALSE; + } + + for(j = 0; j <= NSTEP; j++) + { + for(i = 0; i <= NSTEP; i++) + { + padfX[j * (NSTEP + 1) + i] = psEnvelope->MinX + i * dfXStep; + padfY[j * (NSTEP + 1) + i] = psEnvelope->MinY + j * dfYStep; + } + } + + int bRet = FALSE; + + if( poCT->TransformEx( (NSTEP + 1) * (NSTEP + 1), padfX, padfY, NULL, + pabSuccess ) ) + { + double dfMinX = 0.0, dfMinY = 0.0, dfMaxX = 0.0, dfMaxY = 0.0; + int bSet = FALSE; + for(j = 0; j <= NSTEP; j++) + { + double dfXOld = 0.0; + double dfDXOld = 0.0; + int iOld = -1, iOldOld = -1; + for(i = 0; i <= NSTEP; i++) + { + if( pabSuccess[j * (NSTEP + 1) + i] ) + { + double dfX = padfX[j * (NSTEP + 1) + i]; + double dfY = padfY[j * (NSTEP + 1) + i]; + + if( !bSet ) + { + dfMinX = dfMaxX = dfX; + dfMinY = dfMaxY = dfY; + bSet = TRUE; + } + else + { + if( dfX < dfMinX ) dfMinX = dfX; + if( dfY < dfMinY ) dfMinY = dfY; + if( dfX > dfMaxX ) dfMaxX = dfX; + if( dfY > dfMaxY ) dfMaxY = dfY; + } + + if( iOld >= 0 ) + { + double dfDXNew = dfX - dfXOld; + if( iOldOld >= 0 && dfDXNew * dfDXOld < 0 ) + { + FindXDiscontinuity(poCT, + psEnvelope->MinX + iOldOld * dfXStep, + psEnvelope->MinX + i * dfXStep, + psEnvelope->MinY + j * dfYStep, + dfMinX, dfMinY, dfMaxX, dfMaxY); + } + dfDXOld = dfDXNew; + } + + dfXOld = dfX; + iOldOld = iOld; + iOld = i; + + } + } + } + if( bSet ) + { + psEnvelope->MinX = dfMinX; + psEnvelope->MinY = dfMinY; + psEnvelope->MaxX = dfMaxX; + psEnvelope->MaxY = dfMaxY; + bRet = TRUE; + } + } + + VSIFree(padfX); + VSIFree(padfY); + VSIFree(pabSuccess); + + return bRet; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWarpedLayer::TestCapability( const char * pszCapability ) +{ + if( EQUAL(pszCapability, OLCFastGetExtent) && + sStaticEnvelope.IsInit() ) + return TRUE; + + int bVal = m_poDecoratedLayer->TestCapability(pszCapability); + + if( EQUAL(pszCapability, OLCFastSpatialFilter) || + EQUAL(pszCapability, OLCRandomWrite) || + EQUAL(pszCapability, OLCSequentialWrite) ) + { + if( bVal ) + bVal = m_poReversedCT != NULL; + } + else if( EQUAL(pszCapability, OLCFastFeatureCount) ) + { + if( bVal ) + bVal = m_poFilterGeom == NULL; + } + + return bVal; +} + +/************************************************************************/ +/* SetExtent() */ +/************************************************************************/ + +void OGRWarpedLayer::SetExtent( double dfXMin, double dfYMin, + double dfXMax, double dfYMax ) +{ + sStaticEnvelope.MinX = dfXMin; + sStaticEnvelope.MinY = dfYMin; + sStaticEnvelope.MaxX = dfXMax; + sStaticEnvelope.MaxY = dfYMax; +} diff --git a/ogr/ogrwarpedlayer.h b/ogr/ogrwarpedlayer.h new file mode 100644 index 0000000..755b694 --- /dev/null +++ b/ogr/ogrwarpedlayer.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * $Id: ogrwarpedlayer.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRWarpedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRWARPEDLAYER_H_INCLUDED +#define _OGRWARPEDLAYER_H_INCLUDED + +#include "ogrlayerdecorator.h" + +/************************************************************************/ +/* OGRWarpedLayer */ +/************************************************************************/ + +class OGRWarpedLayer : public OGRLayerDecorator +{ + protected: + OGRFeatureDefn *m_poFeatureDefn; + int m_iGeomField; + + OGRCoordinateTransformation *m_poCT; + OGRCoordinateTransformation *m_poReversedCT; /* may be NULL */ + OGRSpatialReference *m_poSRS; + + OGREnvelope sStaticEnvelope; + + static int ReprojectEnvelope( OGREnvelope* psEnvelope, + OGRCoordinateTransformation* poCT ); + + OGRFeature * SrcFeatureToWarpedFeature(OGRFeature* poFeature); + OGRFeature * WarpedFeatureToSrcFeature(OGRFeature* poFeature); + + public: + + OGRWarpedLayer(OGRLayer* poDecoratedLayer, + int iGeomField, + int bTakeOwnership, + OGRCoordinateTransformation* poCT, /* must NOT be NULL, ownership acquired by OGRWarpedLayer */ + OGRCoordinateTransformation* poReversedCT /* may be NULL, ownership acquired by OGRWarpedLayer */); + virtual ~OGRWarpedLayer(); + + void SetExtent(double dfXMin, double dfYMin, double dfXMax, double dfYMax); + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature( long nFID ); + virtual OGRErr SetFeature( OGRFeature *poFeature ); + virtual OGRErr CreateFeature( OGRFeature *poFeature ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); +}; + +#endif // _OGRWARPEDLAYER_H_INCLUDED diff --git a/ogr/osr_cs_wkt_parser.h b/ogr/osr_cs_wkt_parser.h new file mode 100644 index 0000000..412c907 --- /dev/null +++ b/ogr/osr_cs_wkt_parser.h @@ -0,0 +1,90 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + * Copyright (c) 2013, Even Rouault + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED +# define YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int osr_cs_wkt_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + T_PARAM_MT = 258, + T_CONCAT_MT = 259, + T_INVERSE_MT = 260, + T_PASSTHROUGH_MT = 261, + T_PROJCS = 262, + T_PROJECTION = 263, + T_GEOGCS = 264, + T_DATUM = 265, + T_SPHEROID = 266, + T_PRIMEM = 267, + T_UNIT = 268, + T_GEOCCS = 269, + T_AUTHORITY = 270, + T_VERT_CS = 271, + T_VERT_DATUM = 272, + T_COMPD_CS = 273, + T_AXIS = 274, + T_TOWGS84 = 275, + T_FITTED_CS = 276, + T_LOCAL_CS = 277, + T_LOCAL_DATUM = 278, + T_PARAMETER = 279, + T_EXTENSION = 280, + T_STRING = 281, + T_NUMBER = 282, + T_IDENTIFIER = 283 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int osr_cs_wkt_parse (osr_cs_wkt_parse_context *context); + +#endif /* !YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED */ diff --git a/ogr/overview.cpp b/ogr/overview.cpp new file mode 100644 index 0000000..b02820d --- /dev/null +++ b/ogr/overview.cpp @@ -0,0 +1,2374 @@ +/****************************************************************************** + * $Id: overview.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Helper code to implement overview support in different drivers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include + +CPL_CVSID("$Id: overview.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +typedef CPLErr (*GDALDownsampleFunction) + ( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType); + +/************************************************************************/ +/* GDALDownsampleChunk32R_Near() */ +/************************************************************************/ + +template +static CPLErr +GDALDownsampleChunk32R_NearT( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + T * pChunk, + GByte * pabyChunkNodataMask_unused, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling_unused, + int bHasNoData_unused, float fNoDataValue_unused, + GDALColorTable* poColorTable_unused, + GDALDataType eSrcDataType) + +{ + CPLErr eErr = CE_None; + + int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, nOXSize, nOYSize; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + +/* -------------------------------------------------------------------- */ +/* Figure out the column to start writing to, and the first column */ +/* to not write to. */ +/* -------------------------------------------------------------------- */ + nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize); + nDstXOff2 = (int) + (0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize); + + if( nChunkXOff + nChunkXSize == nSrcWidth ) + nDstXOff2 = nOXSize; + + int nDstXWidth = nDstXOff2 - nDstXOff; + +/* -------------------------------------------------------------------- */ +/* Allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + + T* pDstScanline = (T *) VSIMalloc(nDstXWidth * (GDALGetDataTypeSize(eWrkDataType) / 8)); + int* panSrcXOff = (int*)VSIMalloc(nDstXWidth * sizeof(int)); + + if( pDstScanline == NULL || panSrcXOff == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALDownsampleChunk32R: Out of memory for line buffer." ); + VSIFree(pDstScanline); + VSIFree(panSrcXOff); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); + nDstYOff2 = (int) + (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); + + if( nChunkYOff + nChunkYSize == nSrcHeight ) + nDstYOff2 = nOYSize; + +/* ==================================================================== */ +/* Precompute inner loop constants. */ +/* ==================================================================== */ + int iDstPixel; + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff; + + nSrcXOff = + (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); + if ( nSrcXOff < nChunkXOff ) + nSrcXOff = nChunkXOff; + + panSrcXOff[iDstPixel - nDstXOff] = nSrcXOff; + } + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + T *pSrcScanline; + int nSrcYOff; + + nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); + if ( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + pSrcScanline = pChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize) - nChunkXOff; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + pDstScanline[iDstPixel] = pSrcScanline[panSrcXOff[iDstPixel]]; + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXWidth, 1, + pDstScanline, nDstXWidth, 1, eWrkDataType, + 0, 0 ); + } + + CPLFree( pDstScanline ); + CPLFree( panSrcXOff ); + + return eErr; +} + +static CPLErr +GDALDownsampleChunk32R_Near( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask_unused, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling_unused, + int bHasNoData_unused, float fNoDataValue_unused, + GDALColorTable* poColorTable_unused, + GDALDataType eSrcDataType) +{ + if (eWrkDataType == GDT_Byte) + return GDALDownsampleChunk32R_NearT(nSrcWidth, nSrcHeight, + eWrkDataType, + (GByte *) pChunk, + pabyChunkNodataMask_unused, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + poOverview, + pszResampling_unused, + bHasNoData_unused, fNoDataValue_unused, + poColorTable_unused, + eSrcDataType); + else if (eWrkDataType == GDT_Float32) + return GDALDownsampleChunk32R_NearT(nSrcWidth, nSrcHeight, + eWrkDataType, + (float *) pChunk, + pabyChunkNodataMask_unused, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + poOverview, + pszResampling_unused, + bHasNoData_unused, fNoDataValue_unused, + poColorTable_unused, + eSrcDataType); + + CPLAssert(0); + return CE_Failure; +} + +/************************************************************************/ +/* GDALDownsampleChunk32R_Average() */ +/************************************************************************/ + +template +static CPLErr +GDALDownsampleChunk32R_AverageT( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + T* pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType) + +{ + CPLErr eErr = CE_None; + + int bBit2Grayscale = EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13); + if (bBit2Grayscale) + poColorTable = NULL; + + int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, nOXSize, nOYSize; + T *pDstScanline; + + T tNoDataValue; + if (!bHasNoData) + tNoDataValue = 0; + else + { + T tMaxValue = std::numeric_limits::max(); + T tMinValue = std::numeric_limits::min(); + if( fNoDataValue < tMinValue || fNoDataValue > tMaxValue ) + bHasNoData = FALSE; + else + tNoDataValue = (T)fNoDataValue; + } + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + +/* -------------------------------------------------------------------- */ +/* Figure out the column to start writing to, and the first column */ +/* to not write to. */ +/* -------------------------------------------------------------------- */ + nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize); + nDstXOff2 = (int) + (0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize); + + if( nChunkXOff + nChunkXSize == nSrcWidth ) + nDstXOff2 = nOXSize; + + int nChunkRightXOff = MIN(nSrcWidth, nChunkXOff + nChunkXSize); + int nDstXWidth = nDstXOff2 - nDstXOff; + +/* -------------------------------------------------------------------- */ +/* Allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + + pDstScanline = (T *) VSIMalloc(nDstXWidth * (GDALGetDataTypeSize(eWrkDataType) / 8)); + int* panSrcXOffShifted = (int*)VSIMalloc(2 * nDstXWidth * sizeof(int)); + + if( pDstScanline == NULL || panSrcXOffShifted == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALDownsampleChunk32R: Out of memory for line buffer." ); + VSIFree(pDstScanline); + VSIFree(panSrcXOffShifted); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); + nDstYOff2 = (int) + (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); + + if( nChunkYOff + nChunkYSize == nSrcHeight ) + nDstYOff2 = nOYSize; + + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + +/* ==================================================================== */ +/* Precompute inner loop constants. */ +/* ==================================================================== */ + int iDstPixel; + int bSrcXSpacingIsTwo = TRUE; + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = + (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); + if ( nSrcXOff < nChunkXOff ) + nSrcXOff = nChunkXOff; + nSrcXOff2 = (int) + (0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth); + + if( nSrcXOff2 > nChunkRightXOff || iDstPixel == nOXSize-1 ) + nSrcXOff2 = nChunkRightXOff; + + panSrcXOffShifted[2 * (iDstPixel - nDstXOff)] = nSrcXOff - nChunkXOff; + panSrcXOffShifted[2 * (iDstPixel - nDstXOff) + 1] = nSrcXOff2 - nChunkXOff; + if (nSrcXOff2 - nSrcXOff != 2) + bSrcXSpacingIsTwo = FALSE; + } + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + int nSrcYOff, nSrcYOff2 = 0; + + nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); + if ( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + nSrcYOff2 = + (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight); + + if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) + nSrcYOff2 = nSrcHeight; + if( nSrcYOff2 > nChunkYOff + nChunkYSize) + nSrcYOff2 = nChunkYOff + nChunkYSize; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + if (poColorTable == NULL) + { + if (bSrcXSpacingIsTwo && nSrcYOff2 == nSrcYOff + 2 && + pabyChunkNodataMask == NULL && eWrkDataType == GDT_Byte) + { + /* Optimized case : no nodata, overview by a factor of 2 and regular x and y src spacing */ + T* pSrcScanlineShifted = pChunk + panSrcXOffShifted[0] + (nSrcYOff - nChunkYOff) * nChunkXSize; + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + Tsum nTotal; + + nTotal = pSrcScanlineShifted[0]; + nTotal += pSrcScanlineShifted[1]; + nTotal += pSrcScanlineShifted[nChunkXSize]; + nTotal += pSrcScanlineShifted[1+nChunkXSize]; + + pDstScanline[iDstPixel] = (T) ((nTotal + 2) / 4); + pSrcScanlineShifted += 2; + } + } + else + { + nSrcYOff -= nChunkYOff; + nSrcYOff2 -= nChunkYOff; + + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + int nSrcXOff = panSrcXOffShifted[2 * iDstPixel], + nSrcXOff2 = panSrcXOffShifted[2 * iDstPixel + 1]; + + T val; + Tsum dfTotal = 0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + val = pChunk[iX + iY *nChunkXSize]; + if (pabyChunkNodataMask == NULL || + pabyChunkNodataMask[iX + iY *nChunkXSize]) + { + dfTotal += val; + nCount++; + } + } + } + + if( nCount == 0 ) + pDstScanline[iDstPixel] = tNoDataValue; + else if (eWrkDataType == GDT_Byte) + pDstScanline[iDstPixel] = (T) ((dfTotal + nCount / 2) / nCount); + else + pDstScanline[iDstPixel] = (T) (dfTotal / nCount); + } + } + } + else + { + nSrcYOff -= nChunkYOff; + nSrcYOff2 -= nChunkYOff; + + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + int nSrcXOff = panSrcXOffShifted[2 * iDstPixel], + nSrcXOff2 = panSrcXOffShifted[2 * iDstPixel + 1]; + + T val; + int nTotalR = 0, nTotalG = 0, nTotalB = 0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + val = pChunk[iX + iY *nChunkXSize]; + if (bHasNoData == FALSE || val != tNoDataValue) + { + int nVal = (int)val; + if (nVal >= 0 && nVal < nEntryCount) + { + nTotalR += aEntries[nVal].c1; + nTotalG += aEntries[nVal].c2; + nTotalB += aEntries[nVal].c3; + nCount++; + } + } + } + } + + if( nCount == 0 ) + pDstScanline[iDstPixel] = tNoDataValue; + else + { + int nR = nTotalR / nCount, nG = nTotalG / nCount, nB = nTotalB / nCount; + int i; + Tsum dfMinDist = 0; + int iBestEntry = 0; + for(i=0;iRasterIO( GF_Write, nDstXOff, iDstLine, nDstXWidth, 1, + pDstScanline, nDstXWidth, 1, eWrkDataType, + 0, 0 ); + } + + CPLFree( pDstScanline ); + CPLFree( aEntries ); + CPLFree( panSrcXOffShifted ); + + return eErr; +} + +static CPLErr +GDALDownsampleChunk32R_Average( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType) +{ + if (eWrkDataType == GDT_Byte) + return GDALDownsampleChunk32R_AverageT(nSrcWidth, nSrcHeight, + eWrkDataType, + (GByte *) pChunk, + pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + poOverview, + pszResampling, + bHasNoData, fNoDataValue, + poColorTable, + eSrcDataType); + else if (eWrkDataType == GDT_Float32) + return GDALDownsampleChunk32R_AverageT(nSrcWidth, nSrcHeight, + eWrkDataType, + (float *) pChunk, + pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + poOverview, + pszResampling, + bHasNoData, fNoDataValue, + poColorTable, + eSrcDataType); + + CPLAssert(0); + return CE_Failure; +} + +/************************************************************************/ +/* GDALDownsampleChunk32R_Gauss() */ +/************************************************************************/ + +static CPLErr +GDALDownsampleChunk32R_Gauss( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType) + +{ + CPLErr eErr = CE_None; + + float * pafChunk = (float*) pChunk; + +/* -------------------------------------------------------------------- */ +/* Create the filter kernel and allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, nOXSize, nOYSize; + float *pafDstScanline; + int nGaussMatrixDim = 3; + const int *panGaussMatrix; + static const int anGaussMatrix3x3[] ={ + 1,2,1, + 2,4,2, + 1,2,1 + }; + static const int anGaussMatrix5x5[] = { + 1,4,6,4,1, + 4,16,24,16,4, + 6,24,36,24,6, + 4,16,24,16,4, + 1,4,6,4,1}; + static const int anGaussMatrix7x7[] = { + 1,6,15,20,15,6,1, + 6,36,90,120,90,36,6, + 15,90,225,300,225,90,15, + 20,120,300,400,300,120,20, + 15,90,225,300,225,90,15, + 6,36,90,120,90,36,6, + 1,6,15,20,15,6,1}; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + int nResYFactor = (int) (0.5 + (double)nSrcHeight/(double)nOYSize); + + // matrix for gauss filter + if(nResYFactor <= 2 ) + { + panGaussMatrix = anGaussMatrix3x3; + nGaussMatrixDim=3; + } + else if (nResYFactor <= 4) + { + panGaussMatrix = anGaussMatrix5x5; + nGaussMatrixDim=5; + } + else + { + panGaussMatrix = anGaussMatrix7x7; + nGaussMatrixDim=7; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the column to start writing to, and the first column */ +/* to not write to. */ +/* -------------------------------------------------------------------- */ + nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize); + nDstXOff2 = (int) + (0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize); + + if( nChunkXOff + nChunkXSize == nSrcWidth ) + nDstXOff2 = nOXSize; + + + pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float)); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALDownsampleChunk32R: Out of memory for line buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); + nDstYOff2 = (int) + (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); + + if( nChunkYOff + nChunkYSize == nSrcHeight ) + nDstYOff2 = nOYSize; + + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + + int nChunkRightXOff = MIN(nSrcWidth, nChunkXOff + nChunkXSize); + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + GByte *pabySrcScanlineNodataMask; + int nSrcYOff, nSrcYOff2 = 0, iDstPixel; + + nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); + nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight) + 1; + + if( nSrcYOff < nChunkYOff ) + { + nSrcYOff = nChunkYOff; + nSrcYOff2++; + } + + int iSizeY = nSrcYOff2 - nSrcYOff; + nSrcYOff = nSrcYOff + iSizeY/2 - nGaussMatrixDim/2; + nSrcYOff2 = nSrcYOff + nGaussMatrixDim; + if(nSrcYOff < 0) + nSrcYOff = 0; + + + if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) + nSrcYOff2 = nSrcHeight; + if( nSrcYOff2 > nChunkYOff + nChunkYSize) + nSrcYOff2 = nChunkYOff + nChunkYSize; + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize); + if (pabyChunkNodataMask != NULL) + pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize); + else + pabySrcScanlineNodataMask = NULL; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); + nSrcXOff2 = (int)(0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth) + 1; + + int iSizeX = nSrcXOff2 - nSrcXOff; + nSrcXOff = nSrcXOff + iSizeX/2 - nGaussMatrixDim/2; + nSrcXOff2 = nSrcXOff + nGaussMatrixDim; + if(nSrcXOff < 0) + nSrcXOff = 0; + + if( nSrcXOff2 > nChunkRightXOff || iDstPixel == nOXSize-1 ) + nSrcXOff2 = nChunkRightXOff; + + if (poColorTable == NULL) + { + double dfTotal = 0.0, val; + int nCount = 0, iX, iY; + int i = 0,j = 0; + const int *panLineWeight = panGaussMatrix; + + for( j=0, iY = nSrcYOff; iY < nSrcYOff2; + iY++, j++, panLineWeight += nGaussMatrixDim ) + { + for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i ) + { + val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]; + if (pabySrcScanlineNodataMask == NULL || + pabySrcScanlineNodataMask[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]) + { + int nWeight = panLineWeight[i]; + dfTotal += val * nWeight; + nCount += nWeight; + } + } + } + + if (bHasNoData && nCount == 0) + { + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + } + else + { + if( nCount == 0 ) + pafDstScanline[iDstPixel - nDstXOff] = 0.0; + else + pafDstScanline[iDstPixel - nDstXOff] = (float) (dfTotal / nCount); + } + } + else + { + double val; + int nTotalR = 0, nTotalG = 0, nTotalB = 0; + int nTotalWeight = 0, iX, iY; + int i = 0,j = 0; + const int *panLineWeight = panGaussMatrix; + + for( j=0, iY = nSrcYOff; iY < nSrcYOff2; + iY++, j++, panLineWeight += nGaussMatrixDim ) + { + for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i ) + { + val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]; + if (bHasNoData == FALSE || val != fNoDataValue) + { + int nVal = (int)val; + if (nVal >= 0 && nVal < nEntryCount) + { + int nWeight = panLineWeight[i]; + nTotalR += aEntries[nVal].c1 * nWeight; + nTotalG += aEntries[nVal].c2 * nWeight; + nTotalB += aEntries[nVal].c3 * nWeight; + nTotalWeight += nWeight; + } + } + } + } + + if (bHasNoData && nTotalWeight == 0) + { + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + } + else + { + if( nTotalWeight == 0 ) + pafDstScanline[iDstPixel - nDstXOff] = 0.0; + else + { + int nR = nTotalR / nTotalWeight, nG = nTotalG / nTotalWeight, nB = nTotalB / nTotalWeight; + int i; + double dfMinDist = 0; + int iBestEntry = 0; + for(i=0;iRasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1, + pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32, + 0, 0 ); + } + + CPLFree( pafDstScanline ); + CPLFree( aEntries ); + + return eErr; +} + +/************************************************************************/ +/* GDALDownsampleChunk32R_Mode() */ +/************************************************************************/ + +static CPLErr +GDALDownsampleChunk32R_Mode( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType) + +{ + CPLErr eErr = CE_None; + + float * pafChunk = (float*) pChunk; + +/* -------------------------------------------------------------------- */ +/* Create the filter kernel and allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, nOXSize, nOYSize; + float *pafDstScanline; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + +/* -------------------------------------------------------------------- */ +/* Figure out the column to start writing to, and the first column */ +/* to not write to. */ +/* -------------------------------------------------------------------- */ + nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize); + nDstXOff2 = (int) + (0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize); + + if( nChunkXOff + nChunkXSize == nSrcWidth ) + nDstXOff2 = nOXSize; + + + pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float)); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALDownsampleChunk32R: Out of memory for line buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); + nDstYOff2 = (int) + (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); + + if( nChunkYOff + nChunkYSize == nSrcHeight ) + nDstYOff2 = nOYSize; + + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + + int nMaxNumPx = 0; + float* pafVals = NULL; + int* panSums = NULL; + + int nChunkRightXOff = MIN(nSrcWidth, nChunkXOff + nChunkXSize); + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + GByte *pabySrcScanlineNodataMask; + int nSrcYOff, nSrcYOff2 = 0, iDstPixel; + + nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); + if ( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + nSrcYOff2 = + (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight); + + if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) + nSrcYOff2 = nSrcHeight; + if( nSrcYOff2 > nChunkYOff + nChunkYSize) + nSrcYOff2 = nChunkYOff + nChunkYSize; + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize); + if (pabyChunkNodataMask != NULL) + pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize); + else + pabySrcScanlineNodataMask = NULL; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = + (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); + if ( nSrcXOff < nChunkXOff ) + nSrcXOff = nChunkXOff; + nSrcXOff2 = (int) + (0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth); + + if( nSrcXOff2 > nChunkRightXOff || iDstPixel == nOXSize-1 ) + nSrcXOff2 = nChunkRightXOff; + + if (eSrcDataType != GDT_Byte || nEntryCount > 256) + { + /* I'm not sure how much sense it makes to run a majority + filter on floating point data, but here it is for the sake + of compatability. It won't look right on RGB images by the + nature of the filter. */ + int nNumPx = (nSrcYOff2-nSrcYOff)*(nSrcXOff2-nSrcXOff); + int iMaxInd = 0, iMaxVal = -1, iY, iX; + + if (nNumPx > nMaxNumPx) + { + pafVals = (float*) CPLRealloc(pafVals, nNumPx * sizeof(float)); + panSums = (int*) CPLRealloc(panSums, nNumPx * sizeof(int)); + nMaxNumPx = nNumPx; + } + + for( iY = nSrcYOff; iY < nSrcYOff2; ++iY ) + { + int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff; + for( iX = nSrcXOff; iX < nSrcXOff2; ++iX ) + { + if (pabySrcScanlineNodataMask == NULL || + pabySrcScanlineNodataMask[iX+iTotYOff]) + { + float fVal = pafSrcScanline[iX+iTotYOff]; + int i; + + //Check array for existing entry + for( i = 0; i < iMaxInd; ++i ) + if( pafVals[i] == fVal + && ++panSums[i] > panSums[iMaxVal] ) + { + iMaxVal = i; + break; + } + + //Add to arr if entry not already there + if( i == iMaxInd ) + { + pafVals[iMaxInd] = fVal; + panSums[iMaxInd] = 1; + + if( iMaxVal < 0 ) + iMaxVal = iMaxInd; + + ++iMaxInd; + } + } + } + } + + if( iMaxVal == -1 ) + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + else + pafDstScanline[iDstPixel - nDstXOff] = pafVals[iMaxVal]; + } + else /* if (eSrcDataType == GDT_Byte && nEntryCount < 256) */ + { + /* So we go here for a paletted or non-paletted byte band */ + /* The input values are then between 0 and 255 */ + int anVals[256], nMaxVal = 0, iMaxInd = -1, iY, iX; + + memset(anVals, 0, 256*sizeof(int)); + + for( iY = nSrcYOff; iY < nSrcYOff2; ++iY ) + { + int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff; + for( iX = nSrcXOff; iX < nSrcXOff2; ++iX ) + { + float val = pafSrcScanline[iX+iTotYOff]; + if (bHasNoData == FALSE || val != fNoDataValue) + { + int nVal = (int) val; + if ( ++anVals[nVal] > nMaxVal) + { + //Sum the density + //Is it the most common value so far? + iMaxInd = nVal; + nMaxVal = anVals[nVal]; + } + } + } + } + + if( iMaxInd == -1 ) + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + else + pafDstScanline[iDstPixel - nDstXOff] = (float)iMaxInd; + } + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1, + pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32, + 0, 0 ); + } + + CPLFree( pafDstScanline ); + CPLFree( aEntries ); + CPLFree( pafVals ); + CPLFree( panSums ); + + return eErr; +} + +/************************************************************************/ +/* GDALDownsampleChunk32R_Cubic() */ +/************************************************************************/ + +static CPLErr +GDALDownsampleChunk32R_Cubic( int nSrcWidth, int nSrcHeight, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType) + +{ + + CPLErr eErr = CE_None; + + float * pafChunk = (float*) pChunk; + +/* -------------------------------------------------------------------- */ +/* Create the filter kernel and allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, nOXSize, nOYSize; + float *pafDstScanline; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + +/* -------------------------------------------------------------------- */ +/* Figure out the column to start writing to, and the first column */ +/* to not write to. */ +/* -------------------------------------------------------------------- */ + nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize); + nDstXOff2 = (int) + (0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize); + + if( nChunkXOff + nChunkXSize == nSrcWidth ) + nDstXOff2 = nOXSize; + + + pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float)); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALDownsampleChunk32R: Out of memory for line buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); + nDstYOff2 = (int) + (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); + + if( nChunkYOff + nChunkYSize == nSrcHeight ) + nDstYOff2 = nOYSize; + + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + + int nChunkRightXOff = MIN(nSrcWidth, nChunkXOff + nChunkXSize); + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + GByte *pabySrcScanlineNodataMask; + int nSrcYOff, nSrcYOff2 = 0, iDstPixel; + + nSrcYOff = (int) floor(((iDstLine+0.5)/(double)nOYSize) * nSrcHeight - 0.5)-1; + nSrcYOff2 = nSrcYOff + 4; + if(nSrcYOff < 0) + nSrcYOff = 0; + if(nSrcYOff < nChunkYOff) + nSrcYOff = nChunkYOff; + + if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) + nSrcYOff2 = nSrcHeight; + if( nSrcYOff2 > nChunkYOff + nChunkYSize) + nSrcYOff2 = nChunkYOff + nChunkYSize; + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize); + if (pabyChunkNodataMask != NULL) + pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize); + else + pabySrcScanlineNodataMask = NULL; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = (int) floor(((iDstPixel+0.5)/(double)nOXSize) * nSrcWidth - 0.5)-1; + nSrcXOff2 = nSrcXOff + 4; + + if(nSrcXOff < 0) + nSrcXOff = 0; + + if( nSrcXOff2 > nChunkRightXOff || iDstPixel == nOXSize-1 ) + nSrcXOff2 = nChunkRightXOff; + + // If we do not seem to have our full 4x4 window just + // do nearest resampling. + if( nSrcXOff2 - nSrcXOff != 4 || nSrcYOff2 - nSrcYOff != 4 ) + { + int nLSrcYOff = (int) (0.5+(iDstLine/(double)nOYSize) * nSrcHeight); + int nLSrcXOff = (int) (0.5+(iDstPixel/(double)nOXSize) * nSrcWidth); + + if( nLSrcYOff < nChunkYOff ) + nLSrcYOff = nChunkYOff; + if( nLSrcYOff > nChunkYOff + nChunkYSize - 1 ) + nLSrcYOff = nChunkYOff + nChunkYSize - 1; + + pafDstScanline[iDstPixel - nDstXOff] = + pafChunk[(nLSrcYOff-nChunkYOff) * nChunkXSize + + (nLSrcXOff - nChunkXOff)]; + } + else + { +#define CubicConvolution(distance1,distance2,distance3,f0,f1,f2,f3) \ +( ( -f0 + f1 - f2 + f3) * distance3 \ ++ (2.0*(f0 - f1) + f2 - f3) * distance2 \ ++ ( -f0 + f2 ) * distance1 \ ++ f1 ) + + int ic; + double adfRowResults[4]; + double dfSrcX = (((iDstPixel+0.5)/(double)nOXSize) * nSrcWidth); + double dfDeltaX = dfSrcX - 0.5 - (nSrcXOff+1); + double dfDeltaX2 = dfDeltaX * dfDeltaX; + double dfDeltaX3 = dfDeltaX2 * dfDeltaX; + + for ( ic = 0; ic < 4; ic++ ) + { + float *pafSrcRow = pafSrcScanline + + nSrcXOff-nChunkXOff+(nSrcYOff+ic-nSrcYOff)*nChunkXSize; + + adfRowResults[ic] = + CubicConvolution(dfDeltaX, dfDeltaX2, dfDeltaX3, + pafSrcRow[0], + pafSrcRow[1], + pafSrcRow[2], + pafSrcRow[3] ); + } + + double dfSrcY = (((iDstLine+0.5)/(double)nOYSize) * nSrcHeight); + double dfDeltaY = dfSrcY - 0.5 - (nSrcYOff+1); + double dfDeltaY2 = dfDeltaY * dfDeltaY; + double dfDeltaY3 = dfDeltaY2 * dfDeltaY; + + pafDstScanline[iDstPixel - nDstXOff] = (float) + CubicConvolution(dfDeltaY, dfDeltaY2, dfDeltaY3, + adfRowResults[0], + adfRowResults[1], + adfRowResults[2], + adfRowResults[3] ); + } + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1, + pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32, + 0, 0 ); + } + + CPLFree( pafDstScanline ); + CPLFree( aEntries ); + + return eErr; +} + +/************************************************************************/ +/* GDALDownsampleChunkC32R() */ +/************************************************************************/ + +static CPLErr +GDALDownsampleChunkC32R( int nSrcWidth, int nSrcHeight, + float * pafChunk, int nChunkYOff, int nChunkYSize, + GDALRasterBand * poOverview, + const char * pszResampling ) + +{ + int nDstYOff, nDstYOff2, nOXSize, nOYSize; + float *pafDstScanline; + CPLErr eErr = CE_None; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + + pafDstScanline = (float *) VSIMalloc(nOXSize * sizeof(float) * 2); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALDownsampleChunkC32R: Out of memory for line buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize); + nDstYOff2 = (int) + (0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize); + + if( nChunkYOff + nChunkYSize == nSrcHeight ) + nDstYOff2 = nOYSize; + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + int nSrcYOff, nSrcYOff2, iDstPixel; + + nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight); + if( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight); + if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) + nSrcYOff2 = nSrcHeight; + if( nSrcYOff2 > nChunkYOff + nChunkYSize ) + nSrcYOff2 = nChunkYOff + nChunkYSize; + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nSrcWidth) * 2; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = 0; iDstPixel < nOXSize; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth); + nSrcXOff2 = (int) + (0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth); + if( nSrcXOff2 > nSrcWidth ) + nSrcXOff2 = nSrcWidth; + + if( EQUALN(pszResampling,"NEAR",4) ) + { + pafDstScanline[iDstPixel*2] = pafSrcScanline[nSrcXOff*2]; + pafDstScanline[iDstPixel*2+1] = pafSrcScanline[nSrcXOff*2+1]; + } + else if( EQUAL(pszResampling,"AVERAGE_MAGPHASE") ) + { + double dfTotalR = 0.0, dfTotalI = 0.0, dfTotalM = 0.0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + double dfR, dfI; + + dfR = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2]; + dfI = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1]; + dfTotalR += dfR; + dfTotalI += dfI; + dfTotalM += sqrt( dfR*dfR + dfI*dfI ); + nCount++; + } + } + + CPLAssert( nCount > 0 ); + if( nCount == 0 ) + { + pafDstScanline[iDstPixel*2] = 0.0; + pafDstScanline[iDstPixel*2+1] = 0.0; + } + else + { + double dfM, dfDesiredM, dfRatio=1.0; + + pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount); + pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount); + + dfM = sqrt(pafDstScanline[iDstPixel*2 ]*pafDstScanline[iDstPixel*2 ] + + pafDstScanline[iDstPixel*2+1]*pafDstScanline[iDstPixel*2+1]); + dfDesiredM = dfTotalM / nCount; + if( dfM != 0.0 ) + dfRatio = dfDesiredM / dfM; + + pafDstScanline[iDstPixel*2 ] *= (float) dfRatio; + pafDstScanline[iDstPixel*2+1] *= (float) dfRatio; + } + } + else if( EQUALN(pszResampling,"AVER",4) ) + { + double dfTotalR = 0.0, dfTotalI = 0.0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + dfTotalR += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2]; + dfTotalI += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1]; + nCount++; + } + } + + CPLAssert( nCount > 0 ); + if( nCount == 0 ) + { + pafDstScanline[iDstPixel*2] = 0.0; + pafDstScanline[iDstPixel*2+1] = 0.0; + } + else + { + pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount); + pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount); + } + } + } + + eErr = poOverview->RasterIO( GF_Write, 0, iDstLine, nOXSize, 1, + pafDstScanline, nOXSize, 1, GDT_CFloat32, + 0, 0 ); + } + + CPLFree( pafDstScanline ); + + return eErr; +} + +/************************************************************************/ +/* GDALRegenerateCascadingOverviews() */ +/* */ +/* Generate a list of overviews in order from largest to */ +/* smallest, computing each from the next larger. */ +/************************************************************************/ + +static CPLErr +GDALRegenerateCascadingOverviews( + GDALRasterBand *poSrcBand, int nOverviews, GDALRasterBand **papoOvrBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* First, we must put the overviews in order from largest to */ +/* smallest. */ +/* -------------------------------------------------------------------- */ + int i, j; + + for( i = 0; i < nOverviews-1; i++ ) + { + for( j = 0; j < nOverviews - i - 1; j++ ) + { + + if( papoOvrBands[j]->GetXSize() + * (float) papoOvrBands[j]->GetYSize() < + papoOvrBands[j+1]->GetXSize() + * (float) papoOvrBands[j+1]->GetYSize() ) + { + GDALRasterBand * poTempBand; + + poTempBand = papoOvrBands[j]; + papoOvrBands[j] = papoOvrBands[j+1]; + papoOvrBands[j+1] = poTempBand; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Count total pixels so we can prepare appropriate scaled */ +/* progress functions. */ +/* -------------------------------------------------------------------- */ + double dfTotalPixels = 0.0; + + for( i = 0; i < nOverviews; i++ ) + { + dfTotalPixels += papoOvrBands[i]->GetXSize() + * (double) papoOvrBands[i]->GetYSize(); + } + +/* -------------------------------------------------------------------- */ +/* Generate all the bands. */ +/* -------------------------------------------------------------------- */ + double dfPixelsProcessed = 0.0; + + for( i = 0; i < nOverviews; i++ ) + { + void *pScaledProgressData; + double dfPixels; + GDALRasterBand *poBaseBand; + CPLErr eErr; + + if( i == 0 ) + poBaseBand = poSrcBand; + else + poBaseBand = papoOvrBands[i-1]; + + dfPixels = papoOvrBands[i]->GetXSize() + * (double) papoOvrBands[i]->GetYSize(); + + pScaledProgressData = GDALCreateScaledProgress( + dfPixelsProcessed / dfTotalPixels, + (dfPixelsProcessed + dfPixels) / dfTotalPixels, + pfnProgress, pProgressData ); + + eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBaseBand, + 1, (GDALRasterBandH *) papoOvrBands+i, + pszResampling, + GDALScaledProgress, + pScaledProgressData ); + GDALDestroyScaledProgress( pScaledProgressData ); + + if( eErr != CE_None ) + return eErr; + + dfPixelsProcessed += dfPixels; + + /* we only do the bit2grayscale promotion on the base band */ + if( EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13) ) + pszResampling = "AVERAGE"; + } + + return CE_None; +} + +/************************************************************************/ +/* GDALGetDownsampleFunction() */ +/************************************************************************/ + +static +GDALDownsampleFunction GDALGetDownsampleFunction(const char* pszResampling) +{ + if( EQUALN(pszResampling,"NEAR",4) ) + return GDALDownsampleChunk32R_Near; + else if( EQUALN(pszResampling,"AVER",4) ) + return GDALDownsampleChunk32R_Average; + else if( EQUALN(pszResampling,"GAUSS",5) ) + return GDALDownsampleChunk32R_Gauss; + else if( EQUALN(pszResampling,"MODE",4) ) + return GDALDownsampleChunk32R_Mode; + else if( EQUALN(pszResampling,"CUBIC",5) ) + return GDALDownsampleChunk32R_Cubic; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALGetDownsampleFunction: Unsupported resampling method \"%s\".", + pszResampling ); + return NULL; + } +} + +/************************************************************************/ +/* GDALGetOvrWorkDataType() */ +/************************************************************************/ + +static GDALDataType GDALGetOvrWorkDataType(const char* pszResampling, + GDALDataType eSrcDataType) +{ + if( (EQUALN(pszResampling,"NEAR",4) || EQUALN(pszResampling,"AVER",4)) && + eSrcDataType == GDT_Byte) + return GDT_Byte; + else + return GDT_Float32; +} + +/************************************************************************/ +/* GDALRegenerateOverviews() */ +/************************************************************************/ + +/** + * \brief Generate downsampled overviews. + * + * This function will generate one or more overview images from a base + * image using the requested downsampling algorithm. It's primary use + * is for generating overviews via GDALDataset::BuildOverviews(), but it + * can also be used to generate downsampled images in one file from another + * outside the overview architecture. + * + * The output bands need to exist in advance. + * + * The full set of resampling algorithms is documented in + * GDALDataset::BuildOverviews(). + * + * This function will honour properly NODATA_VALUES tuples (special dataset metadata) so + * that only a given RGB triplet (in case of a RGB image) will be considered as the + * nodata value and not each value of the triplet independantly per band. + * + * @param hSrcBand the source (base level) band. + * @param nOverviewCount the number of downsampled bands being generated. + * @param pahOvrBands the list of downsampled bands to be generated. + * @param pszResampling Resampling algorithm (eg. "AVERAGE"). + * @param pfnProgress progress report function. + * @param pProgressData progress function callback data. + * @return CE_None on success or CE_Failure on failure. + */ +CPLErr +GDALRegenerateOverviews( GDALRasterBandH hSrcBand, + int nOverviewCount, GDALRasterBandH *pahOvrBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand; + GDALRasterBand **papoOvrBands = (GDALRasterBand **) pahOvrBands; + int nFullResYChunk, nWidth; + int nFRXBlockSize, nFRYBlockSize; + GDALDataType eType; + int bHasNoData; + float fNoDataValue; + GDALColorTable* poColorTable = NULL; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( EQUAL(pszResampling,"NONE") ) + return CE_None; + + GDALDownsampleFunction pfnDownsampleFn = GDALGetDownsampleFunction(pszResampling); + if (pfnDownsampleFn == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Check color tables... */ +/* -------------------------------------------------------------------- */ + if ((EQUALN(pszResampling,"AVER",4) + || EQUALN(pszResampling,"MODE",4) + || EQUALN(pszResampling,"GAUSS",5)) && + poSrcBand->GetColorInterpretation() == GCI_PaletteIndex) + { + poColorTable = poSrcBand->GetColorTable(); + if (poColorTable != NULL) + { + if (poColorTable->GetPaletteInterpretation() != GPI_RGB) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Computing overviews on palette index raster bands " + "with a palette whose color interpreation is not RGB " + "will probably lead to unexpected results."); + poColorTable = NULL; + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Computing overviews on palette index raster bands " + "without a palette will probably lead to unexpected results."); + } + } + + + /* If we have a nodata mask and we are doing something more complicated */ + /* than nearest neighbouring, we have to fetch to nodata mask */ + int bUseNoDataMask = (!EQUALN(pszResampling,"NEAR",4) && + (poSrcBand->GetMaskFlags() & GMF_ALL_VALID) == 0); + +/* -------------------------------------------------------------------- */ +/* If we are operating on multiple overviews, and using */ +/* averaging, lets do them in cascading order to reduce the */ +/* amount of computation. */ +/* -------------------------------------------------------------------- */ + + /* In case the mask made be computed from another band of the dataset, */ + /* we can't use cascaded generation, as the computation of the overviews */ + /* of the band used for the mask band may not have yet occured (#3033) */ + if( (EQUALN(pszResampling,"AVER",4) || EQUALN(pszResampling,"GAUSS",5)) && nOverviewCount > 1 + && !(bUseNoDataMask && poSrcBand->GetMaskFlags() != GMF_NODATA)) + return GDALRegenerateCascadingOverviews( poSrcBand, + nOverviewCount, papoOvrBands, + pszResampling, + pfnProgress, + pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Setup one horizontal swath to read from the raw buffer. */ +/* -------------------------------------------------------------------- */ + void *pChunk; + GByte *pabyChunkNodataMask = NULL; + + poSrcBand->GetBlockSize( &nFRXBlockSize, &nFRYBlockSize ); + + if( nFRYBlockSize < 16 || nFRYBlockSize > 256 ) + nFullResYChunk = 64; + else + nFullResYChunk = nFRYBlockSize; + + if( GDALDataTypeIsComplex( poSrcBand->GetRasterDataType() ) ) + eType = GDT_CFloat32; + else + eType = GDALGetOvrWorkDataType(pszResampling, poSrcBand->GetRasterDataType()); + + nWidth = poSrcBand->GetXSize(); + pChunk = + VSIMalloc3((GDALGetDataTypeSize(eType)/8), nFullResYChunk, nWidth ); + if (bUseNoDataMask) + { + pabyChunkNodataMask = (GByte *) + (GByte*) VSIMalloc2( nFullResYChunk, nWidth ); + } + + if( pChunk == NULL || (bUseNoDataMask && pabyChunkNodataMask == NULL)) + { + CPLFree(pChunk); + CPLFree(pabyChunkNodataMask); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in GDALRegenerateOverviews()." ); + + return CE_Failure; + } + + fNoDataValue = (float) poSrcBand->GetNoDataValue(&bHasNoData); + +/* -------------------------------------------------------------------- */ +/* Loop over image operating on chunks. */ +/* -------------------------------------------------------------------- */ + int nChunkYOff = 0; + CPLErr eErr = CE_None; + + for( nChunkYOff = 0; + nChunkYOff < poSrcBand->GetYSize() && eErr == CE_None; + nChunkYOff += nFullResYChunk ) + { + if( !pfnProgress( nChunkYOff / (double) poSrcBand->GetYSize(), + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + + if( nFullResYChunk + nChunkYOff > poSrcBand->GetYSize() ) + nFullResYChunk = poSrcBand->GetYSize() - nChunkYOff; + + /* read chunk */ + if (eErr == CE_None) + eErr = poSrcBand->RasterIO( GF_Read, 0, nChunkYOff, nWidth, nFullResYChunk, + pChunk, nWidth, nFullResYChunk, eType, + 0, 0 ); + if (eErr == CE_None && bUseNoDataMask) + eErr = poSrcBand->GetMaskBand()->RasterIO( GF_Read, 0, nChunkYOff, nWidth, nFullResYChunk, + pabyChunkNodataMask, nWidth, nFullResYChunk, GDT_Byte, + 0, 0 ); + + /* special case to promote 1bit data to 8bit 0/255 values */ + if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE") ) + { + int i; + + if (eType == GDT_Float32) + { + float* pafChunk = (float*)pChunk; + for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- ) + { + if( pafChunk[i] == 1.0 ) + pafChunk[i] = 255.0; + } + } + else if (eType == GDT_Byte) + { + GByte* pabyChunk = (GByte*)pChunk; + for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- ) + { + if( pabyChunk[i] == 1 ) + pabyChunk[i] = 255; + } + } + else + CPLAssert(0); + } + else if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE_MINISWHITE") ) + { + int i; + + if (eType == GDT_Float32) + { + float* pafChunk = (float*)pChunk; + for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- ) + { + if( pafChunk[i] == 1.0 ) + pafChunk[i] = 0.0; + else if( pafChunk[i] == 0.0 ) + pafChunk[i] = 255.0; + } + } + else if (eType == GDT_Byte) + { + GByte* pabyChunk = (GByte*)pChunk; + for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- ) + { + if( pabyChunk[i] == 1 ) + pabyChunk[i] = 0; + else if( pabyChunk[i] == 0 ) + pabyChunk[i] = 255; + } + } + else + CPLAssert(0); + } + + for( int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None; iOverview++ ) + { + if( eType == GDT_Byte || eType == GDT_Float32 ) + eErr = pfnDownsampleFn(nWidth, poSrcBand->GetYSize(), + eType, + pChunk, + pabyChunkNodataMask, + 0, nWidth, + nChunkYOff, nFullResYChunk, + papoOvrBands[iOverview], pszResampling, + bHasNoData, fNoDataValue, poColorTable, + poSrcBand->GetRasterDataType()); + else + eErr = GDALDownsampleChunkC32R(nWidth, poSrcBand->GetYSize(), + (float*)pChunk, nChunkYOff, nFullResYChunk, + papoOvrBands[iOverview], pszResampling); + } + } + + VSIFree( pChunk ); + VSIFree( pabyChunkNodataMask ); + +/* -------------------------------------------------------------------- */ +/* Renormalized overview mean / stddev if needed. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && EQUAL(pszResampling,"AVERAGE_MP") ) + { + GDALOverviewMagnitudeCorrection( (GDALRasterBandH) poSrcBand, + nOverviewCount, + (GDALRasterBandH *) papoOvrBands, + GDALDummyProgress, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* It can be important to flush out data to overviews. */ +/* -------------------------------------------------------------------- */ + for( int iOverview = 0; + eErr == CE_None && iOverview < nOverviewCount; + iOverview++ ) + { + eErr = papoOvrBands[iOverview]->FlushCache(); + } + + if (eErr == CE_None) + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + + + +/************************************************************************/ +/* GDALRegenerateOverviewsMultiBand() */ +/************************************************************************/ + +/** + * \brief Variant of GDALRegenerateOverviews, specialy dedicated for generating + * compressed pixel-interleaved overviews (JPEG-IN-TIFF for example) + * + * This function will generate one or more overview images from a base + * image using the requested downsampling algorithm. It's primary use + * is for generating overviews via GDALDataset::BuildOverviews(), but it + * can also be used to generate downsampled images in one file from another + * outside the overview architecture. + * + * The output bands need to exist in advance and share the same characteristics + * (type, dimensions) + * + * The resampling algorithms supported for the moment are "NEAREST", "AVERAGE" + * and "GAUSS" + * + * The pseudo-algorithm used by the function is : + * for each overview + * iterate on lines of the source by a step of deltay + * iterate on columns of the source by a step of deltax + * read the source data of size deltax * deltay for all the bands + * generate the corresponding overview block for all the bands + * + * This function will honour properly NODATA_VALUES tuples (special dataset metadata) so + * that only a given RGB triplet (in case of a RGB image) will be considered as the + * nodata value and not each value of the triplet independantly per band. + * + * @param nBands the number of bands, size of papoSrcBands and size of + * first dimension of papapoOverviewBands + * @param papoSrcBands the list of source bands to downsample + * @param nOverviews the number of downsampled overview levels being generated. + * @param papapoOverviewBands bidimension array of bands. First dimension is indexed + * by nBands. Second dimension is indexed by nOverviews. + * @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE" or "GAUSS"). + * @param pfnProgress progress report function. + * @param pProgressData progress function callback data. + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr +GDALRegenerateOverviewsMultiBand(int nBands, GDALRasterBand** papoSrcBands, + int nOverviews, + GDALRasterBand*** papapoOverviewBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + CPLErr eErr = CE_None; + int iOverview, iBand; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( EQUAL(pszResampling,"NONE") ) + return CE_None; + + /* Sanity checks */ + if (!EQUALN(pszResampling, "NEAR", 4) && !EQUAL(pszResampling, "AVERAGE") && !EQUAL(pszResampling, "GAUSS")) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: pszResampling='%s' not supported", pszResampling); + return CE_Failure; + } + + GDALDownsampleFunction pfnDownsampleFn = GDALGetDownsampleFunction(pszResampling); + if (pfnDownsampleFn == NULL) + return CE_Failure; + + int nSrcWidth = papoSrcBands[0]->GetXSize(); + int nSrcHeight = papoSrcBands[0]->GetYSize(); + GDALDataType eDataType = papoSrcBands[0]->GetRasterDataType(); + for(iBand=1;iBandGetXSize() != nSrcWidth || + papoSrcBands[iBand]->GetYSize() != nSrcHeight) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the source bands must have the same dimensions"); + return CE_Failure; + } + if (papoSrcBands[iBand]->GetRasterDataType() != eDataType) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the source bands must have the same data type"); + return CE_Failure; + } + } + + for(iOverview=0;iOverviewGetXSize(); + int nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize(); + for(iBand=1;iBandGetXSize() != nDstWidth || + papapoOverviewBands[iBand][iOverview]->GetYSize() != nDstHeight) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the overviews bands of the same level must have the same dimensions"); + return CE_Failure; + } + if (papapoOverviewBands[iBand][iOverview]->GetRasterDataType() != eDataType) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the overviews bands must have the same data type as the source bands"); + return CE_Failure; + } + } + } + + /* First pass to compute the total number of pixels to read */ + double dfTotalPixelCount = 0; + for(iOverview=0;iOverviewGetXSize(); + nSrcHeight = papoSrcBands[0]->GetYSize(); + + int nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize(); + /* Try to use previous level of overview as the source to compute */ + /* the next level */ + if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth) + { + nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize(); + nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize(); + } + + dfTotalPixelCount += (double)nSrcWidth * nSrcHeight; + } + + nSrcWidth = papoSrcBands[0]->GetXSize(); + nSrcHeight = papoSrcBands[0]->GetYSize(); + + GDALDataType eWrkDataType = GDALGetOvrWorkDataType(pszResampling, eDataType); + + /* If we have a nodata mask and we are doing something more complicated */ + /* than nearest neighbouring, we have to fetch to nodata mask */ + int bUseNoDataMask = (!EQUALN(pszResampling,"NEAR",4) && + (papoSrcBands[0]->GetMaskFlags() & GMF_ALL_VALID) == 0); + + int* pabHasNoData = (int*)CPLMalloc(nBands * sizeof(int)); + float* pafNoDataValue = (float*)CPLMalloc(nBands * sizeof(float)); + + for(iBand=0;iBandGetNoDataValue(&pabHasNoData[iBand]); + } + + /* Second pass to do the real job ! */ + double dfCurPixelCount = 0; + for(iOverview=0;iOverviewGetBlockSize(&nDstBlockXSize, &nDstBlockYSize); + nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize(); + nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize(); + + /* Try to use previous level of overview as the source to compute */ + /* the next level */ + if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth) + { + nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize(); + nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize(); + iSrcOverview = iOverview - 1; + } + + /* Compute the chunck size of the source such as it will match the size of */ + /* a block of the overview */ + int nFullResXChunk = (nDstBlockXSize * nSrcWidth) / nDstWidth; + int nFullResYChunk = (nDstBlockYSize * nSrcHeight) / nDstHeight; + + void** papaChunk = (void**) CPLMalloc(nBands * sizeof(void*)); + GByte* pabyChunkNoDataMask = NULL; + for(iBand=0;iBand= 0) + CPLFree(papaChunk[iBand]); + CPLFree(papaChunk); + CPLFree(pabHasNoData); + CPLFree(pafNoDataValue); + + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALRegenerateOverviewsMultiBand: Out of memory." ); + return CE_Failure; + } + } + if (bUseNoDataMask) + { + pabyChunkNoDataMask = (GByte*) VSIMalloc2(nFullResXChunk, nFullResYChunk); + if( pabyChunkNoDataMask == NULL ) + { + for(iBand=0;iBandRasterIO( GF_Read, + nChunkXOff, nChunkYOff, + nXCount, nYCount, + papaChunk[iBand], + nXCount, nYCount, + eWrkDataType, 0, 0 ); + } + + if (bUseNoDataMask && eErr == CE_None) + { + GDALRasterBand* poSrcBand; + if (iSrcOverview == -1) + poSrcBand = papoSrcBands[0]; + else + poSrcBand = papapoOverviewBands[0][iSrcOverview]; + eErr = poSrcBand->GetMaskBand()->RasterIO( GF_Read, + nChunkXOff, nChunkYOff, + nXCount, nYCount, + pabyChunkNoDataMask, + nXCount, nYCount, + GDT_Byte, 0, 0 ); + } + + /* Compute the resulting overview block */ + for(iBand=0;iBandFlushCache(); + } + CPLFree(papaChunk); + CPLFree(pabyChunkNoDataMask); + + } + + CPLFree(pabHasNoData); + CPLFree(pafNoDataValue); + + if (eErr == CE_None) + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + + +/************************************************************************/ +/* GDALComputeBandStats() */ +/************************************************************************/ + +CPLErr CPL_STDCALL +GDALComputeBandStats( GDALRasterBandH hSrcBand, + int nSampleStep, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hSrcBand, "GDALComputeBandStats", CE_Failure ); + + GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand; + int iLine, nWidth, nHeight; + GDALDataType eType = poSrcBand->GetRasterDataType(); + GDALDataType eWrkType; + int bComplex; + float *pafData; + double dfSum=0.0, dfSum2=0.0; + int nSamples = 0; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + nWidth = poSrcBand->GetXSize(); + nHeight = poSrcBand->GetYSize(); + + if( nSampleStep >= nHeight || nSampleStep < 1 ) + nSampleStep = 1; + + bComplex = GDALDataTypeIsComplex(eType); + if( bComplex ) + { + pafData = (float *) VSIMalloc(nWidth * 2 * sizeof(float)); + eWrkType = GDT_CFloat32; + } + else + { + pafData = (float *) VSIMalloc(nWidth * sizeof(float)); + eWrkType = GDT_Float32; + } + + if( pafData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALComputeBandStats: Out of memory for buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Loop over all sample lines. */ +/* -------------------------------------------------------------------- */ + for( iLine = 0; iLine < nHeight; iLine += nSampleStep ) + { + int iPixel; + + if( !pfnProgress( iLine / (double) nHeight, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + + CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nWidth, 1, + pafData, nWidth, 1, eWrkType, + 0, 0 ); + if ( eErr != CE_None ) + { + CPLFree( pafData ); + return eErr; + } + + for( iPixel = 0; iPixel < nWidth; iPixel++ ) + { + float fValue; + + if( bComplex ) + { + // Compute the magnitude of the complex value. + + fValue = (float) + sqrt(pafData[iPixel*2 ] * pafData[iPixel*2 ] + + pafData[iPixel*2+1] * pafData[iPixel*2+1]); + } + else + { + fValue = pafData[iPixel]; + } + + dfSum += fValue; + dfSum2 += fValue * fValue; + } + + nSamples += nWidth; + } + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Produce the result values. */ +/* -------------------------------------------------------------------- */ + if( pdfMean != NULL ) + *pdfMean = dfSum / nSamples; + + if( pdfStdDev != NULL ) + { + double dfMean = dfSum / nSamples; + + *pdfStdDev = sqrt((dfSum2 / nSamples) - (dfMean * dfMean)); + } + + CPLFree( pafData ); + + return CE_None; +} + +/************************************************************************/ +/* GDALOverviewMagnitudeCorrection() */ +/* */ +/* Correct the mean and standard deviation of the overviews of */ +/* the given band to match the base layer approximately. */ +/************************************************************************/ + +CPLErr +GDALOverviewMagnitudeCorrection( GDALRasterBandH hBaseBand, + int nOverviewCount, + GDALRasterBandH *pahOverviews, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBaseBand, "GDALOverviewMagnitudeCorrection", CE_Failure ); + + CPLErr eErr; + double dfOrigMean, dfOrigStdDev; + +/* -------------------------------------------------------------------- */ +/* Compute mean/stddev for source raster. */ +/* -------------------------------------------------------------------- */ + eErr = GDALComputeBandStats( hBaseBand, 2, &dfOrigMean, &dfOrigStdDev, + pfnProgress, pProgressData ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Loop on overview bands. */ +/* -------------------------------------------------------------------- */ + int iOverview; + + for( iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + { + GDALRasterBand *poOverview = (GDALRasterBand *)pahOverviews[iOverview]; + double dfOverviewMean, dfOverviewStdDev; + double dfGain; + + eErr = GDALComputeBandStats( pahOverviews[iOverview], 1, + &dfOverviewMean, &dfOverviewStdDev, + pfnProgress, pProgressData ); + + if( eErr != CE_None ) + return eErr; + + if( dfOrigStdDev < 0.0001 ) + dfGain = 1.0; + else + dfGain = dfOrigStdDev / dfOverviewStdDev; + +/* -------------------------------------------------------------------- */ +/* Apply gain and offset. */ +/* -------------------------------------------------------------------- */ + GDALDataType eWrkType, eType = poOverview->GetRasterDataType(); + int iLine, nWidth, nHeight, bComplex; + float *pafData; + + nWidth = poOverview->GetXSize(); + nHeight = poOverview->GetYSize(); + + bComplex = GDALDataTypeIsComplex(eType); + if( bComplex ) + { + pafData = (float *) VSIMalloc2(nWidth, 2 * sizeof(float)); + eWrkType = GDT_CFloat32; + } + else + { + pafData = (float *) VSIMalloc2(nWidth, sizeof(float)); + eWrkType = GDT_Float32; + } + + if( pafData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALOverviewMagnitudeCorrection: Out of memory for buffer." ); + return CE_Failure; + } + + for( iLine = 0; iLine < nHeight; iLine++ ) + { + int iPixel; + + if( !pfnProgress( iLine / (double) nHeight, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + + poOverview->RasterIO( GF_Read, 0, iLine, nWidth, 1, + pafData, nWidth, 1, eWrkType, + 0, 0 ); + + for( iPixel = 0; iPixel < nWidth; iPixel++ ) + { + if( bComplex ) + { + pafData[iPixel*2] *= (float) dfGain; + pafData[iPixel*2+1] *= (float) dfGain; + } + else + { + pafData[iPixel] = (float) + ((pafData[iPixel]-dfOverviewMean)*dfGain + dfOrigMean); + + } + } + + poOverview->RasterIO( GF_Write, 0, iLine, nWidth, 1, + pafData, nWidth, 1, eWrkType, + 0, 0 ); + } + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + + CPLFree( pafData ); + } + + return CE_None; +} diff --git a/ogr/rasterio.cpp b/ogr/rasterio.cpp new file mode 100644 index 0000000..f53337c --- /dev/null +++ b/ogr/rasterio.cpp @@ -0,0 +1,3218 @@ +/****************************************************************************** + * $Id: rasterio.cpp 27119 2014-04-03 18:48:27Z rouault $ + * + * Project: GDAL Core + * Purpose: Contains default implementation of GDALRasterBand::IRasterIO() + * and supporting functions of broader utility. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +// Define a list of "C++" compilers that have broken template support or +// broken scoping so we can fall back on the legacy implementation of +// GDALCopyWords +#define NOT_BROKEN_COMPILER \ + (!(defined(_MSC_VER) && _MSC_VER <= 1200) && !defined(__BORLANDC__) && \ + !defined(__SUNPRO_CC)) + +#if NOT_BROKEN_COMPILER +#include +#include + +// For now, work around MSVC++ 6.0's broken template support. If this value +// is not defined, the old GDALCopyWords implementation is used. +#define USE_NEW_COPYWORDS 1 +#endif + + +CPL_CVSID("$Id: rasterio.cpp 27119 2014-04-03 18:48:27Z rouault $"); + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* Default internal implementation of RasterIO() ... utilizes */ +/* the Block access methods to satisfy the request. This would */ +/* normally only be overridden by formats with overviews. */ +/************************************************************************/ + +CPLErr GDALRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ) + +{ + int nBandDataSize = GDALGetDataTypeSize( eDataType ) / 8; + int nBufDataSize = GDALGetDataTypeSize( eBufType ) / 8; + GByte *pabySrcBlock = NULL; + GDALRasterBlock *poBlock = NULL; + int nLBlockX=-1, nLBlockY=-1, iBufYOff, iBufXOff, iSrcY; + + if( eRWFlag == GF_Write && eFlushBlockErr != CE_None ) + { + CPLError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + CPLErr eErr = eFlushBlockErr; + eFlushBlockErr = CE_None; + return eErr; + } + +/* ==================================================================== */ +/* A common case is the data requested with the destination */ +/* is packed, and the block width is the raster width. */ +/* ==================================================================== */ + if( nPixelSpace == nBufDataSize + && nLineSpace == nPixelSpace * nXSize + && nBlockXSize == GetXSize() + && nBufXSize == nXSize + && nBufYSize == nYSize ) + { +// printf( "IRasterIO(%d,%d,%d,%d) rw=%d case 1\n", +// nXOff, nYOff, nXSize, nYSize, +// (int) eRWFlag ); + + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff++ ) + { + int nSrcByteOffset; + + iSrcY = iBufYOff + nYOff; + + if( iSrcY < nLBlockY * nBlockYSize + || iSrcY >= (nLBlockY+1) * nBlockYSize ) + { + nLBlockY = iSrcY / nBlockYSize; + int bJustInitialize = + eRWFlag == GF_Write + && nXOff == 0 && nXSize == nBlockXSize + && nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize; + + if( poBlock ) + poBlock->DropLock(); + + poBlock = GetLockedBlockRef( 0, nLBlockY, bJustInitialize ); + if( poBlock == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetBlockRef failed at X block offset %d, " + "Y block offset %d", 0, nLBlockY ); + return( CE_Failure ); + } + + if( eRWFlag == GF_Write ) + poBlock->MarkDirty(); + + pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + } + + nSrcByteOffset = ((iSrcY-nLBlockY*nBlockYSize)*nBlockXSize + nXOff) + * nBandDataSize; + + if( eDataType == eBufType ) + { + if( eRWFlag == GF_Read ) + memcpy( ((GByte *) pData) + (size_t)iBufYOff * nLineSpace, + pabySrcBlock + nSrcByteOffset, + nLineSpace ); + else + memcpy( pabySrcBlock + nSrcByteOffset, + ((GByte *) pData) + (size_t)iBufYOff * nLineSpace, + nLineSpace ); + } + else + { + /* type to type conversion */ + + if( eRWFlag == GF_Read ) + GDALCopyWords( pabySrcBlock + nSrcByteOffset, + eDataType, nBandDataSize, + ((GByte *) pData) + (size_t)iBufYOff * nLineSpace, + eBufType, nPixelSpace, nBufXSize ); + else + GDALCopyWords( ((GByte *) pData) + (size_t)iBufYOff * nLineSpace, + eBufType, nPixelSpace, + pabySrcBlock + nSrcByteOffset, + eDataType, nBandDataSize, nBufXSize ); + } + } + + if( poBlock ) + poBlock->DropLock(); + + return CE_None; + } + +/* ==================================================================== */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* ==================================================================== */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 && eRWFlag == GF_Read ) + { + int nOverview; + + nOverview = + GDALBandGetBestOverviewLevel(this, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize); + if (nOverview >= 0) + { + GDALRasterBand* poOverviewBand = GetOverview(nOverview); + if (poOverviewBand == NULL) + return CE_Failure; + + return poOverviewBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace ); + } + } + + + if( eRWFlag == GF_Read && + nBufXSize < nXSize / 100 && nBufYSize < nYSize / 100 && + nPixelSpace == nBufDataSize && + nLineSpace == nPixelSpace * nBufXSize && + CSLTestBoolean(CPLGetConfigOption("GDAL_NO_COSTLY_OVERVIEW", "NO")) ) + { + memset(pData, 0, nLineSpace * nBufYSize); + return CE_None; + } + + +/* ==================================================================== */ +/* The second case when we don't need subsample data but likely */ +/* need data type conversion. */ +/* ==================================================================== */ + int iSrcX; + + if ( /* nPixelSpace == nBufDataSize + && */ nXSize == nBufXSize + && nYSize == nBufYSize ) + { +// printf( "IRasterIO(%d,%d,%d,%d) rw=%d case 2\n", +// nXOff, nYOff, nXSize, nYSize, +// (int) eRWFlag ); + +/* -------------------------------------------------------------------- */ +/* Loop over buffer computing source locations. */ +/* -------------------------------------------------------------------- */ + int nLBlockXStart, nXSpanEnd; + + // Calculate starting values out of loop + nLBlockXStart = nXOff / nBlockXSize; + nXSpanEnd = nBufXSize + nXOff; + + int nYInc = 0; + for( iBufYOff = 0, iSrcY = nYOff; iBufYOff < nBufYSize; iBufYOff+=nYInc, iSrcY+=nYInc ) + { + size_t iBufOffset, iSrcOffset; + int nXSpan; + + iBufOffset = (size_t)iBufYOff * nLineSpace; + nLBlockY = iSrcY / nBlockYSize; + nLBlockX = nLBlockXStart; + iSrcX = nXOff; + while( iSrcX < nXSpanEnd ) + { + int nXSpanSize; + + nXSpan = (nLBlockX + 1) * nBlockXSize; + nXSpan = ( ( nXSpan < nXSpanEnd )?nXSpan:nXSpanEnd ) - iSrcX; + nXSpanSize = nXSpan * nPixelSpace; + + int bJustInitialize = + eRWFlag == GF_Write + && nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize + && nXOff <= nLBlockX * nBlockXSize + && nXOff + nXSize >= (nLBlockX+1) * nBlockXSize; + +// printf( "bJustInitialize = %d (%d,%d,%d,%d)\n", +// bJustInitialize, +// nYOff, nYSize, +// nLBlockY, nBlockYSize ); +// bJustInitialize = FALSE; + + +/* -------------------------------------------------------------------- */ +/* Ensure we have the appropriate block loaded. */ +/* -------------------------------------------------------------------- */ + poBlock = GetLockedBlockRef( nLBlockX, nLBlockY, bJustInitialize ); + if( !poBlock ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetBlockRef failed at X block offset %d, " + "Y block offset %d", nLBlockX, nLBlockY ); + return( CE_Failure ); + } + + if( eRWFlag == GF_Write ) + poBlock->MarkDirty(); + + pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Copy over this chunk of data. */ +/* -------------------------------------------------------------------- */ + iSrcOffset = ((size_t)iSrcX - (size_t)nLBlockX*nBlockXSize + + ((size_t)(iSrcY) - (size_t)nLBlockY*nBlockYSize) * nBlockXSize)*nBandDataSize; + /* Fill up as many rows as possible for the loaded block */ + int kmax = MIN(nBlockYSize - (iSrcY % nBlockYSize), nBufYSize - iBufYOff); + for(int k=0; kDropLock(); + poBlock = NULL; + } + + /* Compute the increment to go on a block boundary */ + nYInc = nBlockYSize - (iSrcY % nBlockYSize); + } + + return CE_None; + } + +/* ==================================================================== */ +/* Loop reading required source blocks to satisfy output */ +/* request. This is the most general implementation. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Compute stepping increment. */ +/* -------------------------------------------------------------------- */ + double dfSrcXInc, dfSrcYInc; + dfSrcXInc = nXSize / (double) nBufXSize; + dfSrcYInc = nYSize / (double) nBufYSize; + + +// printf( "IRasterIO(%d,%d,%d,%d) rw=%d case 3\n", +// nXOff, nYOff, nXSize, nYSize, +// (int) eRWFlag ); + if (eRWFlag == GF_Write) + { +/* -------------------------------------------------------------------- */ +/* Write case */ +/* Loop over raster window computing source locations in the buffer. */ +/* -------------------------------------------------------------------- */ + int iDstX, iDstY; + GByte* pabyDstBlock = NULL; + + for( iDstY = nYOff; iDstY < nYOff + nYSize; iDstY ++) + { + size_t iBufOffset, iDstOffset; + iBufYOff = (int)((iDstY - nYOff) / dfSrcYInc); + + for( iDstX = nXOff; iDstX < nXOff + nXSize; iDstX ++) + { + iBufXOff = (int)((iDstX - nXOff) / dfSrcXInc); + iBufOffset = (size_t)iBufYOff * nLineSpace + iBufXOff * nPixelSpace; + + /* -------------------------------------------------------------------- */ + /* Ensure we have the appropriate block loaded. */ + /* -------------------------------------------------------------------- */ + if( iDstX < nLBlockX * nBlockXSize + || iDstX >= (nLBlockX+1) * nBlockXSize + || iDstY < nLBlockY * nBlockYSize + || iDstY >= (nLBlockY+1) * nBlockYSize ) + { + nLBlockX = iDstX / nBlockXSize; + nLBlockY = iDstY / nBlockYSize; + + int bJustInitialize = + nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize + && nXOff <= nLBlockX * nBlockXSize + && nXOff + nXSize >= (nLBlockX+1) * nBlockXSize; + + if( poBlock != NULL ) + poBlock->DropLock(); + + poBlock = GetLockedBlockRef( nLBlockX, nLBlockY, + bJustInitialize ); + if( poBlock == NULL ) + { + return( CE_Failure ); + } + + poBlock->MarkDirty(); + + pabyDstBlock = (GByte *) poBlock->GetDataRef(); + if( pabyDstBlock == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + } + + /* -------------------------------------------------------------------- */ + /* Copy over this pixel of data. */ + /* -------------------------------------------------------------------- */ + iDstOffset = ((size_t)iDstX - (size_t)nLBlockX*nBlockXSize + + ((size_t)iDstY - (size_t)nLBlockY*nBlockYSize) * nBlockXSize)*nBandDataSize; + + if( eDataType == eBufType ) + { + memcpy( pabyDstBlock + iDstOffset, + ((GByte *) pData) + iBufOffset, nBandDataSize ); + } + else + { + /* type to type conversion ... ouch, this is expensive way + of handling single words */ + + GDALCopyWords( ((GByte *) pData) + iBufOffset, eBufType, 0, + pabyDstBlock + iDstOffset, eDataType, 0, + 1 ); + } + } + } + } + else + { + double dfSrcX, dfSrcY; + int nLimitBlockY = 0; + int bByteCopy = ( eDataType == eBufType && nBandDataSize == 1); + int nStartBlockX = -nBlockXSize; + +/* -------------------------------------------------------------------- */ +/* Read case */ +/* Loop over buffer computing source locations. */ +/* -------------------------------------------------------------------- */ + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff++ ) + { + size_t iBufOffset, iSrcOffset; + + dfSrcY = (iBufYOff+0.5) * dfSrcYInc + nYOff; + dfSrcX = 0.5 * dfSrcXInc + nXOff; + iSrcY = (int) dfSrcY; + + iBufOffset = (size_t)iBufYOff * nLineSpace; + + if( iSrcY >= nLimitBlockY ) + { + nLBlockY = iSrcY / nBlockYSize; + nLimitBlockY = (nLBlockY + 1) * nBlockYSize; + nStartBlockX = -nBlockXSize; /* make sure a new block is loaded */ + } + else if( (int)dfSrcX < nStartBlockX ) + nStartBlockX = -nBlockXSize; /* make sure a new block is loaded */ + + size_t iSrcOffsetCst = (iSrcY - nLBlockY*nBlockYSize) * (size_t)nBlockXSize; + + for( iBufXOff = 0; iBufXOff < nBufXSize; iBufXOff++, dfSrcX += dfSrcXInc ) + { + iSrcX = (int) dfSrcX; + int nDiffX = iSrcX - nStartBlockX; + + /* -------------------------------------------------------------------- */ + /* Ensure we have the appropriate block loaded. */ + /* -------------------------------------------------------------------- */ + if( nDiffX >= nBlockXSize ) + { + nLBlockX = iSrcX / nBlockXSize; + nStartBlockX = nLBlockX * nBlockXSize; + nDiffX = iSrcX - nStartBlockX; + + if( poBlock != NULL ) + poBlock->DropLock(); + + poBlock = GetLockedBlockRef( nLBlockX, nLBlockY, + FALSE ); + if( poBlock == NULL ) + { + return( CE_Failure ); + } + + pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + } + + /* -------------------------------------------------------------------- */ + /* Copy over this pixel of data. */ + /* -------------------------------------------------------------------- */ + iSrcOffset = ((size_t)nDiffX + iSrcOffsetCst)*nBandDataSize; + + if( bByteCopy ) + { + ((GByte *) pData)[iBufOffset] = pabySrcBlock[iSrcOffset]; + } + else if( eDataType == eBufType ) + { + memcpy( ((GByte *) pData) + iBufOffset, + pabySrcBlock + iSrcOffset, nBandDataSize ); + } + else + { + /* type to type conversion ... ouch, this is expensive way + of handling single words */ + GDALCopyWords( pabySrcBlock + iSrcOffset, eDataType, 0, + ((GByte *) pData) + iBufOffset, eBufType, 0, + 1 ); + } + + iBufOffset += nPixelSpace; + } + } + } + + if( poBlock != NULL ) + poBlock->DropLock(); + + return( CE_None ); +} + +/************************************************************************/ +/* GDALSwapWords() */ +/************************************************************************/ + +/** + * Byte swap words in-place. + * + * This function will byte swap a set of 2, 4 or 8 byte words "in place" in + * a memory array. No assumption is made that the words being swapped are + * word aligned in memory. Use the CPL_LSB and CPL_MSB macros from cpl_port.h + * to determine if the current platform is big endian or little endian. Use + * The macros like CPL_SWAP32() to byte swap single values without the overhead + * of a function call. + * + * @param pData pointer to start of data buffer. + * @param nWordSize size of words being swapped in bytes. Normally 2, 4 or 8. + * @param nWordCount the number of words to be swapped in this call. + * @param nWordSkip the byte offset from the start of one word to the start of + * the next. For packed buffers this is the same as nWordSize. + */ + +void CPL_STDCALL GDALSwapWords( void *pData, int nWordSize, int nWordCount, + int nWordSkip ) + +{ + if (nWordCount > 0) + VALIDATE_POINTER0( pData , "GDALSwapWords" ); + + int i; + GByte *pabyData = (GByte *) pData; + + switch( nWordSize ) + { + case 1: + break; + + case 2: + CPLAssert( nWordSkip >= 2 || nWordCount == 1 ); + for( i = 0; i < nWordCount; i++ ) + { + GByte byTemp; + + byTemp = pabyData[0]; + pabyData[0] = pabyData[1]; + pabyData[1] = byTemp; + + pabyData += nWordSkip; + } + break; + + case 4: + CPLAssert( nWordSkip >= 4 || nWordCount == 1 ); + for( i = 0; i < nWordCount; i++ ) + { + GByte byTemp; + + byTemp = pabyData[0]; + pabyData[0] = pabyData[3]; + pabyData[3] = byTemp; + + byTemp = pabyData[1]; + pabyData[1] = pabyData[2]; + pabyData[2] = byTemp; + + pabyData += nWordSkip; + } + break; + + case 8: + CPLAssert( nWordSkip >= 8 || nWordCount == 1 ); + for( i = 0; i < nWordCount; i++ ) + { + GByte byTemp; + + byTemp = pabyData[0]; + pabyData[0] = pabyData[7]; + pabyData[7] = byTemp; + + byTemp = pabyData[1]; + pabyData[1] = pabyData[6]; + pabyData[6] = byTemp; + + byTemp = pabyData[2]; + pabyData[2] = pabyData[5]; + pabyData[5] = byTemp; + + byTemp = pabyData[3]; + pabyData[3] = pabyData[4]; + pabyData[4] = byTemp; + + pabyData += nWordSkip; + } + break; + + default: + CPLAssert( FALSE ); + } +} + +#ifdef USE_NEW_COPYWORDS +// Place the new GDALCopyWords helpers in an anonymous namespace +namespace { +/************************************************************************/ +/* GetDataLimits() */ +/************************************************************************/ +/** + * Compute the limits of values that can be placed in Tout in terms of + * Tin. Usually used for output clamping, when the output data type's + * limits are stable relative to the input type (i.e. no roundoff error). + * + * @param tMaxValue the returned maximum value + * @param tMinValue the returned minimum value + */ + +template +inline void GetDataLimits(Tin &tMaxValue, Tin &tMinValue) +{ + tMaxValue = std::numeric_limits::max(); + tMinValue = std::numeric_limits::min(); + + // Compute the actual minimum value of Tout in terms of Tin. + if (std::numeric_limits::is_signed && std::numeric_limits::is_integer) + { + // the minimum value is less than zero + if (std::numeric_limits::digits < std::numeric_limits::digits || + !std::numeric_limits::is_integer) + { + // Tout is smaller than Tin, so we need to clamp values in input + // to the range of Tout's min/max values + if (std::numeric_limits::is_signed) + { + tMinValue = static_cast(std::numeric_limits::min()); + } + tMaxValue = static_cast(std::numeric_limits::max()); + } + } + else if (std::numeric_limits::is_integer) + { + // the output is unsigned, so we just need to determine the max + if (std::numeric_limits::digits <= std::numeric_limits::digits) + { + // Tout is smaller than Tin, so we need to clamp the input values + // to the range of Tout's max + tMaxValue = static_cast(std::numeric_limits::max()); + } + tMinValue = 0; + } + +} + +/************************************************************************/ +/* ClampValue() */ +/************************************************************************/ +/** + * Clamp values of type T to a specified range + * + * @param tValue the value + * @param tMax the max value + * @param tMin the min value + */ +template +inline T ClampValue(const T tValue, const T tMax, const T tMin) +{ + return tValue > tMax ? tMax : + tValue < tMin ? tMin : tValue; +} + +/************************************************************************/ +/* CopyWord() */ +/************************************************************************/ +/** + * Copy a single word, optionally rounding if appropriate (i.e. going + * from the float to the integer case). Note that this is the function + * you should specialize if you're adding a new data type. + * + * @param tValueIn value of type Tin; the input value to be converted + * @param tValueOut value of type Tout; the output value + */ + +template +inline void CopyWord(const Tin tValueIn, Tout &tValueOut) +{ + Tin tMaxVal, tMinVal; + GetDataLimits(tMaxVal, tMinVal); + tValueOut = static_cast(ClampValue(tValueIn, tMaxVal, tMinVal)); +} + +template +inline void CopyWord(const Tin tValueIn, float &fValueOut) +{ + fValueOut = (float) tValueIn; +} + +template +inline void CopyWord(const Tin tValueIn, double &dfValueOut) +{ + dfValueOut = tValueIn; +} + +inline void CopyWord(const double dfValueIn, double &dfValueOut) +{ + dfValueOut = dfValueIn; +} + +inline void CopyWord(const float fValueIn, float &fValueOut) +{ + fValueOut = fValueIn; +} + +inline void CopyWord(const float fValueIn, double &dfValueOut) +{ + dfValueOut = fValueIn; +} + +inline void CopyWord(const double dfValueIn, float &fValueOut) +{ + fValueOut = static_cast(dfValueIn); +} + +template +inline void CopyWord(const float fValueIn, Tout &tValueOut) +{ + float fMaxVal, fMinVal; + GetDataLimits(fMaxVal, fMinVal); + tValueOut = static_cast( + ClampValue(fValueIn + 0.5f, fMaxVal, fMinVal)); +} + +template +inline void CopyWord(const double dfValueIn, Tout &tValueOut) +{ + double dfMaxVal, dfMinVal; + GetDataLimits(dfMaxVal, dfMinVal); + tValueOut = static_cast( + ClampValue(dfValueIn + 0.5, dfMaxVal, dfMinVal)); +} + +inline void CopyWord(const double dfValueIn, int &nValueOut) +{ + double dfMaxVal, dfMinVal; + GetDataLimits(dfMaxVal, dfMinVal); + double dfValue = dfValueIn >= 0.0 ? dfValueIn + 0.5 : + dfValueIn - 0.5; + nValueOut = static_cast( + ClampValue(dfValue, dfMaxVal, dfMinVal)); +} + +inline void CopyWord(const float fValueIn, short &nValueOut) +{ + float fMaxVal, fMinVal; + GetDataLimits(fMaxVal, fMinVal); + float fValue = fValueIn >= 0.0f ? fValueIn + 0.5f : + fValueIn - 0.5f; + nValueOut = static_cast( + ClampValue(fValue, fMaxVal, fMinVal)); +} + +inline void CopyWord(const double dfValueIn, short &nValueOut) +{ + double dfMaxVal, dfMinVal; + GetDataLimits(dfMaxVal, dfMinVal); + double dfValue = dfValueIn > 0.0 ? dfValueIn + 0.5 : + dfValueIn - 0.5; + nValueOut = static_cast( + ClampValue(dfValue, dfMaxVal, dfMinVal)); +} + +// Roundoff occurs for Float32 -> int32 for max/min. Overload CopyWord +// specifically for this case. +inline void CopyWord(const float fValueIn, int &nValueOut) +{ + if (fValueIn >= static_cast(std::numeric_limits::max())) + { + nValueOut = std::numeric_limits::max(); + } + else if (fValueIn <= static_cast(std::numeric_limits::min())) + { + nValueOut = std::numeric_limits::min(); + } + else + { + nValueOut = static_cast(fValueIn > 0.0f ? + fValueIn + 0.5f : fValueIn - 0.5f); + } +} + +// Roundoff occurs for Float32 -> uint32 for max. Overload CopyWord +// specifically for this case. +inline void CopyWord(const float fValueIn, unsigned int &nValueOut) +{ + if (fValueIn >= static_cast(std::numeric_limits::max())) + { + nValueOut = std::numeric_limits::max(); + } + else if (fValueIn <= static_cast(std::numeric_limits::min())) + { + nValueOut = std::numeric_limits::min(); + } + else + { + nValueOut = static_cast(fValueIn + 0.5f); + } +} + +/************************************************************************/ +/* GDALCopyWordsT() */ +/************************************************************************/ +/** + * Template function, used to copy data from pSrcData into buffer + * pDstData, with stride nSrcPixelStride in the source data and + * stride nDstPixelStride in the destination data. This template can + * deal with the case where the input data type is real or complex and + * the output is real. + * + * @param pSrcData the source data buffer + * @param nSrcPixelStride the stride, in the buffer pSrcData for pixels + * of interest. + * @param pDstData the destination buffer. + * @param nDstPixelStride the stride in the buffer pDstData for pixels of + * interest. + * @param nWordCount the total number of pixel words to copy + * + * @code + * // Assume an input buffer of type GUInt16 named pBufferIn + * GByte *pBufferOut = new GByte[numBytesOut]; + * GDALCopyWordsT(pSrcData, 2, pDstData, 1, numBytesOut); + * @code + * @note + * This is a private function, and should not be exposed outside of rasterio.cpp. + * External users should call the GDALCopyWords driver function. + * @note + */ + +template +static void GDALCopyWordsT(const Tin* const pSrcData, int nSrcPixelStride, + Tout* const pDstData, int nDstPixelStride, + int nWordCount) +{ + std::ptrdiff_t nDstOffset = 0; + + const char* const pSrcDataPtr = reinterpret_cast(pSrcData); + char* const pDstDataPtr = reinterpret_cast(pDstData); + for (std::ptrdiff_t n = 0; n < nWordCount; n++) + { + const Tin tValue = *reinterpret_cast(pSrcDataPtr + (n * nSrcPixelStride)); + Tout* const pOutPixel = reinterpret_cast(pDstDataPtr + nDstOffset); + + CopyWord(tValue, *pOutPixel); + + nDstOffset += nDstPixelStride; + } +} + +/************************************************************************/ +/* GDALCopyWordsComplexT() */ +/************************************************************************/ +/** + * Template function, used to copy data from pSrcData into buffer + * pDstData, with stride nSrcPixelStride in the source data and + * stride nDstPixelStride in the destination data. Deals with the + * complex case, where input is complex and output is complex. + * + * @param pSrcData the source data buffer + * @param nSrcPixelStride the stride, in the buffer pSrcData for pixels + * of interest. + * @param pDstData the destination buffer. + * @param nDstPixelStride the stride in the buffer pDstData for pixels of + * interest. + * @param nWordCount the total number of pixel words to copy + * + */ +template +inline void GDALCopyWordsComplexT(const Tin* const pSrcData, int nSrcPixelStride, + Tout* const pDstData, int nDstPixelStride, + int nWordCount) +{ + std::ptrdiff_t nDstOffset = 0; + const char* const pSrcDataPtr = reinterpret_cast(pSrcData); + char* const pDstDataPtr = reinterpret_cast(pDstData); + + // Determine the minimum and maximum value we can have based + // on the constraints of Tin and Tout. + Tin tMaxValue, tMinValue; + GetDataLimits(tMaxValue, tMinValue); + + for (std::ptrdiff_t n = 0; n < nWordCount; n++) + { + const Tin* const pPixelIn = reinterpret_cast(pSrcDataPtr + n * nSrcPixelStride); + Tout* const pPixelOut = reinterpret_cast(pDstDataPtr + nDstOffset); + + CopyWord(pPixelIn[0], pPixelOut[0]); + CopyWord(pPixelIn[1], pPixelOut[1]); + + nDstOffset += nDstPixelStride; + } +} + +/************************************************************************/ +/* GDALCopyWordsComplexOutT() */ +/************************************************************************/ +/** + * Template function, used to copy data from pSrcData into buffer + * pDstData, with stride nSrcPixelStride in the source data and + * stride nDstPixelStride in the destination data. Deals with the + * case where the value is real coming in, but complex going out. + * + * @param pSrcData the source data buffer + * @param nSrcPixelStride the stride, in the buffer pSrcData for pixels + * of interest, in bytes. + * @param pDstData the destination buffer. + * @param nDstPixelStride the stride in the buffer pDstData for pixels of + * interest, in bytes. + * @param nWordCount the total number of pixel words to copy + * + */ +template +inline void GDALCopyWordsComplexOutT(const Tin* const pSrcData, int nSrcPixelStride, + Tout* const pDstData, int nDstPixelStride, + int nWordCount) +{ + std::ptrdiff_t nDstOffset = 0; + + const Tout tOutZero = static_cast(0); + + const char* const pSrcDataPtr = reinterpret_cast(pSrcData); + char* const pDstDataPtr = reinterpret_cast(pDstData); + + for (std::ptrdiff_t n = 0; n < nWordCount; n++) + { + const Tin tValue = *reinterpret_cast(pSrcDataPtr + n * nSrcPixelStride); + Tout* const pPixelOut = reinterpret_cast(pDstDataPtr + nDstOffset); + CopyWord(tValue, *pPixelOut); + + pPixelOut[1] = tOutZero; + + nDstOffset += nDstPixelStride; + } +} + +/************************************************************************/ +/* GDALCopyWordsFromT() */ +/************************************************************************/ +/** + * Template driver function. Given the input type T, call the appropriate + * GDALCopyWordsT function template for the desired output type. You should + * never call this function directly (call GDALCopyWords instead). + * + * @param pSrcData source data buffer + * @param nSrcPixelStride pixel stride in input buffer, in pixel words + * @param bInComplex input is complex + * @param pDstData destination data buffer + * @param eDstType destination data type + * @param nDstPixelStride pixel stride in output buffer, in pixel words + * @param nWordCount number of pixel words to be copied + */ +template +inline void GDALCopyWordsFromT(const T* const pSrcData, int nSrcPixelStride, bool bInComplex, + void *pDstData, GDALDataType eDstType, int nDstPixelStride, + int nWordCount) +{ + switch (eDstType) + { + case GDT_Byte: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_UInt16: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Int16: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_UInt32: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Int32: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Float32: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Float64: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_CInt16: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_CInt32: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_CFloat32: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_CFloat64: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_Unknown: + default: + CPLAssert(FALSE); + } +} + +} // end anonymous namespace +#endif + +/************************************************************************/ +/* GDALReplicateWord() */ +/************************************************************************/ + +void GDALReplicateWord(void *pSrcData, GDALDataType eSrcType, + void *pDstData, GDALDataType eDstType, int nDstPixelStride, + int nWordCount) +{ +/* ----------------------------------------------------------------------- */ +/* Special case when the source data is always the same value */ +/* (for VRTSourcedRasterBand::IRasterIO and VRTDerivedRasterBand::IRasterIO*/ +/* for example) */ +/* ----------------------------------------------------------------------- */ + /* Let the general translation case do the necessary conversions */ + /* on the first destination element */ + GDALCopyWords(pSrcData, eSrcType, 0, + pDstData, eDstType, nDstPixelStride, + 1 ); + + /* Now copy the first element to the nWordCount - 1 following destination */ + /* elements */ + nWordCount--; + GByte *pabyDstWord = ((GByte *)pDstData) + nDstPixelStride; + + switch (eDstType) + { + case GDT_Byte: + { + if (nDstPixelStride == 1) + { + if (nWordCount > 0) + memset(pabyDstWord, *(GByte*)pDstData, nWordCount); + } + else + { + GByte valSet = *(GByte*)pDstData; + while(nWordCount--) + { + *pabyDstWord = valSet; + pabyDstWord += nDstPixelStride; + } + } + break; + } + +#define CASE_DUPLICATE_SIMPLE(enum_type, c_type) \ + case enum_type:\ + { \ + c_type valSet = *(c_type*)pDstData; \ + while(nWordCount--) \ + { \ + *(c_type*)pabyDstWord = valSet; \ + pabyDstWord += nDstPixelStride; \ + } \ + break; \ + } + + CASE_DUPLICATE_SIMPLE(GDT_UInt16, GUInt16) + CASE_DUPLICATE_SIMPLE(GDT_Int16, GInt16) + CASE_DUPLICATE_SIMPLE(GDT_UInt32, GUInt32) + CASE_DUPLICATE_SIMPLE(GDT_Int32, GInt32) + CASE_DUPLICATE_SIMPLE(GDT_Float32,float) + CASE_DUPLICATE_SIMPLE(GDT_Float64,double) + +#define CASE_DUPLICATE_COMPLEX(enum_type, c_type) \ + case enum_type:\ + { \ + c_type valSet1 = ((c_type*)pDstData)[0]; \ + c_type valSet2 = ((c_type*)pDstData)[1]; \ + while(nWordCount--) \ + { \ + ((c_type*)pabyDstWord)[0] = valSet1; \ + ((c_type*)pabyDstWord)[1] = valSet2; \ + pabyDstWord += nDstPixelStride; \ + } \ + break; \ + } + + CASE_DUPLICATE_COMPLEX(GDT_CInt16, GInt16) + CASE_DUPLICATE_COMPLEX(GDT_CInt32, GInt32) + CASE_DUPLICATE_COMPLEX(GDT_CFloat32, float) + CASE_DUPLICATE_COMPLEX(GDT_CFloat64, double) + + default: + CPLAssert( FALSE ); + } +} + +/************************************************************************/ +/* GDALCopyWords() */ +/************************************************************************/ + +/** + * Copy pixel words from buffer to buffer. + * + * This function is used to copy pixel word values from one memory buffer + * to another, with support for conversion between data types, and differing + * step factors. The data type conversion is done using the normal GDAL + * rules. Values assigned to a lower range integer type are clipped. For + * instance assigning GDT_Int16 values to a GDT_Byte buffer will cause values + * less the 0 to be set to 0, and values larger than 255 to be set to 255. + * Assignment from floating point to integer uses default C type casting + * semantics. Assignment from non-complex to complex will result in the + * imaginary part being set to zero on output. Assigment from complex to + * non-complex will result in the complex portion being lost and the real + * component being preserved (not magnitidue!). + * + * No assumptions are made about the source or destination words occuring + * on word boundaries. It is assumed that all values are in native machine + * byte order. + * + * @param pSrcData Pointer to source data to be converted. + * @param eSrcType the source data type (see GDALDataType enum) + * @param nSrcPixelStride Source pixel stride (i.e. distance between 2 words), in bytes + * @param pDstData Pointer to buffer where destination data should go + * @param eDstType the destination data type (see GDALDataType enum) + * @param nDstPixelStride Destination pixel stride (i.e. distance between 2 words), in bytes + * @param nWordCount number of words to be copied + * + * + * @note + * When adding a new data type to GDAL, you must do the following to + * support it properly within the GDALCopyWords function: + * 1. Add the data type to the switch on eSrcType in GDALCopyWords. + * This should invoke the appropriate GDALCopyWordsFromT wrapper. + * 2. Add the data type to the switch on eDstType in GDALCopyWordsFromT. + * This should call the appropriate GDALCopyWordsT template. + * 3. If appropriate, overload the appropriate CopyWord template in the + * above namespace. This will ensure that any conversion issues are + * handled (cases like the float -> int32 case, where the min/max) + * values are subject to roundoff error. + */ + +void CPL_STDCALL +GDALCopyWords( void * pSrcData, GDALDataType eSrcType, int nSrcPixelStride, + void * pDstData, GDALDataType eDstType, int nDstPixelStride, + int nWordCount ) + +{ + // Deal with the case where we're replicating a single word into the + // provided buffer + if (nSrcPixelStride == 0 && nWordCount > 1) + { + GDALReplicateWord(pSrcData, eSrcType, pDstData, eDstType, nDstPixelStride, nWordCount); + return; + } + +#ifdef USE_NEW_COPYWORDS + + int nSrcDataTypeSize = GDALGetDataTypeSize(eSrcType) / 8; + // Let memcpy() handle the case where we're copying a packed buffer + // of pixels. + if (eSrcType == eDstType && nSrcPixelStride == nDstPixelStride && + nSrcPixelStride == nSrcDataTypeSize) + { + memcpy(pDstData, pSrcData, nWordCount * nSrcDataTypeSize); + return; + } + + // Handle the more general case -- deals with conversion of data types + // directly. + switch (eSrcType) + { + case GDT_Byte: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_UInt16: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Int16: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_UInt32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Int32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Float32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Float64: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CInt16: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CInt32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CFloat32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CFloat64: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Unknown: + default: + CPLAssert(FALSE); + } + +#else // undefined USE_NEW_COPYWORDS +/* -------------------------------------------------------------------- */ +/* Special case when no data type translation is required. */ +/* -------------------------------------------------------------------- */ + if( eSrcType == eDstType ) + { + int nWordSize = GDALGetDataTypeSize(eSrcType)/8; + int i; + + // contiguous blocks. + if( nWordSize == nSrcPixelStride && nWordSize == nDstPixelStride ) + { + memcpy( pDstData, pSrcData, nSrcPixelStride * nWordCount ); + return; + } + + GByte *pabySrc = (GByte *) pSrcData; + GByte *pabyDst = (GByte *) pDstData; + + // Moving single bytes. + if( nWordSize == 1 ) + { + if (nWordCount > 100) + { +/* ==================================================================== */ +/* Optimization for high number of words to transfer and some */ +/* typical source and destination pixel spacing : we unroll the */ +/* loop. */ +/* ==================================================================== */ +#define ASSIGN(_nSrcPixelStride, _nDstPixelStride, _k) \ + pabyDst[_nDstPixelStride * _k] = pabySrc[_nSrcPixelStride * _k] +#define ASSIGN_LOOP(_nSrcPixelStride, _nDstPixelStride) \ + for( i = nWordCount / 16 ; i != 0; i-- ) \ + { \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 0); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 1); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 2); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 3); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 4); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 5); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 6); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 7); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 8); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 9); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 10); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 11); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 12); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 13); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 14); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 15); \ + pabyDst += _nDstPixelStride * 16; \ + pabySrc += _nSrcPixelStride * 16; \ + } \ + nWordCount = nWordCount % 16; + + if (nSrcPixelStride == 3 && nDstPixelStride == 1) + { + ASSIGN_LOOP(3, 1) + } + else if (nSrcPixelStride == 1 && nDstPixelStride == 3) + { + ASSIGN_LOOP(1, 3) + } + else if (nSrcPixelStride == 4 && nDstPixelStride == 1) + { + ASSIGN_LOOP(4, 1) + } + else if (nSrcPixelStride == 1 && nDstPixelStride == 4) + { + ASSIGN_LOOP(1, 4) + } + else if (nSrcPixelStride == 3 && nDstPixelStride == 4) + { + ASSIGN_LOOP(3, 4) + } + else if (nSrcPixelStride == 4 && nDstPixelStride == 3) + { + ASSIGN_LOOP(4, 3) + } + } + + for( i = nWordCount; i != 0; i-- ) + { + *pabyDst = *pabySrc; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 2) + { + for( i = nWordCount; i != 0; i-- ) + { + *(short*)pabyDst = *(short*)pabySrc; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 4) + { + for( i = nWordCount; i != 0; i-- ) + { + *(int*)pabyDst = *(int*)pabySrc; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 8) + { + for( i = nWordCount; i != 0; i-- ) + { + ((int*)pabyDst)[0] = ((int*)pabySrc)[0]; + ((int*)pabyDst)[1] = ((int*)pabySrc)[1]; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 16) + { + for( i = nWordCount; i != 0; i-- ) + { + ((int*)pabyDst)[0] = ((int*)pabySrc)[0]; + ((int*)pabyDst)[1] = ((int*)pabySrc)[1]; + ((int*)pabyDst)[2] = ((int*)pabySrc)[2]; + ((int*)pabyDst)[3] = ((int*)pabySrc)[3]; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else + { + CPLAssert(FALSE); + } + + return; + } + +/* ==================================================================== */ +/* General translation case */ +/* ==================================================================== */ + for( int iWord = 0; iWord < nWordCount; iWord++ ) + { + void *pSrcWord, *pDstWord; + double dfPixelValue=0.0, dfPixelValueI=0.0; + + pSrcWord = static_cast(pSrcData) + iWord * nSrcPixelStride; + pDstWord = static_cast(pDstData) + iWord * nDstPixelStride; + +/* -------------------------------------------------------------------- */ +/* Fetch source value based on data type. */ +/* -------------------------------------------------------------------- */ + switch( eSrcType ) + { + case GDT_Byte: + { + GByte byVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_UInt16: + *static_cast(pDstWord) = byVal; + continue; + case GDT_Int16: + *static_cast(pDstWord) = byVal; + continue; + case GDT_UInt32: + *static_cast(pDstWord) = byVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = byVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + panDstWord[0] = byVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = byVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = byVal; + } + break; + + case GDT_UInt16: + { + GUInt16 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else + byVal = static_cast(nVal); + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_Int16: + { + GInt16 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = static_cast(nVal); + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_UInt16: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_Int32: + { + GInt32 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = nVal; + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_UInt16: + if( nVal > 65535 ) + nVal = 65535; + else if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_UInt32: + { + GUInt32 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else + byVal = nVal; + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_UInt16: + if( nVal > 65535 ) + nVal = 65535; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + if( nVal > 2147483647UL ) + nVal = 2147483647UL; + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + if( nVal > 2147483647UL ) + nVal = 2147483647UL; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_CInt16: + { + GInt16 *panSrcWord = static_cast(pSrcWord); + GInt16 nVal = panSrcWord[0]; + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = static_cast(nVal); + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_Int16: + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt16: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = panSrcWord[0]; + panDstWord[1] = panSrcWord[1]; + continue; + } + default: + break; + } + dfPixelValue = panSrcWord[0]; + dfPixelValueI = panSrcWord[1]; + } + break; + + case GDT_CInt32: + { + GInt32 *panSrcWord = static_cast(pSrcWord); + GInt32 nVal = panSrcWord[0]; + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = nVal; + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt16: + if( nVal > 65535 ) + nVal = 65535; + else if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + panDstWord[0] = nVal; + nVal = panSrcWord[1]; + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + panDstWord[1] = nVal; + continue; + } + default: + break; + } + dfPixelValue = panSrcWord[0]; + dfPixelValueI = panSrcWord[1]; + } + break; + + case GDT_Float32: + { + float fVal = *static_cast(pSrcWord); + dfPixelValue = fVal; + } + break; + + case GDT_Float64: + { + dfPixelValue = *static_cast(pSrcWord); + } + break; + + case GDT_CFloat32: + { + float *pafSrcWord = static_cast(pSrcWord); + dfPixelValue = pafSrcWord[0]; + dfPixelValueI = pafSrcWord[1]; + } + break; + + case GDT_CFloat64: + { + double *padfSrcWord = static_cast(pSrcWord); + dfPixelValue = padfSrcWord[0]; + dfPixelValueI = padfSrcWord[1]; + } + break; + + default: + CPLAssert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Set the destination pixel, doing range clipping as needed. */ +/* -------------------------------------------------------------------- */ + switch( eDstType ) + { + case GDT_Byte: + { + GByte *pabyDstWord = static_cast(pDstWord); + + dfPixelValue += (float) 0.5; + + if( dfPixelValue < 0.0 ) + *pabyDstWord = 0; + else if( dfPixelValue > 255.0 ) + *pabyDstWord = 255; + else + *pabyDstWord = (GByte) dfPixelValue; + } + break; + + case GDT_UInt16: + { + GUInt16 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < 0.0 ) + nVal = 0; + else if( dfPixelValue > 65535.0 ) + nVal = 65535; + else + nVal = (GUInt16) dfPixelValue; + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_Int16: + { + GInt16 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < -32768 ) + nVal = -32768; + else if( dfPixelValue > 32767 ) + nVal = 32767; + else + nVal = (GInt16) floor(dfPixelValue); + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_UInt32: + { + GUInt32 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < 0 ) + nVal = 0; + else if( dfPixelValue > 4294967295U ) + nVal = 4294967295U; + else + nVal = (GInt32) dfPixelValue; + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_Int32: + { + GInt32 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < -2147483648.0 ) + nVal = INT_MIN; + else if( dfPixelValue > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfPixelValue); + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_Float32: + { + *static_cast(pDstWord) = static_cast(dfPixelValue); + } + break; + + case GDT_Float64: + *static_cast(pDstWord) = dfPixelValue; + break; + + case GDT_CInt16: + { + GInt16 nVal; + GInt16 *panDstWord = static_cast(pDstWord); + + dfPixelValue += 0.5; + dfPixelValueI += 0.5; + + if( dfPixelValue < -32768 ) + nVal = -32768; + else if( dfPixelValue > 32767 ) + nVal = 32767; + else + nVal = (GInt16) floor(dfPixelValue); + panDstWord[0] = nVal; + + if( dfPixelValueI < -32768 ) + nVal = -32768; + else if( dfPixelValueI > 32767 ) + nVal = 32767; + else + nVal = (GInt16) floor(dfPixelValueI); + panDstWord[1] = nVal; + } + break; + + case GDT_CInt32: + { + GInt32 nVal; + GInt32 *panDstWord = static_cast(pDstWord); + + dfPixelValue += 0.5; + dfPixelValueI += 0.5; + + if( dfPixelValue < -2147483648.0 ) + nVal = INT_MIN; + else if( dfPixelValue > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfPixelValue); + + panDstWord[0] = nVal; + + if( dfPixelValueI < -2147483648.0 ) + nVal = INT_MIN; + else if( dfPixelValueI > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfPixelValueI); + + panDstWord[1] = nVal; + } + break; + + case GDT_CFloat32: + { + float *pafDstWord = static_cast(pDstWord); + pafDstWord[0] = static_cast(dfPixelValue); + pafDstWord[1] = static_cast(dfPixelValueI); + } + break; + + case GDT_CFloat64: + { + double *padfDstWord = static_cast(pDstWord); + padfDstWord[0] = dfPixelValue; + padfDstWord[1] = dfPixelValueI; + } + break; + + default: + CPLAssert( FALSE ); + } + } /* next iWord */ +#endif // defined USE_NEW_COPYWORDS +} + +/************************************************************************/ +/* GDALCopyBits() */ +/************************************************************************/ + +/** + * Bitwise word copying. + * + * A function for moving sets of partial bytes around. Loosely + * speaking this is a bitswise analog to GDALCopyWords(). + * + * It copies nStepCount "words" where each word is nBitCount bits long. + * The nSrcStep and nDstStep are the number of bits from the start of one + * word to the next (same as nBitCount if they are packed). The nSrcOffset + * and nDstOffset are the offset into the source and destination buffers + * to start at, also measured in bits. + * + * All bit offsets are assumed to start from the high order bit in a byte + * (ie. most significant bit first). Currently this function is not very + * optimized, but it may be improved for some common cases in the future + * as needed. + * + * @param pabySrcData the source data buffer. + * @param nSrcOffset the offset (in bits) in pabySrcData to the start of the + * first word to copy. + * @param nSrcStep the offset in bits from the start one source word to the + * start of the next. + * @param pabyDstData the destination data buffer. + * @param nDstOffset the offset (in bits) in pabyDstData to the start of the + * first word to copy over. + * @param nDstStep the offset in bits from the start one word to the + * start of the next. + * @param nBitCount the number of bits in a word to be copied. + * @param nStepCount the number of words to copy. + */ + +void GDALCopyBits( const GByte *pabySrcData, int nSrcOffset, int nSrcStep, + GByte *pabyDstData, int nDstOffset, int nDstStep, + int nBitCount, int nStepCount ) + +{ + VALIDATE_POINTER0( pabySrcData, "GDALCopyBits" ); + + int iStep; + int iBit; + + for( iStep = 0; iStep < nStepCount; iStep++ ) + { + for( iBit = 0; iBit < nBitCount; iBit++ ) + { + if( pabySrcData[nSrcOffset>>3] + & (0x80 >>(nSrcOffset & 7)) ) + pabyDstData[nDstOffset>>3] |= (0x80 >> (nDstOffset & 7)); + else + pabyDstData[nDstOffset>>3] &= ~(0x80 >> (nDstOffset & 7)); + + + nSrcOffset++; + nDstOffset++; + } + + nSrcOffset += (nSrcStep - nBitCount); + nDstOffset += (nDstStep - nBitCount); + } +} + +/************************************************************************/ +/* GDALGetBestOverviewLevel() */ +/* */ +/* Returns the best overview level to satisfy the query or -1 if none */ +/* Also updates nXOff, nYOff, nXSize, nYSize when returning a valid */ +/* overview level */ +/************************************************************************/ + +int GDALBandGetBestOverviewLevel(GDALRasterBand* poBand, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize) +{ + double dfDesiredResolution; +/* -------------------------------------------------------------------- */ +/* Compute the desired resolution. The resolution is */ +/* based on the least reduced axis, and represents the number */ +/* of source pixels to one destination pixel. */ +/* -------------------------------------------------------------------- */ + if( (nXSize / (double) nBufXSize) < (nYSize / (double) nBufYSize ) + || nBufYSize == 1 ) + dfDesiredResolution = nXSize / (double) nBufXSize; + else + dfDesiredResolution = nYSize / (double) nBufYSize; + +/* -------------------------------------------------------------------- */ +/* Find the overview level that largest resolution value (most */ +/* downsampled) that is still less than (or only a little more) */ +/* downsampled than the request. */ +/* -------------------------------------------------------------------- */ + int nOverviewCount = poBand->GetOverviewCount(); + GDALRasterBand* poBestOverview = NULL; + double dfBestResolution = 0; + int nBestOverviewLevel = -1; + + for( int iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + { + GDALRasterBand *poOverview = poBand->GetOverview( iOverview ); + if (poOverview == NULL) + continue; + + double dfResolution; + + // What resolution is this? + if( (poBand->GetXSize() / (double) poOverview->GetXSize()) + < (poBand->GetYSize() / (double) poOverview->GetYSize()) ) + dfResolution = + poBand->GetXSize() / (double) poOverview->GetXSize(); + else + dfResolution = + poBand->GetYSize() / (double) poOverview->GetYSize(); + + // Is it nearly the requested resolution and better (lower) than + // the current best resolution? + if( dfResolution >= dfDesiredResolution * 1.2 + || dfResolution <= dfBestResolution ) + continue; + + // Ignore AVERAGE_BIT2GRAYSCALE overviews for RasterIO purposes. + const char *pszResampling = + poOverview->GetMetadataItem( "RESAMPLING" ); + + if( pszResampling != NULL && EQUALN(pszResampling,"AVERAGE_BIT2",12)) + continue; + + // OK, this is our new best overview. + poBestOverview = poOverview; + nBestOverviewLevel = iOverview; + dfBestResolution = dfResolution; + } + +/* -------------------------------------------------------------------- */ +/* If we didn't find an overview that helps us, just return */ +/* indicating failure and the full resolution image will be used. */ +/* -------------------------------------------------------------------- */ + if( nBestOverviewLevel < 0 ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Recompute the source window in terms of the selected */ +/* overview. */ +/* -------------------------------------------------------------------- */ + int nOXOff, nOYOff, nOXSize, nOYSize; + double dfXRes, dfYRes; + + dfXRes = poBand->GetXSize() / (double) poBestOverview->GetXSize(); + dfYRes = poBand->GetYSize() / (double) poBestOverview->GetYSize(); + + nOXOff = MIN(poBestOverview->GetXSize()-1,(int) (nXOff/dfXRes+0.5)); + nOYOff = MIN(poBestOverview->GetYSize()-1,(int) (nYOff/dfYRes+0.5)); + nOXSize = MAX(1,(int) (nXSize/dfXRes + 0.5)); + nOYSize = MAX(1,(int) (nYSize/dfYRes + 0.5)); + if( nOXOff + nOXSize > poBestOverview->GetXSize() ) + nOXSize = poBestOverview->GetXSize() - nOXOff; + if( nOYOff + nOYSize > poBestOverview->GetYSize() ) + nOYSize = poBestOverview->GetYSize() - nOYOff; + + nXOff = nOXOff; + nYOff = nOYOff; + nXSize = nOXSize; + nYSize = nOYSize; + + return nBestOverviewLevel; +} + + +/************************************************************************/ +/* OverviewRasterIO() */ +/* */ +/* Special work function to utilize available overviews to */ +/* more efficiently satisfy downsampled requests. It will */ +/* return CE_Failure if there are no appropriate overviews */ +/* available but it doesn't emit any error messages. */ +/************************************************************************/ + +CPLErr GDALRasterBand::OverviewRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ) + + +{ + int nOverview; + + nOverview = + GDALBandGetBestOverviewLevel(this, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize); + if (nOverview < 0) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Recast the call in terms of the new raster layer. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand* poOverviewBand = GetOverview(nOverview); + if (poOverviewBand == NULL) + return CE_Failure; + + return poOverviewBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace ); +} + +/************************************************************************/ +/* GetBestOverviewLevel() */ +/* */ +/* Returns the best overview level to satisfy the query or -1 if none */ +/* Also updates nXOff, nYOff, nXSize, nYSize when returning a valid */ +/* overview level */ +/************************************************************************/ + +static +int GDALDatasetGetBestOverviewLevel(GDALDataset* poDS, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandMap) +{ + int iBand, iOverview; + int nOverviewCount = 0; + GDALRasterBand *poFirstBand = NULL; + + if (nBandCount == 0) + return -1; + +/* -------------------------------------------------------------------- */ +/* Check that all bands have the same number of overviews and */ +/* that they have all the same size and block dimensions */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = poDS->GetRasterBand( panBandMap[iBand] ); + if (iBand == 0) + { + poFirstBand = poBand; + nOverviewCount = poBand->GetOverviewCount(); + } + else if (nOverviewCount != poBand->GetOverviewCount()) + { + CPLDebug( "GDAL", + "GDALDataset::GetBestOverviewLevel() ... " + "mismatched overview count, use std method." ); + return -1; + } + else + { + for(iOverview = 0; iOverview < nOverviewCount; iOverview++) + { + GDALRasterBand* poOvrBand = + poBand->GetOverview(iOverview); + GDALRasterBand* poOvrFirstBand = + poFirstBand->GetOverview(iOverview); + if ( poOvrBand == NULL || poOvrFirstBand == NULL) + continue; + + if ( poOvrFirstBand->GetXSize() != poOvrBand->GetXSize() || + poOvrFirstBand->GetYSize() != poOvrBand->GetYSize() ) + { + CPLDebug( "GDAL", + "GDALDataset::GetBestOverviewLevel() ... " + "mismatched overview sizes, use std method." ); + return -1; + } + int nBlockXSizeFirst=0, nBlockYSizeFirst=0; + int nBlockXSizeCurrent=0, nBlockYSizeCurrent=0; + poOvrFirstBand->GetBlockSize(&nBlockXSizeFirst, &nBlockYSizeFirst); + poOvrBand->GetBlockSize(&nBlockXSizeCurrent, &nBlockYSizeCurrent); + if (nBlockXSizeFirst != nBlockXSizeCurrent || + nBlockYSizeFirst != nBlockYSizeCurrent) + { + CPLDebug( "GDAL", + "GDALDataset::GetBestOverviewLevel() ... " + "mismatched block sizes, use std method." ); + return -1; + } + } + } + } + + return GDALBandGetBestOverviewLevel(poFirstBand, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize); +} + +/************************************************************************/ +/* BlockBasedRasterIO() */ +/* */ +/* This convenience function implements a dataset level */ +/* RasterIO() interface based on calling down to fetch blocks, */ +/* much like the GDALRasterBand::IRasterIO(), but it handles */ +/* all bands at once, so that a format driver that handles a */ +/* request for different bands of the same block efficiently */ +/* (ie. without re-reading interleaved data) will efficiently. */ +/* */ +/* This method is intended to be called by an overridden */ +/* IRasterIO() method in the driver specific GDALDataset */ +/* derived class. */ +/* */ +/* Default internal implementation of RasterIO() ... utilizes */ +/* the Block access methods to satisfy the request. This would */ +/* normally only be overridden by formats with overviews. */ +/* */ +/* To keep things relatively simple, this method does not */ +/* currently take advantage of some special cases addressed in */ +/* GDALRasterBand::IRasterIO(), so it is likely best to only */ +/* call it when you know it will help. That is in cases where */ +/* data is at 1:1 to the buffer, and you know the driver is */ +/* implementing interleaved IO efficiently on a block by block */ +/* basis. Overviews will be used when possible. */ +/************************************************************************/ + +CPLErr +GDALDataset::BlockBasedRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace,int nBandSpace) + +{ + GByte **papabySrcBlock = NULL; + GDALRasterBlock *poBlock = NULL; + GDALRasterBlock **papoBlocks = NULL; + int nLBlockX=-1, nLBlockY=-1, iBufYOff, iBufXOff, iSrcY, iBand; + int nBlockXSize=1, nBlockYSize=1; + CPLErr eErr = CE_None; + GDALDataType eDataType = GDT_Byte; + + CPLAssert( NULL != pData ); + +/* -------------------------------------------------------------------- */ +/* Ensure that all bands share a common block size and data type. */ +/* -------------------------------------------------------------------- */ + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand( panBandMap[iBand] ); + int nThisBlockXSize, nThisBlockYSize; + + if( iBand == 0 ) + { + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + eDataType = poBand->GetRasterDataType(); + } + else + { + poBand->GetBlockSize( &nThisBlockXSize, &nThisBlockYSize ); + if( nThisBlockXSize != nBlockXSize + || nThisBlockYSize != nBlockYSize ) + { + CPLDebug( "GDAL", + "GDALDataset::BlockBasedRasterIO() ... " + "mismatched block sizes, use std method." ); + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace ); + } + + if( eDataType != poBand->GetRasterDataType() + && (nXSize != nBufXSize || nYSize != nBufYSize) ) + { + CPLDebug( "GDAL", + "GDALDataset::BlockBasedRasterIO() ... " + "mismatched band data types, use std method." ); + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace ); + } + } + } + +/* ==================================================================== */ +/* In this special case at full resolution we step through in */ +/* blocks, turning the request over to the per-band */ +/* IRasterIO(), but ensuring that all bands of one block are */ +/* called before proceeding to the next. */ +/* ==================================================================== */ + if( nXSize == nBufXSize && nYSize == nBufYSize ) + { + int nChunkYSize, nChunkXSize, nChunkXOff, nChunkYOff; + + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff += nChunkYSize ) + { + nChunkYSize = nBlockYSize; + nChunkYOff = iBufYOff + nYOff; + nChunkYSize = nBlockYSize - (nChunkYOff % nBlockYSize); + if( nChunkYSize == 0 ) + nChunkYSize = nBlockYSize; + if( nChunkYOff + nChunkYSize > nYOff + nYSize ) + nChunkYSize = (nYOff + nYSize) - nChunkYOff; + + for( iBufXOff = 0; iBufXOff < nBufXSize; iBufXOff += nChunkXSize ) + { + nChunkXSize = nBlockXSize; + nChunkXOff = iBufXOff + nXOff; + nChunkXSize = nBlockXSize - (nChunkXOff % nBlockXSize); + if( nChunkXSize == 0 ) + nChunkXSize = nBlockXSize; + if( nChunkXOff + nChunkXSize > nXOff + nXSize ) + nChunkXSize = (nXOff + nXSize) - nChunkXOff; + + GByte *pabyChunkData; + + pabyChunkData = ((GByte *) pData) + + iBufXOff * nPixelSpace + + iBufYOff * nLineSpace; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand(panBandMap[iBand]); + + eErr = + poBand->GDALRasterBand::IRasterIO( + eRWFlag, nChunkXOff, nChunkYOff, + nChunkXSize, nChunkYSize, + pabyChunkData + iBand * nBandSpace, + nChunkXSize, nChunkYSize, eBufType, + nPixelSpace, nLineSpace ); + if( eErr != CE_None ) + return eErr; + } + } + } + + return CE_None; + } + + /* Below code is not compatible with that case. It would need a complete */ + /* separate code like done in GDALRasterBand::IRasterIO. */ + if (eRWFlag == GF_Write && (nBufXSize < nXSize || nBufYSize < nYSize)) + { + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace ); + } + +/* ==================================================================== */ +/* Loop reading required source blocks to satisfy output */ +/* request. This is the most general implementation. */ +/* ==================================================================== */ + + int nBandDataSize = GDALGetDataTypeSize( eDataType ) / 8; + + papabySrcBlock = (GByte **) CPLCalloc(sizeof(GByte*),nBandCount); + papoBlocks = (GDALRasterBlock **) CPLCalloc(sizeof(void*),nBandCount); + +/* -------------------------------------------------------------------- */ +/* Select an overview level if appropriate. */ +/* -------------------------------------------------------------------- */ + int nOverviewLevel = GDALDatasetGetBestOverviewLevel (this, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandMap); + if (nOverviewLevel >= 0) + { + GetRasterBand(panBandMap[0])->GetOverview(nOverviewLevel)-> + GetBlockSize( &nBlockXSize, &nBlockYSize ); + } + +/* -------------------------------------------------------------------- */ +/* Compute stepping increment. */ +/* -------------------------------------------------------------------- */ + double dfSrcX, dfSrcY, dfSrcXInc, dfSrcYInc; + + dfSrcXInc = nXSize / (double) nBufXSize; + dfSrcYInc = nYSize / (double) nBufYSize; + +/* -------------------------------------------------------------------- */ +/* Loop over buffer computing source locations. */ +/* -------------------------------------------------------------------- */ + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff++ ) + { + int iBufOffset, iSrcOffset; + + dfSrcY = (iBufYOff+0.5) * dfSrcYInc + nYOff; + iSrcY = (int) dfSrcY; + + iBufOffset = iBufYOff * nLineSpace; + + for( iBufXOff = 0; iBufXOff < nBufXSize; iBufXOff++ ) + { + int iSrcX; + + dfSrcX = (iBufXOff+0.5) * dfSrcXInc + nXOff; + + iSrcX = (int) dfSrcX; + +/* -------------------------------------------------------------------- */ +/* Ensure we have the appropriate block loaded. */ +/* -------------------------------------------------------------------- */ + if( iSrcX < nLBlockX * nBlockXSize + || iSrcX >= (nLBlockX+1) * nBlockXSize + || iSrcY < nLBlockY * nBlockYSize + || iSrcY >= (nLBlockY+1) * nBlockYSize ) + { + nLBlockX = iSrcX / nBlockXSize; + nLBlockY = iSrcY / nBlockYSize; + + int bJustInitialize = + eRWFlag == GF_Write + && nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize + && nXOff <= nLBlockX * nBlockXSize + && nXOff + nXSize >= (nLBlockX+1) * nBlockXSize; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand( panBandMap[iBand]); + if (nOverviewLevel >= 0) + poBand = poBand->GetOverview(nOverviewLevel); + poBlock = poBand->GetLockedBlockRef( nLBlockX, nLBlockY, + bJustInitialize ); + if( poBlock == NULL ) + { + eErr = CE_Failure; + goto CleanupAndReturn; + } + + if( eRWFlag == GF_Write ) + poBlock->MarkDirty(); + + if( papoBlocks[iBand] != NULL ) + papoBlocks[iBand]->DropLock(); + + papoBlocks[iBand] = poBlock; + + papabySrcBlock[iBand] = (GByte *) poBlock->GetDataRef(); + if( papabySrcBlock[iBand] == NULL ) + { + eErr = CE_Failure; + goto CleanupAndReturn; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Copy over this pixel of data. */ +/* -------------------------------------------------------------------- */ + iSrcOffset = (iSrcX - nLBlockX*nBlockXSize + + (iSrcY - nLBlockY*nBlockYSize) * nBlockXSize)*nBandDataSize; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GByte *pabySrcBlock = papabySrcBlock[iBand]; + int iBandBufOffset = iBufOffset + iBand * nBandSpace; + + if( eDataType == eBufType ) + { + if( eRWFlag == GF_Read ) + memcpy( ((GByte *) pData) + iBandBufOffset, + pabySrcBlock + iSrcOffset, nBandDataSize ); + else + memcpy( pabySrcBlock + iSrcOffset, + ((GByte *)pData) + iBandBufOffset, nBandDataSize ); + } + else + { + /* type to type conversion ... ouch, this is expensive way + of handling single words */ + + if( eRWFlag == GF_Read ) + GDALCopyWords( pabySrcBlock + iSrcOffset, eDataType, 0, + ((GByte *) pData) + iBandBufOffset, + eBufType, 0, 1 ); + else + GDALCopyWords( ((GByte *) pData) + iBandBufOffset, + eBufType, 0, + pabySrcBlock + iSrcOffset, eDataType, 0, + 1 ); + } + } + + iBufOffset += nPixelSpace; + } + } + +/* -------------------------------------------------------------------- */ +/* CleanupAndReturn. */ +/* -------------------------------------------------------------------- */ + CleanupAndReturn: + CPLFree( papabySrcBlock ); + if( papoBlocks != NULL ) + { + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + if( papoBlocks[iBand] != NULL ) + papoBlocks[iBand]->DropLock(); + } + CPLFree( papoBlocks ); + } + + return( CE_None ); +} + +/************************************************************************/ +/* GDALCopyWholeRasterGetSwathSize() */ +/************************************************************************/ + +static void GDALCopyWholeRasterGetSwathSize(GDALRasterBand *poSrcPrototypeBand, + GDALRasterBand *poDstPrototypeBand, + int nBandCount, + int bDstIsCompressed, int bInterleave, + int* pnSwathCols, int *pnSwathLines) +{ + GDALDataType eDT = poDstPrototypeBand->GetRasterDataType(); + int nSrcBlockXSize, nSrcBlockYSize; + int nBlockXSize, nBlockYSize; + + int nXSize = poSrcPrototypeBand->GetXSize(); + int nYSize = poSrcPrototypeBand->GetYSize(); + + poSrcPrototypeBand->GetBlockSize( &nSrcBlockXSize, &nSrcBlockYSize ); + poDstPrototypeBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + int nMaxBlockXSize = MAX(nBlockXSize, nSrcBlockXSize); + int nMaxBlockYSize = MAX(nBlockYSize, nSrcBlockYSize); + + int nPixelSize = (GDALGetDataTypeSize(eDT) / 8); + if( bInterleave) + nPixelSize *= nBandCount; + + // aim for one row of blocks. Do not settle for less. + int nSwathCols = nXSize; + int nSwathLines = nBlockYSize; + +/* -------------------------------------------------------------------- */ +/* What will our swath size be? */ +/* -------------------------------------------------------------------- */ + /* When writing interleaved data in a compressed format, we want to be sure */ + /* that each block will only be written once, so the swath size must not be */ + /* greater than the block cache. */ + const char* pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", NULL); + int nTargetSwathSize; + if( pszSwathSize != NULL ) + nTargetSwathSize = atoi(pszSwathSize); + else + { + /* As a default, take one 1/4 of the cache size */ + nTargetSwathSize = MIN(INT_MAX, GDALGetCacheMax64() / 4); + + /* but if the minimum idal swath buf size is less, then go for it to */ + /* avoid unnecessarily abusing RAM usage */ + GIntBig nIdealSwathBufSize = (GIntBig)nSwathCols * nSwathLines * nPixelSize; + if( nTargetSwathSize > nIdealSwathBufSize ) + nTargetSwathSize = nIdealSwathBufSize; + } + + if (nTargetSwathSize < 1000000) + nTargetSwathSize = 1000000; + + /* But let's check that */ + if (bDstIsCompressed && bInterleave && nTargetSwathSize > GDALGetCacheMax64()) + { + CPLError(CE_Warning, CPLE_AppDefined, + "When translating into a compressed interleave format, the block cache size (" CPL_FRMT_GIB ") " + "should be at least the size of the swath (%d) (GDAL_SWATH_SIZE config. option)", GDALGetCacheMax64(), nTargetSwathSize); + } + +#define IS_MULTIPLE_OF(x,y) ((y)%(x) == 0) +#define ROUND_TO(x,y) (((x)/(y))*(y)) + + /* if both input and output datasets are tiled, that the tile dimensions */ + /* are "compatible", try to stick to a swath dimension that is a multiple */ + /* of input and output block dimensions */ + if (nBlockXSize != nXSize && nSrcBlockXSize != nXSize && + IS_MULTIPLE_OF(nBlockXSize, nMaxBlockXSize) && + IS_MULTIPLE_OF(nSrcBlockXSize, nMaxBlockXSize) && + IS_MULTIPLE_OF(nBlockYSize, nMaxBlockYSize) && + IS_MULTIPLE_OF(nSrcBlockYSize, nMaxBlockYSize)) + { + if (((GIntBig)nMaxBlockXSize) * nMaxBlockYSize * nPixelSize <= + (GIntBig)nTargetSwathSize) + { + nSwathCols = nTargetSwathSize / (nMaxBlockYSize * nPixelSize); + nSwathCols = ROUND_TO(nSwathCols, nMaxBlockXSize); + if (nSwathCols == 0) + nSwathCols = nMaxBlockXSize; + if (nSwathCols > nXSize) + nSwathCols = nXSize; + nSwathLines = nMaxBlockYSize; + + if (((GIntBig)nSwathCols) * nSwathLines * nPixelSize > + (GIntBig)nTargetSwathSize) + { + nSwathCols = nXSize; + nSwathLines = nBlockYSize; + } + } + } + + int nMemoryPerCol = nSwathCols * nPixelSize; + + /* Do the computation on a big int since for example when translating */ + /* the JPL WMS layer, we overflow 32 bits*/ + GIntBig nSwathBufSize = (GIntBig)nMemoryPerCol * nSwathLines; + if (nSwathBufSize > (GIntBig)nTargetSwathSize) + { + nSwathLines = nTargetSwathSize / nMemoryPerCol; + if (nSwathLines == 0) + nSwathLines = 1; + + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): adjusting to %d line swath " + "since requirement (%d * %d bytes) exceed target swath size (%d bytes) (GDAL_SWATH_SIZE config. option)", + nSwathLines, nBlockYSize, nMemoryPerCol, nTargetSwathSize); + } + // If we are processing single scans, try to handle several at once. + // If we are handling swaths already, only grow the swath if a row + // of blocks is substantially less than our target buffer size. + else if( nSwathLines == 1 + || nMemoryPerCol * nSwathLines < nTargetSwathSize / 10 ) + { + nSwathLines = MIN(nYSize,MAX(1,nTargetSwathSize/nMemoryPerCol)); + + /* If possible try to align to source and target block height */ + if ((nSwathLines % nMaxBlockYSize) != 0 && nSwathLines > nMaxBlockYSize && + IS_MULTIPLE_OF(nBlockYSize, nMaxBlockYSize) && + IS_MULTIPLE_OF(nSrcBlockYSize, nMaxBlockYSize)) + nSwathLines = ROUND_TO(nSwathLines, nMaxBlockYSize); + } + + + if (bDstIsCompressed) + { + if (nSwathLines < nBlockYSize) + { + nSwathLines = nBlockYSize; + + /* Number of pixels that can be read/write simultaneously */ + nSwathCols = nTargetSwathSize / (nSwathLines * nPixelSize); + nSwathCols = ROUND_TO(nSwathCols, nBlockXSize); + if (nSwathCols == 0) + nSwathCols = nBlockXSize; + if (nSwathCols > nXSize) + nSwathCols = nXSize; + + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): because of compression and too high block,\n" + "use partial width at one time"); + } + else if ((nSwathLines % nBlockYSize) != 0) + { + /* Round on a multiple of nBlockYSize */ + nSwathLines = ROUND_TO(nSwathLines, nBlockYSize); + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): because of compression, \n" + "round nSwathLines to block height : %d", nSwathLines); + } + } + + *pnSwathCols = nSwathCols; + *pnSwathLines = nSwathLines; +} + +/************************************************************************/ +/* GDALDatasetCopyWholeRaster() */ +/************************************************************************/ + +/** + * \brief Copy all dataset raster data. + * + * This function copies the complete raster contents of one dataset to + * another similarly configured dataset. The source and destination + * dataset must have the same number of bands, and the same width + * and height. The bands do not have to have the same data type. + * + * This function is primarily intended to support implementation of + * driver specific CreateCopy() functions. It implements efficient copying, + * in particular "chunking" the copy in substantial blocks and, if appropriate, + * performing the transfer in a pixel interleaved fashion. + * + * Currently the only papszOptions value supported are : "INTERLEAVE=PIXEL" + * to force pixel interleaved operation and "COMPRESSED=YES" to force alignment + * on target dataset block sizes to achieve best compression. More options may be supported in + * the future. + * + * @param hSrcDS the source dataset + * @param hDstDS the destination dataset + * @param papszOptions transfer hints in "StringList" Name=Value format. + * @param pfnProgress progress reporting function. + * @param pProgressData callback data for progress function. + * + * @return CE_None on success, or CE_Failure on failure. + */ + +CPLErr CPL_STDCALL GDALDatasetCopyWholeRaster( + GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALDatasetCopyWholeRaster", CE_Failure ); + VALIDATE_POINTER1( hDstDS, "GDALDatasetCopyWholeRaster", CE_Failure ); + + GDALDataset *poSrcDS = (GDALDataset *) hSrcDS; + GDALDataset *poDstDS = (GDALDataset *) hDstDS; + CPLErr eErr = CE_None; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Confirm the datasets match in size and band counts. */ +/* -------------------------------------------------------------------- */ + int nXSize = poDstDS->GetRasterXSize(), + nYSize = poDstDS->GetRasterYSize(), + nBandCount = poDstDS->GetRasterCount(); + + if( poSrcDS->GetRasterXSize() != nXSize + || poSrcDS->GetRasterYSize() != nYSize + || poSrcDS->GetRasterCount() != nBandCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input and output dataset sizes or band counts do not\n" + "match in GDALDatasetCopyWholeRaster()" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Report preliminary (0) progress. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get our prototype band, and assume the others are similarly */ +/* configured. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 0 ) + return CE_None; + + GDALRasterBand *poSrcPrototypeBand = poSrcDS->GetRasterBand(1); + GDALRasterBand *poDstPrototypeBand = poDstDS->GetRasterBand(1); + GDALDataType eDT = poDstPrototypeBand->GetRasterDataType(); + +/* -------------------------------------------------------------------- */ +/* Do we want to try and do the operation in a pixel */ +/* interleaved fashion? */ +/* -------------------------------------------------------------------- */ + int bInterleave = FALSE; + const char *pszInterleave = NULL; + + pszInterleave = poSrcDS->GetMetadataItem( "INTERLEAVE", "IMAGE_STRUCTURE"); + if( pszInterleave != NULL + && (EQUAL(pszInterleave,"PIXEL") || EQUAL(pszInterleave,"LINE")) ) + bInterleave = TRUE; + + pszInterleave = poDstDS->GetMetadataItem( "INTERLEAVE", "IMAGE_STRUCTURE"); + if( pszInterleave != NULL + && (EQUAL(pszInterleave,"PIXEL") || EQUAL(pszInterleave,"LINE")) ) + bInterleave = TRUE; + + pszInterleave = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + if( pszInterleave != NULL + && (EQUAL(pszInterleave,"PIXEL") || EQUAL(pszInterleave,"LINE")) ) + bInterleave = TRUE; + + /* If the destination is compressed, we must try to write blocks just once, to save */ + /* disk space (GTiff case for example), and to avoid data loss (JPEG compression for example) */ + int bDstIsCompressed = FALSE; + const char* pszDstCompressed= CSLFetchNameValue( papszOptions, "COMPRESSED" ); + if (pszDstCompressed != NULL && CSLTestBoolean(pszDstCompressed)) + bDstIsCompressed = TRUE; + +/* -------------------------------------------------------------------- */ +/* What will our swath size be? */ +/* -------------------------------------------------------------------- */ + + int nSwathCols, nSwathLines; + GDALCopyWholeRasterGetSwathSize(poSrcPrototypeBand, + poDstPrototypeBand, + nBandCount, + bDstIsCompressed, bInterleave, + &nSwathCols, &nSwathLines); + + int nPixelSize = (GDALGetDataTypeSize(eDT) / 8); + if( bInterleave) + nPixelSize *= nBandCount; + + void *pSwathBuf = VSIMalloc3(nSwathCols, nSwathLines, nPixelSize ); + if( pSwathBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d*%d*%d byte swath buffer in\n" + "GDALDatasetCopyWholeRaster()", + nSwathCols, nSwathLines, nPixelSize ); + return CE_Failure; + } + + CPLDebug( "GDAL", + "GDALDatasetCopyWholeRaster(): %d*%d swaths, bInterleave=%d", + nSwathCols, nSwathLines, bInterleave ); + +/* ==================================================================== */ +/* Band oriented (uninterleaved) case. */ +/* ==================================================================== */ + if( !bInterleave ) + { + int iBand, iX, iY; + + for( iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++ ) + { + int nBand = iBand+1; + + for( iY = 0; iY < nYSize && eErr == CE_None; iY += nSwathLines ) + { + int nThisLines = nSwathLines; + + if( iY + nThisLines > nYSize ) + nThisLines = nYSize - iY; + + for( iX = 0; iX < nXSize && eErr == CE_None; iX += nSwathCols ) + { + int nThisCols = nSwathCols; + + if( iX + nThisCols > nXSize ) + nThisCols = nXSize - iX; + + eErr = poSrcDS->RasterIO( GF_Read, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 1, &nBand, + 0, 0, 0 ); + + if( eErr == CE_None ) + eErr = poDstDS->RasterIO( GF_Write, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 1, &nBand, + 0, 0, 0 ); + + if( eErr == CE_None + && !pfnProgress( + iBand / (float)nBandCount + + (iY+nThisLines) / (float) (nYSize*nBandCount), + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + } + } + +/* ==================================================================== */ +/* Pixel interleaved case. */ +/* ==================================================================== */ + else /* if( bInterleave ) */ + { + int iY, iX; + + for( iY = 0; iY < nYSize && eErr == CE_None; iY += nSwathLines ) + { + int nThisLines = nSwathLines; + + if( iY + nThisLines > nYSize ) + nThisLines = nYSize - iY; + + for( iX = 0; iX < nXSize && eErr == CE_None; iX += nSwathCols ) + { + int nThisCols = nSwathCols; + + if( iX + nThisCols > nXSize ) + nThisCols = nXSize - iX; + + eErr = poSrcDS->RasterIO( GF_Read, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, nBandCount, NULL, + 0, 0, 0 ); + + if( eErr == CE_None ) + eErr = poDstDS->RasterIO( GF_Write, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, nBandCount, NULL, + 0, 0, 0 ); + + if( eErr == CE_None + && !pfnProgress( (iY+nThisLines) / (float) nYSize, + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pSwathBuf ); + + return eErr; +} + + +/************************************************************************/ +/* GDALRasterBandCopyWholeRaster() */ +/************************************************************************/ + +/** + * \brief Copy all raster band raster data. + * + * This function copies the complete raster contents of one band to + * another similarly configured band. The source and destination + * bands must have the same width and height. The bands do not have + * to have the same data type. + * + * It implements efficient copying, in particular "chunking" the copy in + * substantial blocks. + * + * Currently the only papszOptions value supported is : "COMPRESSED=YES" to + * force alignment on target dataset block sizes to achieve best compression. + * More options may be supported in the future. + * + * @param hSrcBand the source band + * @param hDstBand the destination band + * @param papszOptions transfer hints in "StringList" Name=Value format. + * @param pfnProgress progress reporting function. + * @param pProgressData callback data for progress function. + * + * @return CE_None on success, or CE_Failure on failure. + */ + +CPLErr CPL_STDCALL GDALRasterBandCopyWholeRaster( + GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ) + +{ + VALIDATE_POINTER1( hSrcBand, "GDALRasterBandCopyWholeRaster", CE_Failure ); + VALIDATE_POINTER1( hDstBand, "GDALRasterBandCopyWholeRaster", CE_Failure ); + + GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand; + GDALRasterBand *poDstBand = (GDALRasterBand *) hDstBand; + CPLErr eErr = CE_None; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Confirm the datasets match in size and band counts. */ +/* -------------------------------------------------------------------- */ + int nXSize = poSrcBand->GetXSize(), + nYSize = poSrcBand->GetYSize(); + + if( poDstBand->GetXSize() != nXSize + || poDstBand->GetYSize() != nYSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input and output band sizes do not\n" + "match in GDALRasterBandCopyWholeRaster()" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Report preliminary (0) progress. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + return CE_Failure; + } + + GDALDataType eDT = poDstBand->GetRasterDataType(); + + /* If the destination is compressed, we must try to write blocks just once, to save */ + /* disk space (GTiff case for example), and to avoid data loss (JPEG compression for example) */ + int bDstIsCompressed = FALSE; + const char* pszDstCompressed= CSLFetchNameValue( papszOptions, "COMPRESSED" ); + if (pszDstCompressed != NULL && CSLTestBoolean(pszDstCompressed)) + bDstIsCompressed = TRUE; + +/* -------------------------------------------------------------------- */ +/* What will our swath size be? */ +/* -------------------------------------------------------------------- */ + + int nSwathCols, nSwathLines; + GDALCopyWholeRasterGetSwathSize(poSrcBand, + poDstBand, + 1, + bDstIsCompressed, FALSE, + &nSwathCols, &nSwathLines); + + int nPixelSize = (GDALGetDataTypeSize(eDT) / 8); + + void *pSwathBuf = VSIMalloc3(nSwathCols, nSwathLines, nPixelSize ); + if( pSwathBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d*%d*%d byte swath buffer in\n" + "GDALRasterBandCopyWholeRaster()", + nSwathCols, nSwathLines, nPixelSize ); + return CE_Failure; + } + + CPLDebug( "GDAL", + "GDALRasterBandCopyWholeRaster(): %d*%d swaths", + nSwathCols, nSwathLines ); + +/* ==================================================================== */ +/* Band oriented (uninterleaved) case. */ +/* ==================================================================== */ + + int iX, iY; + + for( iY = 0; iY < nYSize && eErr == CE_None; iY += nSwathLines ) + { + int nThisLines = nSwathLines; + + if( iY + nThisLines > nYSize ) + nThisLines = nYSize - iY; + + for( iX = 0; iX < nXSize && eErr == CE_None; iX += nSwathCols ) + { + int nThisCols = nSwathCols; + + if( iX + nThisCols > nXSize ) + nThisCols = nXSize - iX; + + eErr = poSrcBand->RasterIO( GF_Read, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 0, 0 ); + + if( eErr == CE_None ) + eErr = poDstBand->RasterIO( GF_Write, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 0, 0 ); + + if( eErr == CE_None + && !pfnProgress( + (iY+nThisLines) / (float) (nYSize), + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pSwathBuf ); + + return eErr; +} diff --git a/ogr/stub.cpp b/ogr/stub.cpp new file mode 100644 index 0000000..6e6cfd3 --- /dev/null +++ b/ogr/stub.cpp @@ -0,0 +1,157 @@ +#include "gdal_priv.h" +#include "ogr_api.h" +#include "gdal_alg_priv.h" + +char *OGR_G_ExportToKML( OGRGeometryH, const char* pszAltitudeMode ) +{ + return NULL; +} + +char *OGR_G_ExportToJson( OGRGeometryH ) +{ + return NULL; +} + +CPLErr +HFAAuxBuildOverviews( const char *pszOvrFilename, GDALDataset *poParentDS, + GDALDataset **ppoDS, + int nBands, int *panBandList, + int nNewOverviews, int *panNewOverviewList, + const char *pszResampling, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + return CE_Failure; +} + +CPLErr +GTIFFBuildOverviews( const char * pszFilename, + int nBands, GDALRasterBand **papoBandList, + int nOverviews, int * panOverviewList, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + return CE_Failure; +} + +void * +GDALCreateGCPRefineTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed, double tolerance, int minimumGcps) +{ + return NULL; +} + +void * +GDALCreateGCPTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed ) +{ + return NULL; +} + +void * +GDALCreateTPSTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed ) +{ + return NULL; +} + +void * +GDALCreateRPCTransformer( GDALRPCInfo *psRPC, int bReversed, + double dfPixErrThreshold, + char **papszOptions ) + +{ + return NULL; +} + +void * +GDALCreateGeoLocTransformer( GDALDatasetH hBaseDS, + char **papszGeolocationInfo, + int bReversed ) + +{ + return NULL; +} + +void* GDALCreateTPSTransformerInt( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed, char** papszOptions ) +{ + return NULL; +} + +void GDALDestroyGCPTransformer( void *pTransformArg ) +{ +} + +void GDALDestroyTPSTransformer( void *pTransformArg ) +{ +} + +void GDALDestroyRPCTransformer( void *pTransformArg ) +{ +} + +void GDALDestroyGeoLocTransformer( void *pTransformArg ) +{ +} + +int GDALGCPTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ) +{ + return 0; +} + +int GDALTPSTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ) +{ + return 0; +} + +int GDALRPCTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ) +{ + return 0; +} + +int GDALGeoLocTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ) +{ + return 0; +} + +CPL_C_START +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeGeoLocTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeRPCTransformer( CPLXMLNode *psTree ); +CPL_C_END; + +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ) +{ + return NULL; +} + +void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree ) +{ + return NULL; +} + +void *GDALDeserializeGeoLocTransformer( CPLXMLNode *psTree ) +{ + return NULL; +} + +void *GDALDeserializeRPCTransformer( CPLXMLNode *psTree ) +{ + return NULL; +} + +void* GDALCloneTPSTransformer( void *pTransformArg ) +{ + return NULL; +} + diff --git a/ogr/swq.c b/ogr/swq.c deleted file mode 100644 index 82cd876..0000000 --- a/ogr/swq.c +++ /dev/null @@ -1,2674 +0,0 @@ -/****************************************************************************** - * - * Component: OGDI Driver Support Library - * Purpose: Generic SQL WHERE Expression Implementation. - * Author: Frank Warmerdam - * - ****************************************************************************** - * Copyright (C) 2001 Information Interoperability Institute (3i) - * - * Permission to use, copy, modify and distribute this software and - * its documentation for any purpose and without fee is hereby granted, - * provided that the above copyright notice appear in all copies, that - * both the copyright notice and this permission notice appear in - * supporting documentation, and that the name of 3i not be used - * in advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. 3i makes no - * representations about the suitability of this software for any purpose. - * It is provided "as is" without express or implied warranty. - ****************************************************************************/ - -#include -#include -#include -#include - -#include "cpl_conv.h" -#include "cpl_multiproc.h" -#include "swq.h" - -#ifndef SWQ_MALLOC -#define SWQ_MALLOC(x) malloc(x) -#define SWQ_FREE(x) free(x) -#endif - -#ifdef _MSC_VER -# define FORCE_CDECL __cdecl -#else -# define FORCE_CDECL -#endif - -#ifndef TRUE -# define TRUE 1 -#endif - -#ifndef FALSE -# define FALSE 0 -#endif - -#if defined(_WIN32) && !defined(_WIN32_WCE) -# define strcasecmp stricmp -#elif defined(_WIN32_WCE) -# define strcasecmp _stricmp -#endif - - -#define SWQ_OP_IS_LOGICAL(op) ((op) == SWQ_OR || (op) == SWQ_AND || (op) == SWQ_NOT) -#define SWQ_OP_IS_POSTUNARY(op) ((op) == SWQ_ISNULL || (op) == SWQ_ISNOTNULL) - -/************************************************************************/ -/* swq_free() */ -/************************************************************************/ - -void swq_free( void *pMemory ) - -{ - SWQ_FREE( pMemory ); -} - -/************************************************************************/ -/* swq_malloc() */ -/************************************************************************/ - -void *swq_malloc( int nSize ) - -{ - return SWQ_MALLOC(nSize); -} - -/************************************************************************/ -/* swq_realloc() */ -/************************************************************************/ - -void *swq_realloc( void *old_mem, int old_size, int new_size ) - -{ - void *new_mem; - - new_mem = swq_malloc( new_size ); - - if( old_mem != NULL ) - { - memcpy( new_mem, old_mem, old_size < new_size ? old_size : new_size); - SWQ_FREE( old_mem ); - } - if (old_size <= new_size ) - memset( ((char *) new_mem) + old_size, 0, new_size - old_size ); - - return new_mem; -} - -/************************************************************************/ -/* swq_get_errbuf() */ -/************************************************************************/ - -#define SWQ_SIZEOF_ERRBUF 1024 - -#define SNPRINTF_ERR1(x) do { char* pszErrBuf = swq_get_errbuf(); snprintf( pszErrBuf, SWQ_SIZEOF_ERRBUF, x); pszErrBuf[SWQ_SIZEOF_ERRBUF-1] = '\0'; } while(0) -#define SNPRINTF_ERR2(x,y) do { char* pszErrBuf = swq_get_errbuf(); snprintf( pszErrBuf, SWQ_SIZEOF_ERRBUF, x, y); pszErrBuf[SWQ_SIZEOF_ERRBUF-1] = '\0'; } while(0) -#define SNPRINTF_ERR3(x,y,z) do { char* pszErrBuf = swq_get_errbuf(); snprintf( pszErrBuf, SWQ_SIZEOF_ERRBUF, x, y, z); pszErrBuf[SWQ_SIZEOF_ERRBUF-1] = '\0'; } while(0) - -char *swq_get_errbuf() - -{ - char *pszStaticResult = (char *) CPLGetTLS( CTLS_SWQ_ERRBUF ); - if( pszStaticResult == NULL ) - { - pszStaticResult = (char *) CPLMalloc(SWQ_SIZEOF_ERRBUF); - CPLSetTLS( CTLS_SWQ_ERRBUF, pszStaticResult, TRUE ); - } - - return pszStaticResult; -} - -/************************************************************************/ -/* swq_isalphanum() */ -/* */ -/* Is the passed character in the set of things that could */ -/* occur in an alphanumeric token, or a number? */ -/************************************************************************/ - -static int swq_isalphanum( char c ) - -{ - if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') || c == '.' || c == '+' || c == '-' - || c == '_' || c == '*' || ((unsigned char) c) > 127 ) - return TRUE; - else - return FALSE; -} - -/************************************************************************/ -/* swq_token() */ -/************************************************************************/ - -static char *swq_token( const char *expression, char **next, int *is_literal ) - -{ - char *token; - int i_token; - - if( is_literal != NULL ) - *is_literal = 0; - - while( *expression == ' ' || *expression == '\t' - || *expression == 10 || *expression == 13 ) - expression++; - - if( *expression == '\0' ) - { - *next = (char *) expression; - return NULL; - } - -/* -------------------------------------------------------------------- */ -/* Handle string constants. */ -/* -------------------------------------------------------------------- */ - if( *expression == '"' || *expression == '\'' ) - { - expression++; - - token = (char *) SWQ_MALLOC(strlen(expression)+1); - i_token = 0; - - while( *expression != '\0' ) - { - if( *expression == '\\' && expression[1] == '"' ) - expression++; - else if( *expression == '\\' && expression[1] == '\'' ) - expression++; - else if( *expression == '\'' && expression[1] == '\'' ) - expression++; - else if( *expression == '"' ) - { - expression++; - break; - } - else if( *expression == '\'' ) - { - expression++; - break; - } - - token[i_token++] = *(expression++); - } - token[i_token] = '\0'; - - if( is_literal != NULL ) - *is_literal = 1; - } - -/* -------------------------------------------------------------------- */ -/* Handle alpha-numerics. */ -/* -------------------------------------------------------------------- */ - else if( swq_isalphanum( *expression ) ) - { - token = (char *) SWQ_MALLOC(strlen(expression)+1); - i_token = 0; - - while( swq_isalphanum( *expression ) ) - { - token[i_token++] = *(expression++); - } - - token[i_token] = '\0'; - } - -/* -------------------------------------------------------------------- */ -/* Handle special tokens. */ -/* -------------------------------------------------------------------- */ - else - { - token = (char *) SWQ_MALLOC(3); - token[0] = *expression; - token[1] = '\0'; - expression++; - - /* special logic to group stuff like '>=' into one token. */ - - if( (*token == '<' || *token == '>' || *token == '=' || *token == '!') - && (*expression == '<' || *expression == '>' || *expression == '=')) - { - token[1] = *expression; - token[2] = '\0'; - expression++; - } - } - - *next = (char *) expression; - - return token; -} - -/************************************************************************/ -/* swq_strdup() */ -/************************************************************************/ - -static char *swq_strdup( const char *input ) - -{ - char *result; - - result = (char *) SWQ_MALLOC(strlen(input)+1); - strcpy( result, input ); - - return result; -} - -/************************************************************************/ -/* ==================================================================== */ -/* WHERE clause parsing */ -/* ==================================================================== */ -/************************************************************************/ - -/************************************************************************/ -/* swq_test_like() */ -/* */ -/* Does input match pattern? */ -/************************************************************************/ - -int swq_test_like( const char *input, const char *pattern ) - -{ - if( input == NULL || pattern == NULL ) - return 0; - - while( *input != '\0' ) - { - if( *pattern == '\0' ) - return 0; - - else if( *pattern == '_' ) - { - input++; - pattern++; - } - else if( *pattern == '%' ) - { - int eat; - - if( pattern[1] == '\0' ) - return 1; - - /* try eating varying amounts of the input till we get a positive*/ - for( eat = 0; input[eat] != '\0'; eat++ ) - { - if( swq_test_like(input+eat,pattern+1) ) - return 1; - } - - return 0; - } - else - { - if( tolower(*pattern) != tolower(*input) ) - return 0; - else - { - input++; - pattern++; - } - } - } - - if( *pattern != '\0' && strcmp(pattern,"%") != 0 ) - return 0; - else - return 1; -} - -/************************************************************************/ -/* swq_identify_op() */ -/************************************************************************/ - -static swq_op swq_identify_op( char **tokens, int *tokens_consumed ) - -{ - const char *token = tokens[*tokens_consumed]; - - if( strcasecmp(token,"OR") == 0 ) - return SWQ_OR; - - if( strcasecmp(token,"AND") == 0 ) - return SWQ_AND; - - if( strcasecmp(token,"NOT") == 0 ) - { - if( tokens[*tokens_consumed+1] != NULL - && (strcasecmp(tokens[*tokens_consumed+1],"LIKE") == 0 - || strcasecmp(tokens[*tokens_consumed+1],"ILIKE") == 0) ) - { - *tokens_consumed += 1; - return SWQ_NOTLIKE; - } - else if( tokens[*tokens_consumed+1] != NULL - && strcasecmp(tokens[*tokens_consumed+1],"IN") == 0 ) - { - *tokens_consumed += 1; - return SWQ_NOTIN; - } - else - return SWQ_NOT; - } - - if( strcasecmp(token,"<=") == 0 ) - return SWQ_LE; - - if( strcasecmp(token,">=") == 0 ) - return SWQ_GE; - - if( strcasecmp(token,"=") == 0 ) - return SWQ_EQ; - - if( strcasecmp(token,"!=") == 0 ) - return SWQ_NE; - - if( strcasecmp(token,"<>") == 0 ) - return SWQ_NE; - - if( strcasecmp(token,"<") == 0 ) - return SWQ_LT; - - if( strcasecmp(token,">") == 0 ) - return SWQ_GT; - - if( strcasecmp(token,"LIKE") == 0 ) - return SWQ_LIKE; - - if( strcasecmp(token,"ILIKE") == 0 ) - return SWQ_LIKE; - - if( strcasecmp(token,"IN") == 0 ) - return SWQ_IN; - - if( strcasecmp(token,"IS") == 0 ) - { - if( tokens[*tokens_consumed+1] == NULL ) - return SWQ_UNKNOWN; - else if( strcasecmp(tokens[*tokens_consumed+1],"NULL") == 0 ) - { - *tokens_consumed += 1; - return SWQ_ISNULL; - } - else if( strcasecmp(tokens[*tokens_consumed+1],"NOT") == 0 - && tokens[*tokens_consumed+2] != NULL - && strcasecmp(tokens[*tokens_consumed+2],"NULL") == 0 ) - { - *tokens_consumed += 2; - return SWQ_ISNOTNULL; - } - else - return SWQ_UNKNOWN; - } - - return SWQ_UNKNOWN; -} - -/************************************************************************/ -/* swq_identify_field() */ -/************************************************************************/ - -static int swq_identify_field( const char *token, swq_field_list *field_list, - swq_field_type *this_type, int *table_id ) - -{ - int i; - char table_name[128]; - const char *field_token = token; - int tables_enabled; - - if( field_list->table_count > 0 && field_list->table_ids != NULL ) - tables_enabled = TRUE; - else - tables_enabled = FALSE; - -/* -------------------------------------------------------------------- */ -/* Parse out table name if present, and table support enabled. */ -/* -------------------------------------------------------------------- */ - table_name[0] = '\0'; - if( tables_enabled && strchr(token, '.') != NULL ) - { - int dot_offset = (int)(strchr(token,'.') - token); - - if( dot_offset < sizeof(table_name) ) - { - strncpy( table_name, token, dot_offset ); - table_name[dot_offset] = '\0'; - field_token = token + dot_offset + 1; - } - } - -/* -------------------------------------------------------------------- */ -/* Search for matching field. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < field_list->count; i++ ) - { - int t_id = 0; - - if( strcasecmp( field_list->names[i], field_token ) != 0 ) - continue; - - /* Do the table specifications match? */ - if( tables_enabled ) - { - t_id = field_list->table_ids[i]; - if( table_name[0] != '\0' - && strcasecmp(table_name, - field_list->table_defs[t_id].table_alias) != 0 ) - continue; - -#ifdef notdef - if( t_id != 0 && table_name[0] == '\0' ) - continue; -#endif - } - - /* We have a match, return various information */ - if( this_type != NULL ) - { - if( field_list->types != NULL ) - *this_type = field_list->types[i]; - else - *this_type = SWQ_OTHER; - } - - if( table_id != NULL ) - *table_id = t_id; - - if( field_list->ids == NULL ) - return i; - else - return field_list->ids[i]; - } - -/* -------------------------------------------------------------------- */ -/* No match, return failure. */ -/* -------------------------------------------------------------------- */ - if( this_type != NULL ) - *this_type = SWQ_OTHER; - - if( table_id != NULL ) - *table_id = 0; - - return -1; -} - -/************************************************************************/ -/* swq_parse_in_list() */ -/* */ -/* Parse the argument list to the IN predicate. Might be used */ -/* something like: */ -/* */ -/* WHERE color IN ('Red', 'Green', 'Blue') */ -/************************************************************************/ - -static char *swq_parse_in_list( char **tokens, int *tokens_consumed ) - -{ - int i, text_off = 2; - char *result; - - if( tokens[*tokens_consumed] == NULL - || strcasecmp(tokens[*tokens_consumed],"(") != 0 ) - { - SNPRINTF_ERR1("IN argument doesn't start with '('." ); - return NULL; - } - - *tokens_consumed += 1; - - /* Establish length of all tokens plus separators. */ - - for( i = *tokens_consumed; - tokens[i] != NULL && strcasecmp(tokens[i],")") != 0; - i++ ) - { - text_off += strlen(tokens[i]) + 1; - } - - result = (char *) SWQ_MALLOC(text_off); - - /* Actually capture all the arguments. */ - - text_off = 0; - while( tokens[*tokens_consumed] != NULL - && strcasecmp(tokens[*tokens_consumed],")") != 0 ) - { - strcpy( result + text_off, tokens[*tokens_consumed] ); - text_off += strlen(tokens[*tokens_consumed]) + 1; - - *tokens_consumed += 1; - - if( strcasecmp(tokens[*tokens_consumed],",") != 0 - && strcasecmp(tokens[*tokens_consumed],")") != 0 ) - { - SNPRINTF_ERR1("Contents of IN predicate missing comma or closing bracket." ); - SWQ_FREE( result ); - return NULL; - } - else if( strcasecmp(tokens[*tokens_consumed],",") == 0 ) - *tokens_consumed += 1; - } - - /* add final extra terminating zero char */ - result[text_off] = '\0'; - - if( tokens[*tokens_consumed] == NULL ) - { - SNPRINTF_ERR1("Contents of IN predicate missing closing bracket." ); - SWQ_FREE( result ); - return NULL; - } - - *tokens_consumed += 1; - - return result; -} - - -/************************************************************************/ -/* swq_subexpr_compile() */ -/************************************************************************/ - -static const char * -swq_subexpr_compile( char **tokens, swq_field_list *field_list, - swq_expr **expr_out, int *tokens_consumed ) - -{ - swq_expr *op; - const char *error; - int op_code = 0; - - *tokens_consumed = 0; - *expr_out = NULL; - - if( tokens[0] == NULL || tokens[1] == NULL ) - { - SNPRINTF_ERR1("Not enough tokens to complete expression." ); - return swq_get_errbuf(); - } - - op = (swq_field_op *) SWQ_MALLOC(sizeof(swq_field_op)); - memset( op, 0, sizeof(swq_field_op) ); - op->field_index = -1; - - if( strcmp(tokens[0],"(") == 0 ) - { - int sub_consumed = 0; - - error = swq_subexpr_compile( tokens + 1, field_list, - (swq_expr **) &(op->first_sub_expr), - &sub_consumed ); - if( error != NULL ) - { - swq_expr_free( op ); - return error; - } - - if( strcmp(tokens[sub_consumed+1],")") != 0 ) - { - swq_expr_free( op ); - SNPRINTF_ERR1("Unclosed brackets, or incomplete expression."); - return swq_get_errbuf(); - } - - *tokens_consumed += sub_consumed + 2; - - /* If we are at the end of the tokens, we should return our subnode */ - if( tokens[*tokens_consumed] == NULL - || strcmp(tokens[*tokens_consumed],")") == 0 ) - { - *expr_out = (swq_expr *) op->first_sub_expr; - op->first_sub_expr = NULL; - swq_expr_free( op ); - return NULL; - } - } - else if( strcasecmp(tokens[0],"NOT") == 0 ) - { - /* do nothing, the NOT will be collected as the operation */ - } - else - { - op->field_index = - swq_identify_field( tokens[*tokens_consumed], field_list, - &(op->field_type), - &(op->table_index) ); - - if( op->field_index < 0 ) - { - swq_expr_free( op ); - SNPRINTF_ERR2( "Failed to identify field:%s", tokens[*tokens_consumed] ); - return swq_get_errbuf(); - } - - (*tokens_consumed)++; - } - - /* - ** Identify the operation. - */ - if( tokens[*tokens_consumed] == NULL || tokens[*tokens_consumed+1] == NULL) - { - swq_expr_free( op ); - SNPRINTF_ERR1( "Not enough tokens to complete expression." ); - return swq_get_errbuf(); - } - - op->operation = swq_identify_op( tokens, tokens_consumed ); - if( op->operation == SWQ_UNKNOWN ) - { - swq_expr_free( op ); - SNPRINTF_ERR2( "Failed to identify operation:%s", tokens[*tokens_consumed] ); - return swq_get_errbuf(); - } - - if( SWQ_OP_IS_LOGICAL( op->operation ) - && op->first_sub_expr == NULL - && op->operation != SWQ_NOT ) - { - swq_expr_free( op ); - SNPRINTF_ERR1( "Used logical operation with non-logical operand."); - return swq_get_errbuf(); - } - - if( op->field_index != -1 && op->field_type == SWQ_STRING - && (op->operation != SWQ_EQ && op->operation != SWQ_NE - && op->operation != SWQ_GT && op->operation != SWQ_LT - && op->operation != SWQ_GE && op->operation != SWQ_LE - && op->operation != SWQ_LIKE && op->operation != SWQ_NOTLIKE - && op->operation != SWQ_IN && op->operation != SWQ_NOTIN - && op->operation != SWQ_ISNULL && op->operation != SWQ_ISNOTNULL )) - { - /* NOTE: the use of names[] here is wrong. We should be looking - up the field that matches op->field_index and op->table_index */ - - SNPRINTF_ERR3( - "Attempt to use STRING field `%s' with numeric comparison `%s'.", - field_list->names[op->field_index], tokens[*tokens_consumed] ); - swq_expr_free( op ); - return swq_get_errbuf(); - } - - (*tokens_consumed)++; - - /* - ** Collect the second operand as a subexpression. - */ - - if( SWQ_OP_IS_POSTUNARY(op->operation) ) - { - /* we don't need another argument. */ - } - - else if( tokens[*tokens_consumed] == NULL ) - { - SNPRINTF_ERR1( "Not enough tokens to complete expression." ); - swq_expr_free( op ); - return swq_get_errbuf(); - } - - else if( SWQ_OP_IS_LOGICAL( op->operation ) ) - { - int sub_consumed = 0; - - error = swq_subexpr_compile( tokens + *tokens_consumed, field_list, - (swq_expr **) &(op->second_sub_expr), - &sub_consumed ); - if( error != NULL ) - { - swq_expr_free( op ); - return error; - } - - *tokens_consumed += sub_consumed; - } - - /* The IN predicate has a complex argument syntax. */ - else if( op->operation == SWQ_IN || op->operation == SWQ_NOTIN ) - { - op->string_value = swq_parse_in_list( tokens, tokens_consumed ); - if( op->string_value == NULL ) - { - swq_expr_free( op ); - return swq_get_errbuf(); - } - } - - /* - ** Otherwise collect it as a literal value. - */ - else - { - op->string_value = swq_strdup(tokens[*tokens_consumed]); - op->int_value = atoi(op->string_value); - op->float_value = atof(op->string_value); - - if( op->field_index != -1 - && (op->field_type == SWQ_INTEGER || op->field_type == SWQ_FLOAT) - && op->string_value[0] != '-' - && op->string_value[0] != '+' - && op->string_value[0] != '.' - && (op->string_value[0] < '0' || op->string_value[0] > '9') ) - { - /* NOTE: the use of names[] here is wrong. We should be looking - up the field that matches op->field_index and op->table_index */ - - SNPRINTF_ERR3( - "Attempt to compare numeric field `%s' to non-numeric" - " value `%s' is illegal.", - field_list->names[op->field_index], op->string_value ); - swq_expr_free( op ); - return swq_get_errbuf(); - } - - (*tokens_consumed)++; - } - - *expr_out = op; - - /* Transform stuff like A NOT LIKE X into NOT (A LIKE X) */ - if( op->operation == SWQ_NOTLIKE - || op->operation == SWQ_ISNOTNULL - || op->operation == SWQ_NOTIN ) - { - if( op->operation == SWQ_NOTLIKE ) - op->operation = SWQ_LIKE; - else if( op->operation == SWQ_NOTIN ) - op->operation = SWQ_IN; - else if( op->operation == SWQ_ISNOTNULL ) - op->operation = SWQ_ISNULL; - - op = (swq_field_op *) SWQ_MALLOC(sizeof(swq_field_op)); - memset( op, 0, sizeof(swq_field_op) ); - op->field_index = -1; - op->second_sub_expr = (struct swq_node_s *) *expr_out; - op->operation = SWQ_NOT; - - *expr_out = op; - } - - op = NULL; - - /* - ** Are we part of an unparantized logical expression chain? If so, - ** grab the remainder of the expression at "this level" and add to the - ** local tree. - */ - op_code = SWQ_UNKNOWN; - if( tokens[*tokens_consumed] != NULL ) - op_code = swq_identify_op( tokens, tokens_consumed ); - - if( SWQ_OP_IS_LOGICAL(op_code) ) - { - swq_expr *remainder = NULL; - swq_expr *parent; - int sub_consumed; - - error = swq_subexpr_compile( tokens + *tokens_consumed + 1, field_list, - &remainder, &sub_consumed ); - if( error != NULL ) - { - swq_expr_free( *expr_out ); - *expr_out = NULL; - return error; - } - - parent = (swq_field_op *) SWQ_MALLOC(sizeof(swq_field_op)); - memset( parent, 0, sizeof(swq_field_op) ); - parent->field_index = -1; - - parent->first_sub_expr = (struct swq_node_s *) *expr_out; - parent->second_sub_expr = (struct swq_node_s *) remainder; - parent->operation = op_code; - - *expr_out = parent; - - *tokens_consumed += sub_consumed + 1; - } - - return NULL; -} - -/************************************************************************/ -/* swq_expr_compile() */ -/************************************************************************/ - -const char *swq_expr_compile( const char *where_clause, - int field_count, - char **field_names, - swq_field_type *field_types, - swq_expr **expr_out ) - -{ - swq_field_list field_list; - - field_list.count = field_count; - field_list.names = field_names; - field_list.types = field_types; - field_list.table_ids = NULL; - field_list.ids = NULL; - - field_list.table_count = 0; - field_list.table_defs = NULL; - - return swq_expr_compile2( where_clause, &field_list, expr_out ); -} - - -/************************************************************************/ -/* swq_expr_compile2() */ -/************************************************************************/ - -const char *swq_expr_compile2( const char *where_clause, - swq_field_list *field_list, - swq_expr **expr_out ) - -{ -#define TOKEN_BLOCK_SIZE 1024 - char **token_list, *rest_of_expr; - int token_count = 0; - int tokens_consumed, i, token_list_size; - const char *error; - - /* - ** Collect token array. - */ - token_list = (char **)SWQ_MALLOC(sizeof(char *) * TOKEN_BLOCK_SIZE); - token_list_size = TOKEN_BLOCK_SIZE; - rest_of_expr = (char *) where_clause; - while( (token_list[token_count] = - swq_token(rest_of_expr,&rest_of_expr,NULL)) != NULL ) - { - token_count++; - if (token_count == token_list_size) - { - token_list = (char **)swq_realloc(token_list, - sizeof(char *) * token_list_size, - sizeof(char *) * (token_list_size + TOKEN_BLOCK_SIZE)); - token_list_size += TOKEN_BLOCK_SIZE; - } - } - - /* - ** Parse the expression. - */ - *expr_out = NULL; - error = - swq_subexpr_compile( token_list, field_list, expr_out, - &tokens_consumed ); - - for( i = 0; i < token_count; i++ ) - SWQ_FREE( token_list[i] ); - - SWQ_FREE(token_list); - - if( error != NULL ) - return error; - - if( tokens_consumed < token_count ) - { - swq_expr_free( *expr_out ); - *expr_out = NULL; - SNPRINTF_ERR2( "Syntax error, %d extra tokens", - token_count - tokens_consumed ); - return swq_get_errbuf(); - } - - return NULL; -} - -/************************************************************************/ -/* swq_expr_free() */ -/************************************************************************/ - -void swq_expr_free( swq_expr *expr ) - -{ - if( expr == NULL ) - return; - - if( expr->first_sub_expr != NULL ) - swq_expr_free( (swq_expr *) expr->first_sub_expr ); - if( expr->second_sub_expr != NULL ) - swq_expr_free( (swq_expr *) expr->second_sub_expr ); - - if( expr->string_value != NULL ) - SWQ_FREE( expr->string_value ); - - SWQ_FREE( expr ); -} - -/************************************************************************/ -/* swq_expr_evaluate() */ -/************************************************************************/ - -int swq_expr_evaluate( swq_expr *expr, swq_op_evaluator fn_evaluator, - void *record_handle ) - -{ - if( expr->operation == SWQ_OR ) - { - return swq_expr_evaluate( (swq_expr *) expr->first_sub_expr, - fn_evaluator, - record_handle) - || swq_expr_evaluate( (swq_expr *) expr->second_sub_expr, - fn_evaluator, - record_handle); - } - else if( expr->operation == SWQ_AND ) - { - return swq_expr_evaluate( (swq_expr *) expr->first_sub_expr, - fn_evaluator, - record_handle) - && swq_expr_evaluate( (swq_expr *) expr->second_sub_expr, - fn_evaluator, - record_handle); - } - else if( expr->operation == SWQ_NOT ) - { - return !swq_expr_evaluate( (swq_expr *) expr->second_sub_expr, - fn_evaluator, - record_handle); - } - else - { - return fn_evaluator( expr, record_handle ); - } -} - -/************************************************************************/ -/* swq_expr_dump() */ -/************************************************************************/ - -void swq_expr_dump( swq_expr *expr, FILE * fp, int depth ) - -{ - char spaces[60]; - int i; - const char *op_name = "unknown"; - - for( i = 0; i < depth*2 && i < sizeof(spaces); i++ ) - spaces[i] = ' '; - spaces[i] = '\0'; - - /* - ** first term. - */ - if( expr->first_sub_expr != NULL ) - swq_expr_dump( (swq_expr *) expr->first_sub_expr, fp, depth + 1 ); - else - fprintf( fp, "%s Field %d\n", spaces, expr->field_index ); - - /* - ** Operation. - */ - if( expr->operation == SWQ_OR ) - op_name = "OR"; - if( expr->operation == SWQ_AND ) - op_name = "AND"; - if( expr->operation == SWQ_NOT) - op_name = "NOT"; - if( expr->operation == SWQ_GT ) - op_name = ">"; - if( expr->operation == SWQ_LT ) - op_name = "<"; - if( expr->operation == SWQ_EQ ) - op_name = "="; - if( expr->operation == SWQ_NE ) - op_name = "!="; - if( expr->operation == SWQ_GE ) - op_name = ">="; - if( expr->operation == SWQ_LE ) - op_name = "<="; - if( expr->operation == SWQ_LIKE ) - op_name = "LIKE"; - if( expr->operation == SWQ_ISNULL ) - op_name = "IS NULL"; - if( expr->operation == SWQ_IN ) - op_name = "IN"; - - fprintf( fp, "%s%s\n", spaces, op_name ); - - /* - ** Second term. - */ - if( expr->second_sub_expr != NULL ) - swq_expr_dump( (swq_expr *) expr->second_sub_expr, fp, depth + 1 ); - else if( expr->operation == SWQ_IN || expr->operation == SWQ_NOTIN ) - { - const char *src; - - fprintf( fp, "%s (\"%s\"", spaces, expr->string_value ); - src = expr->string_value + strlen(expr->string_value) + 1; - while( *src != '\0' ) - { - fprintf( fp, ",\"%s\"", src ); - src += strlen(src) + 1; - } - - fprintf( fp, ")\n" ); - } - else if( expr->string_value != NULL ) - fprintf( fp, "%s %s\n", spaces, expr->string_value ); -} - -/************************************************************************/ -/* ==================================================================== */ -/* SELECT statement parsing */ -/* ==================================================================== */ -/************************************************************************/ - -/* -Supported SQL Syntax: - -SELECT FROM - [LEFT JOIN - ON [.] = [.].]* - [WHERE ] - [ORDER BY ] - - ::= [ { , }... ] - - ::= [ ] - | CAST ( AS ) [ ] - - ::= [DISTINCT] - | ( [DISTINCT] ) - | Count(*) - - ::= [ AS ] - - ::= character [ ( field_length ) ] - | float [ ( field_length ) ] - | numeric [ ( field_length [, field_precision ] ) ] - | integer [ ( field_length ) ] - | date [ ( field_length ) ] - | time [ ( field_length ) ] - | timestamp [ ( field_length ) ] - - ::= AVG | MAX | MIN | SUM | COUNT - - ::= [.]field_name - - ::= - [ { }... ] - - ::= [ ] - - ::= - - ::= ASC | DESC - - ::= [''.]table_name [table_alias] - - ::= table_name | table_alias - */ - -static int swq_parse_table_def( swq_select *select_info, - int *is_literal, - char **token, char **input ); - -static int swq_parse_typename( swq_col_def *col_def, - int *is_literal, - char **token, char **input ); - -/************************************************************************/ -/* swq_select_preparse() */ -/************************************************************************/ - -const char *swq_select_preparse( const char *select_statement, - swq_select **select_info_ret ) - -{ - swq_select *select_info; - char *token; - char *input; - int is_literal; - int type_cast; - swq_col_def *swq_cols; - -#define MAX_COLUMNS 250 - - *select_info_ret = NULL; - - if (select_statement == NULL || select_statement[0] == '\0') - { - SNPRINTF_ERR1( "Empty SQL request string" ); - return swq_get_errbuf(); - } - -/* -------------------------------------------------------------------- */ -/* Get first token. Ensure it is SELECT. */ -/* -------------------------------------------------------------------- */ - token = swq_token( select_statement, &input, NULL ); - if( strcasecmp(token,"select") != 0 ) - { - SWQ_FREE( token ); - SNPRINTF_ERR1( "Missing keyword SELECT" ); - return swq_get_errbuf(); - } - SWQ_FREE( token ); - -/* -------------------------------------------------------------------- */ -/* allocate selection structure. */ -/* -------------------------------------------------------------------- */ - select_info = (swq_select *) SWQ_MALLOC(sizeof(swq_select)); - memset( select_info, 0, sizeof(swq_select) ); - - select_info->raw_select = swq_strdup( select_statement ); - -/* -------------------------------------------------------------------- */ -/* Allocate a big field list. */ -/* -------------------------------------------------------------------- */ - swq_cols = (swq_col_def *) SWQ_MALLOC(sizeof(swq_col_def) * MAX_COLUMNS); - memset( swq_cols, 0, sizeof(swq_col_def) * MAX_COLUMNS ); - - select_info->column_defs = swq_cols; - -/* -------------------------------------------------------------------- */ -/* Collect the field list, terminated by FROM keyword. */ -/* -------------------------------------------------------------------- */ - token = swq_token( input, &input, &is_literal ); - while( token != NULL - && (is_literal || strcasecmp(token,"FROM") != 0) ) - { - char *next_token; - int next_is_literal; - - if( select_info->result_columns == MAX_COLUMNS ) - { - SWQ_FREE( token ); - swq_select_free( select_info ); - SNPRINTF_ERR2( - "More than MAX_COLUMNS (%d) columns in SELECT statement.", - MAX_COLUMNS ); - return swq_get_errbuf(); - } - - /* Ensure that we have a comma before fields other than the first. */ - - if( select_info->result_columns > 0 ) - { - if( strcasecmp(token,",") != 0 ) - { - SNPRINTF_ERR2( - "Missing comma after column %s in SELECT statement.", - swq_cols[select_info->result_columns-1].field_name ); - SWQ_FREE( token ); - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - } - - /* set up some default values. */ - swq_cols[select_info->result_columns].field_precision = -1; - swq_cols[select_info->result_columns].target_type = SWQ_OTHER; - select_info->result_columns++; - - next_token = swq_token( input, &input, &next_is_literal ); - - /* Detect the type cast. */ - type_cast = 0; - if (token != NULL && next_token != NULL &&strcasecmp(token,"CAST") == 0 - && strcasecmp(next_token,"(") == 0) - { - type_cast = 1; - SWQ_FREE( token ); - SWQ_FREE( next_token ); - token = swq_token( input, &input, &is_literal ); - next_token = swq_token( input, &input, &next_is_literal ); - } - - /* - ** Handle function operators. - */ - if( !is_literal && !next_is_literal && next_token != NULL - && strcasecmp(next_token,"(") == 0 ) - { - SWQ_FREE( next_token ); - - swq_cols[select_info->result_columns-1].col_func_name = token; - - token = swq_token( input, &input, &is_literal ); - - if( token != NULL && !is_literal - && strcasecmp(token,"DISTINCT") == 0 ) - { - swq_cols[select_info->result_columns-1].distinct_flag = 1; - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - } - - swq_cols[select_info->result_columns-1].field_name = token; - - token = swq_token( input, &input, &is_literal ); - - if( token == NULL || strcasecmp(token,")") != 0 ) - { - if( token != NULL ) - SWQ_FREE( token ); - swq_select_free( select_info ); - return "Missing closing bracket in field function."; - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - } - - /* - ** Handle simple field. - */ - else - { - if( token != NULL && !is_literal - && strcasecmp(token,"DISTINCT") == 0 ) - { - swq_cols[select_info->result_columns-1].distinct_flag = 1; - - SWQ_FREE( token ); - token = next_token; - is_literal = next_is_literal; - - next_token = swq_token( input, &input, &next_is_literal ); - } - - swq_cols[select_info->result_columns-1].field_name = token; - token = next_token; - is_literal = next_is_literal; - } - - /* handle the type cast*/ - if (type_cast && token != NULL) - { - if (strcasecmp(token,"AS") != 0) - { - SWQ_FREE( token ); - swq_select_free( select_info ); - return "Missing 'AS' keyword in the type cast in SELECT statement."; - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - - /* processing the typename */ - if( swq_parse_typename( &swq_cols[select_info->result_columns-1], &is_literal, &token, &input) != 0 ) - { - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - if (token != NULL && strcasecmp(token,")") != 0) - { - if( token != NULL ) - SWQ_FREE( token ); - swq_select_free( select_info ); - return "Missing closing bracket after the type cast in SELECT statement."; - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - - type_cast = 0; - } - - /* Handle the field alias */ - if( token != NULL && strcasecmp(token,",") != 0 && strcasecmp(token,"from") != 0) - { - /* Skip field alias keyword. */ - if (strcasecmp(token,"AS") == 0) - { - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - if (token == NULL) - { - swq_select_free( select_info ); - return "Unexpected terminator after the type cast in SELECT statement."; - } - } - swq_cols[select_info->result_columns-1].field_alias = token; - token = swq_token( input, &input, &is_literal ); - } - } - - /* make a columns_def list that is just the right size. */ - select_info->column_defs = (swq_col_def *) - SWQ_MALLOC(sizeof(swq_col_def) * select_info->result_columns); - memcpy( select_info->column_defs, swq_cols, - sizeof(swq_col_def) * select_info->result_columns ); - SWQ_FREE( swq_cols ); - -/* -------------------------------------------------------------------- */ -/* Collect the table name from the FROM clause. */ -/* -------------------------------------------------------------------- */ - if( token == NULL || strcasecmp(token,"FROM") != 0 ) - { - SNPRINTF_ERR1( "Missing FROM clause in SELECT statement." ); - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - - if( token == NULL ) - { - SNPRINTF_ERR1( "Missing table name in FROM clause." ); - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - if( swq_parse_table_def( select_info, &is_literal, &token, &input) != 0 ) - { - swq_select_free( select_info ); - return swq_get_errbuf(); - } - -/* -------------------------------------------------------------------- */ -/* Do we have a LEFT JOIN (or just JOIN) clause? */ -/* -------------------------------------------------------------------- */ - while( token != NULL - && (strcasecmp(token,"LEFT") == 0 - || strcasecmp(token,"JOIN") == 0) ) - { - swq_join_def *join_info; - - if( strcasecmp(token,"LEFT") == 0 ) - { - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - - if( token == NULL || strcasecmp(token,"JOIN") != 0 ) - { - SNPRINTF_ERR1( "Missing JOIN keyword after LEFT." ); - swq_select_free( select_info ); - return swq_get_errbuf(); - } - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - - /* Create join definition structure */ - select_info->join_defs = (swq_join_def *) - swq_realloc( select_info->join_defs, - sizeof(swq_join_def) * (select_info->join_count), - sizeof(swq_join_def) * (select_info->join_count+1) ); - - join_info = select_info->join_defs + select_info->join_count++; - - /* Parse out target table */ - join_info->secondary_table = - swq_parse_table_def( select_info, &is_literal, &token, &input); - - if( join_info->secondary_table < 0 ) - { - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - /* Check for ON keyword */ - if( token == NULL ) - token = swq_token( input, &input, &is_literal ); - - if( token == NULL || strcasecmp(token,"ON") != 0 ) - { - swq_select_free( select_info ); - SNPRINTF_ERR1( "Corrupt JOIN clause, expecting ON keyword." ); - return swq_get_errbuf(); - } - - SWQ_FREE( token ); - - join_info->primary_field_name = - swq_token( input, &input, &is_literal ); - - token = swq_token( input, &input, &is_literal ); - if( token == NULL || strcasecmp(token,"=") != 0 ) - { - swq_select_free( select_info ); - SNPRINTF_ERR1( "Corrupt JOIN clause, expecting '=' condition."); - return swq_get_errbuf(); - } - - SWQ_FREE( token ); - - join_info->op = SWQ_EQ; - - join_info->secondary_field_name = - swq_token( input, &input, &is_literal ); - - if( join_info->secondary_field_name == NULL ) - { - swq_select_free( select_info ); - SNPRINTF_ERR1( "Corrupt JOIN clause, missing secondary field."); - return swq_get_errbuf(); - } - - token = swq_token( input, &input, &is_literal ); - } - -/* -------------------------------------------------------------------- */ -/* Do we have a WHERE clause? */ -/* -------------------------------------------------------------------- */ - if( token != NULL && strcasecmp(token,"WHERE") == 0 ) - { - const char *where_base = input; - - while( *where_base == ' ' ) - where_base++; - - SWQ_FREE( token ); - - token = swq_token( input, &input, &is_literal ); - while( token != NULL ) - { - if( strcasecmp(token,"ORDER") == 0 && !is_literal ) - { - break; - } - - if( token != NULL ) - { - SWQ_FREE( token ); - - token = swq_token( input, &input, &is_literal ); - } - } - - select_info->whole_where_clause = swq_strdup(where_base); - - if( input != NULL ) - { - if( token != NULL ) - select_info->whole_where_clause[input - where_base - strlen(token)] = '\0'; - else - select_info->whole_where_clause[input - where_base] = '\0'; - } - } - -/* -------------------------------------------------------------------- */ -/* Parse ORDER BY clause. */ -/* -------------------------------------------------------------------- */ - if( token != NULL && strcasecmp(token,"ORDER") == 0 ) - { - SWQ_FREE( token ); - - token = swq_token( input, &input, &is_literal ); - - if( token == NULL || strcasecmp(token,"BY") != 0 ) - { - if( token != NULL ) - SWQ_FREE( token ); - - SNPRINTF_ERR1( "ORDER BY clause missing BY keyword." ); - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - while( token != NULL - && (select_info->order_specs == 0 - || strcasecmp(token,",") == 0) ) - { - swq_order_def *old_defs = select_info->order_defs; - swq_order_def *def; - - if( select_info->order_specs != 0 ) - { - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - } - - select_info->order_defs = (swq_order_def *) - SWQ_MALLOC(sizeof(swq_order_def)*(select_info->order_specs+1)); - - if( old_defs != NULL ) - { - memcpy( select_info->order_defs, old_defs, - sizeof(swq_order_def)*select_info->order_specs ); - SWQ_FREE( old_defs ); - } - - def = select_info->order_defs + select_info->order_specs; - def->field_name = token; - def->field_index = 0; - def->ascending_flag = 1; - - token = swq_token( input, &input, &is_literal ); - if( token != NULL && strcasecmp(token,"DESC") == 0 ) - { - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - - def->ascending_flag = 0; - } - else if( token != NULL && strcasecmp(token,"ASC") == 0 ) - { - SWQ_FREE( token ); - token = swq_token( input, &input, &is_literal ); - } - - select_info->order_specs++; - } - } - -/* -------------------------------------------------------------------- */ -/* If we have anything left it indicates an error! */ -/* -------------------------------------------------------------------- */ - if( token != NULL ) - { - - SNPRINTF_ERR2( - "Failed to parse SELECT statement, extra input at %s token.", - token ); - - SWQ_FREE( token ); - swq_select_free( select_info ); - return swq_get_errbuf(); - } - - *select_info_ret = select_info; - - return NULL; -} - -/************************************************************************/ -/* swq_parse_typename() */ -/************************************************************************/ - -static int swq_parse_typename( swq_col_def *col_def, - int *is_literal, - char **token, char **input ) - -{ - int parse_length; - int parse_precision; - - if( *token == NULL ) - *token = swq_token( *input, input, is_literal ); - - if( *token == NULL ) - { - SNPRINTF_ERR1( "Corrupt type name, insufficient tokens." ); - return -1; - } - -/* -------------------------------------------------------------------- */ -/* Check for the SQL92 typenames */ -/* -------------------------------------------------------------------- */ - parse_length = 0; - parse_precision = 0; - if( strcasecmp(*token,"character") == 0 ) - { - col_def->target_type = SWQ_STRING; - col_def->field_length = 1; - parse_length = 1; - } - else if( strcasecmp(*token,"integer") == 0 ) - { - col_def->target_type = SWQ_INTEGER; - parse_length = 1; - } - else if( strcasecmp(*token,"float") == 0 ) - { - col_def->target_type = SWQ_FLOAT; - parse_length = 1; - } - else if( strcasecmp(*token,"numeric") == 0 ) - { - col_def->target_type = SWQ_FLOAT; - parse_length = 1; - parse_precision = 1; - } - else if( strcasecmp(*token,"timestamp") == 0 ) - { - col_def->target_type = SWQ_TIMESTAMP; - parse_length = 1; - } - else if( strcasecmp(*token,"date") == 0 ) - { - col_def->target_type = SWQ_DATE; - parse_length = 1; - } - else if( strcasecmp(*token,"time") == 0 ) - { - col_def->target_type = SWQ_TIME; - parse_length = 1; - } - else - { - SNPRINTF_ERR2( "Unrecognized typename %s.", *token ); - SWQ_FREE( *token ); - *token = NULL; - return -1; - } - - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - -/* -------------------------------------------------------------------- */ -/* Check for the field length and precision */ -/* -------------------------------------------------------------------- */ - if (parse_length && *token != NULL && strcasecmp(*token,"(") == 0) - { - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - - if (*token != NULL) - { - col_def->field_length = atoi( *token ); - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - } - - if (parse_precision && *token != NULL && strcasecmp(*token,",") == 0) - { - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - if (*token != NULL) - { - col_def->field_precision = atoi( *token ); - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - } - } - - if (*token == NULL || strcasecmp(*token,")") != 0) - { - if (*token != NULL) - { - SWQ_FREE( *token ); - *token = NULL; - } - SNPRINTF_ERR1( "Missing closing bracket in the field length specifier." ); - return -1; - } - - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - } - return 0; -} - -/************************************************************************/ -/* swq_parse_table_def() */ -/* */ -/* Supported table definition forms: */ -/* */ -/* :== table_name */ -/* | 'data_source'.table_name */ -/* | table_name table_alias */ -/* | 'data_source'.table_name table_alias */ -/************************************************************************/ - -static int swq_parse_table_def( swq_select *select_info, - int *is_literal, - char **token, char **input ) - -{ - int i; - char *datasource = NULL; - char *table = NULL; - char *alias = NULL; - - if( *token == NULL ) - *token = swq_token( *input, input, is_literal ); - - if( *token == NULL ) - { - SNPRINTF_ERR1( "Corrupt table definition, insufficient tokens." ); - return -1; - } - -/* -------------------------------------------------------------------- */ -/* Do we have a datasource literal? */ -/* -------------------------------------------------------------------- */ - if( *token != NULL && *is_literal ) - { - datasource = *token; - *token = swq_token( *input, input, is_literal ); - - if( *token == NULL ) - { - *token = datasource; - datasource = NULL; - } - } - -/* -------------------------------------------------------------------- */ -/* Get the table name. Remove the '.' used to qualify it */ -/* relative to the datasource name if found. */ -/* -------------------------------------------------------------------- */ - if( datasource != NULL && (*token)[0] != '.' ) - { - table = datasource; - datasource = NULL; - } - else if( (*token)[0] == '.' ) - { - table = swq_strdup( (*token) + 1 ); - SWQ_FREE( *token ); - *token = swq_token( *input, input, is_literal ); - } - else - { - table = *token; - *token = swq_token( *input, input, is_literal ); - } - -/* -------------------------------------------------------------------- */ -/* Was an alias provided? */ -/* -------------------------------------------------------------------- */ - if( *token != NULL && ! *is_literal - && strcasecmp(*token,"ON") != 0 - && strcasecmp(*token,"ORDER") != 0 - && strcasecmp(*token,"WHERE") != 0 - && strcasecmp(*token,"LEFT") != 0 - && strcasecmp(*token,"JOIN") != 0 ) - { - alias = *token; - *token = swq_token( *input, input, is_literal ); - } - -/* -------------------------------------------------------------------- */ -/* Does this match an existing table definition? */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < select_info->table_count; i++ ) - { - swq_table_def *table_def = select_info->table_defs + i; - - if( datasource == NULL - && alias == NULL - && strcasecmp(table_def->table_alias,table) == 0 ) - return i; - - if( datasource != NULL && table_def->data_source != NULL - && strcasecmp(datasource,table_def->data_source) == 0 - && strcasecmp(table,table_def->table_name) == 0 ) - return i; - } - -/* -------------------------------------------------------------------- */ -/* Add a new entry to the tables table. */ -/* -------------------------------------------------------------------- */ - select_info->table_defs = - swq_realloc( select_info->table_defs, - sizeof(swq_table_def) * (select_info->table_count), - sizeof(swq_table_def) * (select_info->table_count+1) ); - -/* -------------------------------------------------------------------- */ -/* Populate the new entry. */ -/* -------------------------------------------------------------------- */ - if( alias == NULL ) - alias = swq_strdup( table ); - - select_info->table_defs[select_info->table_count].data_source = datasource; - select_info->table_defs[select_info->table_count].table_name = table; - select_info->table_defs[select_info->table_count].table_alias = alias; - - select_info->table_count++; - - return select_info->table_count - 1; -} - -/************************************************************************/ -/* swq_select_expand_wildcard() */ -/* */ -/* This function replaces the '*' in a "SELECT *" with the list */ -/* provided list of fields. Itis used by swq_select_parse(), */ -/* but may be called in advance by applications wanting the */ -/* "default" field list to be different than the full list of */ -/* fields. */ -/************************************************************************/ - -const char *swq_select_expand_wildcard( swq_select *select_info, - swq_field_list *field_list ) - -{ - int isrc; - -/* ==================================================================== */ -/* Check each pre-expansion field. */ -/* ==================================================================== */ - for( isrc = 0; isrc < select_info->result_columns; isrc++ ) - { - const char *src_fieldname = select_info->column_defs[isrc].field_name; - int itable, new_fields, i, iout; - - if( src_fieldname[strlen(src_fieldname)-1] != '*' ) - continue; - - /* We don't want to expand COUNT(*) */ - if( select_info->column_defs[isrc].col_func_name != NULL ) - continue; - -/* -------------------------------------------------------------------- */ -/* Parse out the table name, verify it, and establish the */ -/* number of fields to insert from it. */ -/* -------------------------------------------------------------------- */ - if( strcmp(src_fieldname,"*") == 0 ) - { - itable = -1; - new_fields = field_list->count; - } - else if( strlen(src_fieldname) < 3 - || src_fieldname[strlen(src_fieldname)-2] != '.' ) - { - SNPRINTF_ERR2( "Ill formatted field definition '%s'.", - src_fieldname ); - return swq_get_errbuf(); - } - else - { - char *table_name = swq_strdup( src_fieldname ); - table_name[strlen(src_fieldname)-2] = '\0'; - - for( itable = 0; itable < field_list->table_count; itable++ ) - { - if( strcasecmp(table_name, - field_list->table_defs[itable].table_alias ) == 0 ) - break; - } - - if( itable == field_list->table_count ) - { - SNPRINTF_ERR3( - "Table %s not recognised from %s definition.", - table_name, src_fieldname ); - swq_free( table_name ); - return swq_get_errbuf(); - } - swq_free( table_name ); - - /* count the number of fields in this table. */ - new_fields = 0; - for( i = 0; i < field_list->count; i++ ) - { - if( field_list->table_ids[i] == itable ) - new_fields++; - } - } - - if (new_fields > 0) - { -/* -------------------------------------------------------------------- */ -/* Reallocate the column list larger. */ -/* -------------------------------------------------------------------- */ - SWQ_FREE( select_info->column_defs[isrc].field_name ); - select_info->column_defs = (swq_col_def *) - swq_realloc( select_info->column_defs, - sizeof(swq_col_def) * select_info->result_columns, - sizeof(swq_col_def) * - (select_info->result_columns + new_fields - 1 ) ); - -/* -------------------------------------------------------------------- */ -/* Push the old definitions that came after the one to be */ -/* replaced further up in the array. */ -/* -------------------------------------------------------------------- */ - if (new_fields != 1) - { - for( i = select_info->result_columns-1; i > isrc; i-- ) - { - memcpy( select_info->column_defs + i + new_fields - 1, - select_info->column_defs + i, - sizeof( swq_col_def ) ); - } - } - - select_info->result_columns += (new_fields - 1 ); - -/* -------------------------------------------------------------------- */ -/* Zero out all the stuff in the target column definitions. */ -/* -------------------------------------------------------------------- */ - memset( select_info->column_defs + isrc, 0, - new_fields * sizeof(swq_col_def) ); - } - else - { -/* -------------------------------------------------------------------- */ -/* The wildcard expands to nothing */ -/* -------------------------------------------------------------------- */ - SWQ_FREE( select_info->column_defs[isrc].field_name ); - memmove( select_info->column_defs + isrc, - select_info->column_defs + isrc + 1, - sizeof( swq_col_def ) * (select_info->result_columns-1-isrc) ); - - select_info->result_columns --; - } - -/* -------------------------------------------------------------------- */ -/* Assign the selected fields. */ -/* -------------------------------------------------------------------- */ - iout = isrc; - - for( i = 0; i < field_list->count; i++ ) - { - swq_col_def *def; - int compose = itable != -1; - - /* skip this field if it isn't in the target table. */ - if( itable != -1 && field_list->table_ids != NULL - && itable != field_list->table_ids[i] ) - continue; - - /* set up some default values. */ - def = select_info->column_defs + iout; - def->field_precision = -1; - def->target_type = SWQ_OTHER; - - /* does this field duplicate an earlier one? */ - if( field_list->table_ids != NULL - && field_list->table_ids[i] != 0 - && !compose ) - { - int other; - - for( other = 0; other < i; other++ ) - { - if( strcasecmp(field_list->names[i], - field_list->names[other]) == 0 ) - { - compose = 1; - break; - } - } - } - - if( !compose ) - def->field_name = swq_strdup( field_list->names[i] ); - else - { - int itable = field_list->table_ids[i]; - char *composed_name; - const char *field_name = field_list->names[i]; - const char *table_alias = - field_list->table_defs[itable].table_alias; - - composed_name = (char *) - swq_malloc(strlen(field_name)+strlen(table_alias)+2); - - sprintf( composed_name, "%s.%s", table_alias, field_name ); - - def->field_name = composed_name; - } - - iout++; - - /* All the other table info will be provided by the later - parse operation. */ - } - - /* If there are several occurrences of '*', go on, but stay on the */ - /* same index in case '*' is expanded to nothing */ - /* (the -- is to compensate the fact that isrc will be incremented in */ - /* the after statement of the for loop) */ - isrc --; - } - - - - return NULL; -} - -/************************************************************************/ -/* swq_select_parse() */ -/************************************************************************/ - -const char *swq_select_parse( swq_select *select_info, - swq_field_list *field_list, - int parse_flags ) - -{ - int i; - const char *error; - - error = swq_select_expand_wildcard( select_info, field_list ); - if( error != NULL ) - return error; - -/* -------------------------------------------------------------------- */ -/* Identify field information. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < select_info->result_columns; i++ ) - { - swq_col_def *def = select_info->column_defs + i; - swq_field_type this_type; - - /* identify field */ - def->field_index = swq_identify_field( def->field_name, field_list, - &this_type, - &(def->table_index) ); - - /* record field type */ - def->field_type = this_type; - - /* identify column function if present */ - if( def->col_func_name != NULL ) - { - if( strcasecmp(def->col_func_name,"AVG") == 0 ) - def->col_func = SWQCF_AVG; - else if( strcasecmp(def->col_func_name,"MIN") == 0 ) - def->col_func = SWQCF_MIN; - else if( strcasecmp(def->col_func_name,"MAX") == 0 ) - def->col_func = SWQCF_MAX; - else if( strcasecmp(def->col_func_name,"SUM") == 0 ) - def->col_func = SWQCF_SUM; - else if( strcasecmp(def->col_func_name,"COUNT") == 0 ) - def->col_func = SWQCF_COUNT; - else - { - def->col_func = SWQCF_CUSTOM; - if( !(parse_flags & SWQP_ALLOW_UNDEFINED_COL_FUNCS) ) - { - SNPRINTF_ERR2( "Unrecognised field function %s.", - def->col_func_name ); - return swq_get_errbuf(); - } - } - - if( (def->col_func == SWQCF_MIN - || def->col_func == SWQCF_MAX - || def->col_func == SWQCF_AVG - || def->col_func == SWQCF_SUM) - && this_type == SWQ_STRING ) - { - SNPRINTF_ERR3( - "Use of field function %s() on string field %s illegal.", - def->col_func_name, def->field_name ); - return swq_get_errbuf(); - } - } - else - def->col_func = SWQCF_NONE; - - if( def->field_index == -1 && def->col_func != SWQCF_COUNT ) - { - SNPRINTF_ERR2( "Unrecognised field name %s.", - def->field_name ); - return swq_get_errbuf(); - } - } - -/* -------------------------------------------------------------------- */ -/* Check if we are producing a one row summary result or a set */ -/* of records. Generate an error if we get conflicting */ -/* indications. */ -/* -------------------------------------------------------------------- */ - select_info->query_mode = -1; - for( i = 0; i < select_info->result_columns; i++ ) - { - swq_col_def *def = select_info->column_defs + i; - int this_indicator = -1; - - if( def->col_func == SWQCF_MIN - || def->col_func == SWQCF_MAX - || def->col_func == SWQCF_AVG - || def->col_func == SWQCF_SUM - || def->col_func == SWQCF_COUNT ) - this_indicator = SWQM_SUMMARY_RECORD; - else if( def->col_func == SWQCF_NONE ) - { - if( def->distinct_flag ) - this_indicator = SWQM_DISTINCT_LIST; - else - this_indicator = SWQM_RECORDSET; - } - - if( this_indicator != select_info->query_mode - && this_indicator != -1 - && select_info->query_mode != -1 ) - { - return "Field list implies mixture of regular recordset mode, summary mode or distinct field list mode."; - } - - if( this_indicator != -1 ) - select_info->query_mode = this_indicator; - } - - if( select_info->result_columns > 1 - && select_info->query_mode == SWQM_DISTINCT_LIST ) - { - return "SELECTing more than one DISTINCT field is a query not supported."; - } - else if (select_info->result_columns == 0) - { - select_info->query_mode = SWQM_RECORDSET; - } - -/* -------------------------------------------------------------------- */ -/* Process column names in JOIN specs. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < select_info->join_count; i++ ) - { - swq_join_def *def = select_info->join_defs + i; - int table_id; - - /* identify primary field */ - def->primary_field = swq_identify_field( def->primary_field_name, - field_list, NULL, &table_id ); - if( def->primary_field == -1 ) - { - SNPRINTF_ERR2( - "Unrecognised primary field %s in JOIN clause..", - def->primary_field_name ); - return swq_get_errbuf(); - } - - if( table_id != 0 ) - { - SNPRINTF_ERR2( - "Currently the primary key must come from the primary table in\n" - "JOIN, %s is not from the primary table.", - def->primary_field_name ); - return swq_get_errbuf(); - } - - /* identify secondary field */ - def->secondary_field = swq_identify_field( def->secondary_field_name, - field_list, NULL,&table_id); - if( def->secondary_field == -1 ) - { - SNPRINTF_ERR2( - "Unrecognised secondary field %s in JOIN clause..", - def->primary_field_name ); - return swq_get_errbuf(); - } - - if( table_id != def->secondary_table ) - { - SNPRINTF_ERR3( - "Currently the secondary key must come from the secondary table\n" - "listed in the JOIN. %s is not from table %s..", - def->primary_field_name, - select_info->table_defs[def->secondary_table].table_name); - return swq_get_errbuf(); - } - } - -/* -------------------------------------------------------------------- */ -/* Process column names in order specs. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < select_info->order_specs; i++ ) - { - swq_order_def *def = select_info->order_defs + i; - - /* identify field */ - def->field_index = swq_identify_field( def->field_name, field_list, - NULL, &(def->table_index) ); - if( def->field_index == -1 ) - { - SNPRINTF_ERR2( "Unrecognised field name %s in ORDER BY.", - def->field_name ); - return swq_get_errbuf(); - } - } - -/* -------------------------------------------------------------------- */ -/* Parse the WHERE clause. */ -/* -------------------------------------------------------------------- */ - if( select_info->whole_where_clause != NULL ) - { - const char *error; - - error = swq_expr_compile2( select_info->whole_where_clause, - field_list, &(select_info->where_expr) ); - - if( error != NULL ) - return error; - } - - return NULL; -} - -/************************************************************************/ -/* swq_select_summarize() */ -/************************************************************************/ - -const char * -swq_select_summarize( swq_select *select_info, - int dest_column, const char *value ) - -{ - swq_col_def *def = select_info->column_defs + dest_column; - swq_summary *summary; - -/* -------------------------------------------------------------------- */ -/* Do various checking. */ -/* -------------------------------------------------------------------- */ - if( !select_info->query_mode == SWQM_RECORDSET ) - return "swq_select_summarize() called on non-summary query."; - - if( dest_column < 0 || dest_column >= select_info->result_columns ) - return "dest_column out of range in swq_select_summarize()."; - - if( def->col_func == SWQCF_NONE && !def->distinct_flag ) - return NULL; - -/* -------------------------------------------------------------------- */ -/* Create the summary information if this is the first row */ -/* being processed. */ -/* -------------------------------------------------------------------- */ - if( select_info->column_summary == NULL ) - { - int i; - - select_info->column_summary = (swq_summary *) - SWQ_MALLOC(sizeof(swq_summary) * select_info->result_columns); - memset( select_info->column_summary, 0, - sizeof(swq_summary) * select_info->result_columns ); - - for( i = 0; i < select_info->result_columns; i++ ) - { - select_info->column_summary[i].min = 1e20; - select_info->column_summary[i].max = -1e20; - } - } - -/* -------------------------------------------------------------------- */ -/* If distinct processing is on, process that now. */ -/* -------------------------------------------------------------------- */ - summary = select_info->column_summary + dest_column; - - if( def->distinct_flag ) - { - int i; - - /* This should be implemented with a much more complicated - data structure to achieve any sort of efficiency. */ - for( i = 0; i < summary->count; i++ ) - { - if( strcmp(value,summary->distinct_list[i]) == 0 ) - break; - } - - if( i == summary->count ) - { - char **old_list = summary->distinct_list; - - summary->distinct_list = (char **) - SWQ_MALLOC(sizeof(char *) * (summary->count+1)); - memcpy( summary->distinct_list, old_list, - sizeof(char *) * summary->count ); - summary->distinct_list[(summary->count)++] = - swq_strdup( value ); - - SWQ_FREE(old_list); - } - } - -/* -------------------------------------------------------------------- */ -/* Process various options. */ -/* -------------------------------------------------------------------- */ - - switch( def->col_func ) - { - case SWQCF_MIN: - if( value != NULL && value[0] != '\0' ) - { - double df_val = atof(value); - if( df_val < summary->min ) - summary->min = df_val; - } - break; - case SWQCF_MAX: - if( value != NULL && value[0] != '\0' ) - { - double df_val = atof(value); - if( df_val > summary->max ) - summary->max = df_val; - } - break; - case SWQCF_AVG: - case SWQCF_SUM: - if( value != NULL && value[0] != '\0' ) - { - summary->count++; - summary->sum += atof(value); - } - break; - - case SWQCF_COUNT: - if( value != NULL && !def->distinct_flag ) - summary->count++; - break; - - case SWQCF_NONE: - break; - - case SWQCF_CUSTOM: - return "swq_select_summarize() called on custom field function."; - - default: - return "swq_select_summarize() - unexpected col_func"; - } - - return NULL; -} -/************************************************************************/ -/* sort comparison functions. */ -/************************************************************************/ - -static int FORCE_CDECL swq_compare_int( const void *item1, const void *item2 ) -{ - int v1, v2; - - v1 = atoi(*((const char **) item1)); - v2 = atoi(*((const char **) item2)); - - if( v1 < v2 ) - return -1; - else if( v1 == v2 ) - return 0; - else - return 1; -} - -static int FORCE_CDECL swq_compare_real( const void *item1, const void *item2 ) -{ - double v1, v2; - - v1 = atof(*((const char **) item1)); - v2 = atof(*((const char **) item2)); - - if( v1 < v2 ) - return -1; - else if( v1 == v2 ) - return 0; - else - return 1; -} - -static int FORCE_CDECL swq_compare_string( const void *item1, const void *item2 ) -{ - return strcmp( *((const char **) item1), *((const char **) item2) ); -} - -/************************************************************************/ -/* swq_select_finish_summarize() */ -/* */ -/* Call to complete summarize work. Does stuff like ordering */ -/* the distinct list for instance. */ -/************************************************************************/ - -const char *swq_select_finish_summarize( swq_select *select_info ) - -{ - int (FORCE_CDECL *compare_func)(const void *, const void*); - int count = 0; - char **distinct_list = NULL; - - if( select_info->query_mode != SWQM_DISTINCT_LIST - || select_info->order_specs == 0 ) - return NULL; - - if( select_info->order_specs > 1 ) - return "Can't ORDER BY a DISTINCT list by more than one key."; - - if( select_info->order_defs[0].field_index != - select_info->column_defs[0].field_index ) - return "Only selected DISTINCT field can be used for ORDER BY."; - - if( select_info->column_summary == NULL ) - return NULL; - - if( select_info->column_defs[0].field_type == SWQ_INTEGER ) - compare_func = swq_compare_int; - else if( select_info->column_defs[0].field_type == SWQ_FLOAT ) - compare_func = swq_compare_real; - else - compare_func = swq_compare_string; - - distinct_list = select_info->column_summary[0].distinct_list; - count = select_info->column_summary[0].count; - - qsort( distinct_list, count, sizeof(char *), compare_func ); - -/* -------------------------------------------------------------------- */ -/* Do we want the list ascending in stead of descending? */ -/* -------------------------------------------------------------------- */ - if( !select_info->order_defs[0].ascending_flag ) - { - char *saved; - int i; - - for( i = 0; i < count/2; i++ ) - { - saved = distinct_list[i]; - distinct_list[i] = distinct_list[count-i-1]; - distinct_list[count-i-1] = saved; - } - } - - return NULL; -} - -/************************************************************************/ -/* swq_select_free() */ -/************************************************************************/ - -void swq_select_free( swq_select *select_info ) - -{ - int i; - - if( select_info == NULL ) - return; - - if( select_info->where_expr != NULL ) - swq_expr_free(select_info->where_expr); - - if( select_info->raw_select != NULL ) - SWQ_FREE( select_info->raw_select ); - - if( select_info->whole_where_clause != NULL ) - SWQ_FREE( select_info->whole_where_clause ); - - for( i = 0; i < select_info->table_count; i++ ) - { - swq_table_def *table_def = select_info->table_defs + i; - - if( table_def->data_source != NULL ) - SWQ_FREE( table_def->data_source ); - SWQ_FREE( table_def->table_name ); - SWQ_FREE( table_def->table_alias ); - } - if( select_info->table_defs != NULL ) - SWQ_FREE( select_info->table_defs ); - - for( i = 0; i < select_info->result_columns; i++ ) - { - if( select_info->column_defs[i].field_name != NULL ) - SWQ_FREE( select_info->column_defs[i].field_name ); - if( select_info->column_defs[i].col_func_name != NULL ) - SWQ_FREE( select_info->column_defs[i].col_func_name ); - - if( select_info->column_summary != NULL - && select_info->column_summary[i].distinct_list != NULL ) - { - int j; - - for( j = 0; j < select_info->column_summary[i].count; j++ ) - SWQ_FREE( select_info->column_summary[i].distinct_list[j] ); - - SWQ_FREE( select_info->column_summary[i].distinct_list ); - } - } - - if( select_info->column_defs != NULL ) - SWQ_FREE( select_info->column_defs ); - - if( select_info->column_summary != NULL ) - SWQ_FREE( select_info->column_summary ); - - for( i = 0; i < select_info->order_specs; i++ ) - { - if( select_info->order_defs[i].field_name != NULL ) - SWQ_FREE( select_info->order_defs[i].field_name ); - } - - if( select_info->order_defs != NULL ) - SWQ_FREE( select_info->order_defs ); - - for( i = 0; i < select_info->join_count; i++ ) - { - SWQ_FREE( select_info->join_defs[i].primary_field_name ); - if( select_info->join_defs[i].secondary_field_name != NULL ) - SWQ_FREE( select_info->join_defs[i].secondary_field_name ); - } - if( select_info->join_defs != NULL ) - SWQ_FREE( select_info->join_defs ); - - SWQ_FREE( select_info ); -} - -/************************************************************************/ -/* swq_reform_command() */ -/* */ -/* Rebuild the command string from the components in the */ -/* swq_select structure. The where expression is taken from */ -/* the whole_where_clause instead of being reformed. The */ -/* results of forming the command are applied to the raw_select */ -/* field. */ -/************************************************************************/ - -#define CHECK_COMMAND( new_bytes ) grow_command( &command, &max_cmd_size, &cmd_size, new_bytes ); - -static void grow_command( char **p_command, int *max_cmd_size, int *cmd_size, - int new_bytes ); - -const char *swq_reform_command( swq_select *select_info ) - -{ - char *command; - int max_cmd_size = 10; - int cmd_size = 0; - int i; - - command = SWQ_MALLOC(max_cmd_size); - - strcpy( command, "SELECT " ); - -/* -------------------------------------------------------------------- */ -/* Handle the field list. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < select_info->result_columns; i++ ) - { - swq_col_def *def = select_info->column_defs + i; - const char *distinct = ""; - - if( def->distinct_flag ) - distinct = "DISTINCT "; - - if( i != 0 ) - { - CHECK_COMMAND(3); - strcat( command + cmd_size, ", " ); - } - - if( def->col_func_name != NULL ) - { - CHECK_COMMAND( strlen(def->col_func_name) - + strlen(def->field_name) + 15 ); - sprintf( command + cmd_size, "%s(%s%s)", - def->col_func_name, distinct, def->field_name ); - } - else - { - CHECK_COMMAND( strlen(def->field_name) + 15 ); - sprintf( command + cmd_size, "%s\"%s\"", - distinct, def->field_name ); - } - } - -/* -------------------------------------------------------------------- */ -/* Handle the FROM tablename. */ -/* -------------------------------------------------------------------- */ - if( select_info->table_count > 0 ) - { - CHECK_COMMAND( 10 + strlen(select_info->table_defs[0].table_name) ); - sprintf( command + cmd_size, " FROM \"%s\"", - select_info->table_defs[0].table_name ); - } - -/* -------------------------------------------------------------------- */ -/* Handle the JOIN clause(s). */ -/* -------------------------------------------------------------------- */ - /* TODO notdef */ - -/* -------------------------------------------------------------------- */ -/* Add WHERE statement if it exists. */ -/* -------------------------------------------------------------------- */ - if( select_info->whole_where_clause != NULL ) - { - CHECK_COMMAND( 12 + strlen(select_info->whole_where_clause) ); - sprintf( command + cmd_size, " WHERE %s", - select_info->whole_where_clause ); - } - -/* -------------------------------------------------------------------- */ -/* Add order by clause(s) if appropriate. */ -/* -------------------------------------------------------------------- */ - for( i = 0; i < select_info->order_specs; i++ ) - { - swq_order_def *def = select_info->order_defs + i; - - if( i == 0 ) - { - CHECK_COMMAND( 12 ); - sprintf( command + cmd_size, " ORDER BY " ); - } - else - { - CHECK_COMMAND( 3 ); - sprintf( command + cmd_size, ", " ); - } - - CHECK_COMMAND( strlen(def->field_name)+1 ); - sprintf( command + cmd_size, "\"%s\"", def->field_name ); - - CHECK_COMMAND( 6 ); - if( def->ascending_flag ) - strcat( command + cmd_size, " ASC" ); - else - strcat( command + cmd_size, " DESC" ); - } - -/* -------------------------------------------------------------------- */ -/* Assign back to the select info. */ -/* -------------------------------------------------------------------- */ - SWQ_FREE( select_info->raw_select ); - select_info->raw_select = command; - - return NULL; -} - -/* helper for the swq_reform_command() function. */ - -static void grow_command( char **p_command, int *max_cmd_size, int *cmd_size, - int new_bytes ) - -{ - char *new_command; - - *cmd_size += strlen(*p_command + *cmd_size); - - if( *cmd_size + new_bytes < *max_cmd_size - 1 ) - return; - - *max_cmd_size = 2 * *max_cmd_size; - if( *max_cmd_size < *cmd_size + new_bytes ) - *max_cmd_size = *cmd_size + new_bytes + 100; - - new_command = SWQ_MALLOC(*max_cmd_size); - - strcpy( new_command, *p_command ); - SWQ_FREE( *p_command ); - *p_command = new_command; -} diff --git a/ogr/swq.cpp b/ogr/swq.cpp new file mode 100644 index 0000000..67f3281 --- /dev/null +++ b/ogr/swq.cpp @@ -0,0 +1,856 @@ +/****************************************************************************** + * + * Component: OGDI Driver Support Library + * Purpose: Generic SQL WHERE Expression Implementation. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2001 Information Interoperability Institute (3i) + * Copyright (c) 2010-2013, Even Rouault + * + * Permission to use, copy, modify and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies, that + * both the copyright notice and this permission notice appear in + * supporting documentation, and that the name of 3i not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. 3i makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + ****************************************************************************/ + +#include + +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "swq.h" +#include "swq_parser.hpp" +#include "cpl_time.h" + +#define YYSTYPE swq_expr_node* + +/************************************************************************/ +/* swqlex() */ +/************************************************************************/ + +void swqerror( swq_parse_context *context, const char *msg ) +{ + CPLString osMsg; + osMsg.Printf( "SQL Expression Parsing Error: %s. Occured around :\n", msg ); + + int i; + int n = context->pszLastValid - context->pszInput; + + for( i = MAX(0,n-40); i < n + 40 && context->pszInput[i] != '\0'; i ++ ) + osMsg += context->pszInput[i]; + osMsg += "\n"; + for(i=0;ipszNext; + + *ppNode = NULL; + +/* -------------------------------------------------------------------- */ +/* Do we have a start symbol to return? */ +/* -------------------------------------------------------------------- */ + if( context->nStartToken != 0 ) + { + int nRet = context->nStartToken; + context->nStartToken = 0; + return nRet; + } + +/* -------------------------------------------------------------------- */ +/* Skip white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' + || *pszInput == 10 || *pszInput == 13 ) + pszInput++; + + context->pszLastValid = pszInput; + + if( *pszInput == '\0' ) + { + context->pszNext = pszInput; + return EOF; + } + +/* -------------------------------------------------------------------- */ +/* Handle string constants. */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '"' || *pszInput == '\'' ) + { + char *token; + int i_token; + char chQuote = *pszInput; + int bFoundEndQuote = FALSE; + + pszInput++; + + token = (char *) CPLMalloc(strlen(pszInput)+1); + i_token = 0; + + while( *pszInput != '\0' ) + { + if( chQuote == '"' && *pszInput == '\\' && pszInput[1] == '"' ) + pszInput++; + else if( chQuote == '\'' && *pszInput == '\\' && pszInput[1] == '\'' ) + pszInput++; + else if( chQuote == '\'' && *pszInput == '\'' && pszInput[1] == '\'' ) + pszInput++; + else if( *pszInput == chQuote ) + { + pszInput++; + bFoundEndQuote = TRUE; + break; + } + + token[i_token++] = *(pszInput++); + } + token[i_token] = '\0'; + + if( !bFoundEndQuote ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did not find end-of-string character"); + CPLFree( token ); + return 0; + } + + *ppNode = new swq_expr_node( token ); + CPLFree( token ); + + context->pszNext = pszInput; + + return SWQT_STRING; + } + +/* -------------------------------------------------------------------- */ +/* Handle numbers. */ +/* -------------------------------------------------------------------- */ + else if( *pszInput >= '0' && *pszInput <= '9' ) + { + CPLString osToken; + const char *pszNext = pszInput + 1; + + osToken += *pszInput; + + // collect non-decimal part of number + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + + // collect decimal places. + if( *pszNext == '.' ) + { + osToken += *(pszNext++); + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + } + + // collect exponent + if( *pszNext == 'e' || *pszNext == 'E' ) + { + osToken += *(pszNext++); + if( *pszNext == '-' || *pszNext == '+' ) + osToken += *(pszNext++); + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + } + + context->pszNext = pszNext; + + if( strstr(osToken,".") + || strstr(osToken,"e") + || strstr(osToken,"E") ) + { + *ppNode = new swq_expr_node( CPLAtof(osToken) ); + return SWQT_FLOAT_NUMBER; + } + else + { + *ppNode = new swq_expr_node( atoi(osToken) ); + return SWQT_INTEGER_NUMBER; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle alpha-numerics. */ +/* -------------------------------------------------------------------- */ + else if( isalnum( *pszInput ) ) + { + int nReturn = SWQT_IDENTIFIER; + CPLString osToken; + const char *pszNext = pszInput + 1; + + osToken += *pszInput; + + // collect text characters + while( isalnum( *pszNext ) || *pszNext == '_' + || ((unsigned char) *pszNext) > 127 ) + osToken += *(pszNext++); + + context->pszNext = pszNext; + + if( EQUAL(osToken,"IN") ) + nReturn = SWQT_IN; + else if( EQUAL(osToken,"LIKE") ) + nReturn = SWQT_LIKE; + else if( EQUAL(osToken,"ILIKE") ) + nReturn = SWQT_LIKE; + else if( EQUAL(osToken,"ESCAPE") ) + nReturn = SWQT_ESCAPE; + else if( EQUAL(osToken,"NULL") ) + nReturn = SWQT_NULL; + else if( EQUAL(osToken,"IS") ) + nReturn = SWQT_IS; + else if( EQUAL(osToken,"NOT") ) + nReturn = SWQT_NOT; + else if( EQUAL(osToken,"AND") ) + nReturn = SWQT_AND; + else if( EQUAL(osToken,"OR") ) + nReturn = SWQT_OR; + else if( EQUAL(osToken,"BETWEEN") ) + nReturn = SWQT_BETWEEN; + else if( EQUAL(osToken,"SELECT") ) + nReturn = SWQT_SELECT; + else if( EQUAL(osToken,"LEFT") ) + nReturn = SWQT_LEFT; + else if( EQUAL(osToken,"JOIN") ) + nReturn = SWQT_JOIN; + else if( EQUAL(osToken,"WHERE") ) + nReturn = SWQT_WHERE; + else if( EQUAL(osToken,"ON") ) + nReturn = SWQT_ON; + else if( EQUAL(osToken,"ORDER") ) + nReturn = SWQT_ORDER; + else if( EQUAL(osToken,"BY") ) + nReturn = SWQT_BY; + else if( EQUAL(osToken,"FROM") ) + nReturn = SWQT_FROM; + else if( EQUAL(osToken,"AS") ) + nReturn = SWQT_AS; + else if( EQUAL(osToken,"ASC") ) + nReturn = SWQT_ASC; + else if( EQUAL(osToken,"DESC") ) + nReturn = SWQT_DESC; + else if( EQUAL(osToken,"DISTINCT") ) + nReturn = SWQT_DISTINCT; + else if( EQUAL(osToken,"CAST") ) + nReturn = SWQT_CAST; + else if( EQUAL(osToken,"UNION") ) + nReturn = SWQT_UNION; + else if( EQUAL(osToken,"ALL") ) + nReturn = SWQT_ALL; + + /* Unhandled by OGR SQL */ + else if( EQUAL(osToken,"LIMIT") || + EQUAL(osToken,"OUTER") || + EQUAL(osToken,"INNER") ) + nReturn = SWQT_RESERVED_KEYWORD; + + else + { + *ppNode = new swq_expr_node( osToken ); + nReturn = SWQT_IDENTIFIER; + } + + return nReturn; + } + +/* -------------------------------------------------------------------- */ +/* Handle special tokens. */ +/* -------------------------------------------------------------------- */ + else + { + context->pszNext = pszInput+1; + return *pszInput; + } +} + +/************************************************************************/ +/* swq_select_summarize() */ +/************************************************************************/ + +const char * +swq_select_summarize( swq_select *select_info, + int dest_column, const char *value ) + +{ + swq_col_def *def = select_info->column_defs + dest_column; + swq_summary *summary; + +/* -------------------------------------------------------------------- */ +/* Do various checking. */ +/* -------------------------------------------------------------------- */ + if( select_info->query_mode == SWQM_RECORDSET ) + return "swq_select_summarize() called on non-summary query."; + + if( dest_column < 0 || dest_column >= select_info->result_columns ) + return "dest_column out of range in swq_select_summarize()."; + + if( def->col_func == SWQCF_NONE && !def->distinct_flag ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the summary information if this is the first row */ +/* being processed. */ +/* -------------------------------------------------------------------- */ + if( select_info->column_summary == NULL && value != NULL ) + { + int i; + + select_info->column_summary = (swq_summary *) + CPLMalloc(sizeof(swq_summary) * select_info->result_columns); + memset( select_info->column_summary, 0, + sizeof(swq_summary) * select_info->result_columns ); + + for( i = 0; i < select_info->result_columns; i++ ) + { + select_info->column_summary[i].min = 1e20; + select_info->column_summary[i].max = -1e20; + strcpy(select_info->column_summary[i].szMin, "9999/99/99 99:99:99"); + strcpy(select_info->column_summary[i].szMax, "0000/00/00 00:00:00"); + } + } + + if( select_info->column_summary == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If distinct processing is on, process that now. */ +/* -------------------------------------------------------------------- */ + summary = select_info->column_summary + dest_column; + + if( def->distinct_flag ) + { + int i; + + /* This should be implemented with a much more complicated + data structure to achieve any sort of efficiency. */ + for( i = 0; i < summary->count; i++ ) + { + if( value == NULL ) + { + if (summary->distinct_list[i] == NULL) + break; + } + else if( summary->distinct_list[i] != NULL && + strcmp(value,summary->distinct_list[i]) == 0 ) + break; + } + + if( i == summary->count ) + { + char **old_list = summary->distinct_list; + + summary->distinct_list = (char **) + CPLMalloc(sizeof(char *) * (summary->count+1)); + memcpy( summary->distinct_list, old_list, + sizeof(char *) * summary->count ); + summary->distinct_list[(summary->count)++] = + (value != NULL) ? CPLStrdup( value ) : NULL; + + CPLFree(old_list); + } + } + +/* -------------------------------------------------------------------- */ +/* Process various options. */ +/* -------------------------------------------------------------------- */ + + switch( def->col_func ) + { + case SWQCF_MIN: + if( value != NULL && value[0] != '\0' ) + { + if(def->field_type == SWQ_DATE || + def->field_type == SWQ_TIME || + def->field_type == SWQ_TIMESTAMP) + { + if( strcmp( value, summary->szMin ) < 0 ) + { + strncpy( summary->szMin, value, sizeof(summary->szMin) ); + summary->szMin[sizeof(summary->szMin) - 1] = '\0'; + } + } + else + { + double df_val = CPLAtof(value); + if( df_val < summary->min ) + summary->min = df_val; + } + } + break; + case SWQCF_MAX: + if( value != NULL && value[0] != '\0' ) + { + if(def->field_type == SWQ_DATE || + def->field_type == SWQ_TIME || + def->field_type == SWQ_TIMESTAMP) + { + if( strcmp( value, summary->szMax ) > 0 ) + { + strncpy( summary->szMax, value, sizeof(summary->szMax) ); + summary->szMax[sizeof(summary->szMax) - 1] = '\0'; + } + } + else + { + double df_val = CPLAtof(value); + if( df_val > summary->max ) + summary->max = df_val; + } + } + break; + case SWQCF_AVG: + case SWQCF_SUM: + if( value != NULL && value[0] != '\0' ) + { + if(def->field_type == SWQ_DATE || + def->field_type == SWQ_TIME || + def->field_type == SWQ_TIMESTAMP) + { + int nYear, nMonth, nDay, nHour, nMin, nSec; + if( sscanf(value, "%04d/%02d/%02d %02d:%02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMin, &nSec) == 6 ) + { + struct tm brokendowntime; + brokendowntime.tm_year = nYear - 1900; + brokendowntime.tm_mon = nMonth - 1; + brokendowntime.tm_mday = nDay; + brokendowntime.tm_hour = nHour; + brokendowntime.tm_min = nMin; + brokendowntime.tm_sec = nSec; + summary->count++; + summary->sum += CPLYMDHMSToUnixTime(&brokendowntime); + } + } + else + { + summary->count++; + summary->sum += CPLAtof(value); + } + } + break; + + case SWQCF_COUNT: + if( value != NULL && !def->distinct_flag ) + summary->count++; + break; + + case SWQCF_NONE: + break; + + case SWQCF_CUSTOM: + return "swq_select_summarize() called on custom field function."; + + default: + return "swq_select_summarize() - unexpected col_func"; + } + + return NULL; +} +/************************************************************************/ +/* sort comparison functions. */ +/************************************************************************/ + +static int FORCE_CDECL swq_compare_int( const void *item1, const void *item2 ) +{ + int v1, v2; + + const char* pszStr1 = *((const char **) item1); + const char* pszStr2 = *((const char **) item2); + if (pszStr1 == NULL) + return (pszStr2 == NULL) ? 0 : -1; + else if (pszStr2 == NULL) + return 1; + + v1 = atoi(pszStr1); + v2 = atoi(pszStr2); + + if( v1 < v2 ) + return -1; + else if( v1 == v2 ) + return 0; + else + return 1; +} + +static int FORCE_CDECL swq_compare_real( const void *item1, const void *item2 ) +{ + double v1, v2; + + const char* pszStr1 = *((const char **) item1); + const char* pszStr2 = *((const char **) item2); + if (pszStr1 == NULL) + return (pszStr2 == NULL) ? 0 : -1; + else if (pszStr2 == NULL) + return 1; + + v1 = CPLAtof(pszStr1); + v2 = CPLAtof(pszStr2); + + if( v1 < v2 ) + return -1; + else if( v1 == v2 ) + return 0; + else + return 1; +} + +static int FORCE_CDECL swq_compare_string( const void *item1, const void *item2 ) +{ + const char* pszStr1 = *((const char **) item1); + const char* pszStr2 = *((const char **) item2); + if (pszStr1 == NULL) + return (pszStr2 == NULL) ? 0 : -1; + else if (pszStr2 == NULL) + return 1; + + return strcmp( pszStr1, pszStr2 ); +} + +/************************************************************************/ +/* swq_select_finish_summarize() */ +/* */ +/* Call to complete summarize work. Does stuff like ordering */ +/* the distinct list for instance. */ +/************************************************************************/ + +const char *swq_select_finish_summarize( swq_select *select_info ) + +{ + int (FORCE_CDECL *compare_func)(const void *, const void*); + int count = 0; + char **distinct_list = NULL; + + if( select_info->query_mode != SWQM_DISTINCT_LIST + || select_info->order_specs == 0 ) + return NULL; + + if( select_info->order_specs > 1 ) + return "Can't ORDER BY a DISTINCT list by more than one key."; + + if( select_info->order_defs[0].field_index != + select_info->column_defs[0].field_index ) + return "Only selected DISTINCT field can be used for ORDER BY."; + + if( select_info->column_summary == NULL ) + return NULL; + + if( select_info->column_defs[0].field_type == SWQ_INTEGER ) + compare_func = swq_compare_int; + else if( select_info->column_defs[0].field_type == SWQ_FLOAT ) + compare_func = swq_compare_real; + else + compare_func = swq_compare_string; + + distinct_list = select_info->column_summary[0].distinct_list; + count = select_info->column_summary[0].count; + + qsort( distinct_list, count, sizeof(char *), compare_func ); + +/* -------------------------------------------------------------------- */ +/* Do we want the list ascending in stead of descending? */ +/* -------------------------------------------------------------------- */ + if( !select_info->order_defs[0].ascending_flag ) + { + char *saved; + int i; + + for( i = 0; i < count/2; i++ ) + { + saved = distinct_list[i]; + distinct_list[i] = distinct_list[count-i-1]; + distinct_list[count-i-1] = saved; + } + } + + return NULL; +} + +/************************************************************************/ +/* swq_select_free() */ +/************************************************************************/ + +void swq_select_free( swq_select *select_info ) + +{ + delete select_info; +} + +/************************************************************************/ +/* swq_identify_field() */ +/************************************************************************/ +static +int swq_identify_field_internal( const char *field_token, const char* table_name, + swq_field_list *field_list, + swq_field_type *this_type, int *table_id, int tables_enabled ); + +int swq_identify_field( const char *token, swq_field_list *field_list, + swq_field_type *this_type, int *table_id ) + +{ + CPLString osTableName; + const char *field_token = token; + int tables_enabled; + + if( field_list->table_count > 0 && field_list->table_ids != NULL ) + tables_enabled = TRUE; + else + tables_enabled = FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse out table name if present, and table support enabled. */ +/* -------------------------------------------------------------------- */ + if( tables_enabled && strchr(token, '.') != NULL ) + { + int dot_offset = (int)(strchr(token,'.') - token); + + osTableName = token; + osTableName.resize(dot_offset); + field_token = token + dot_offset + 1; + +#ifdef notdef + /* We try to detect if a.b is the a.b field */ + /* of the main table, or the b field of the a table */ + /* If both exists, report an error. */ + /* This works, but I'm not sure this is a good idea to */ + /* enable that. It is a sign that our SQL grammar is somewhat */ + /* ambiguous */ + + swq_field_type eTypeWithTablesEnabled; + int nTableIdWithTablesEnabled; + int nRetWithTablesEnabled = swq_identify_field_internal( + field_token, osTableName.c_str(), field_list, + &eTypeWithTablesEnabled, &nTableIdWithTablesEnabled, TRUE); + + swq_field_type eTypeWithTablesDisabled; + int nTableIdWithTablesDisabled; + int nRetWithTablesDisabled = swq_identify_field_internal( + token, "", field_list, + &eTypeWithTablesDisabled, &nTableIdWithTablesDisabled, FALSE); + + if( nRetWithTablesEnabled >= 0 && nRetWithTablesDisabled >= 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Ambiguous situation. Both %s exists as a field in " + "main table and %s exists as a field in %s table", + token, osTableName.c_str(), field_token); + return -1; + } + else if( nRetWithTablesEnabled >= 0 ) + { + if( this_type != NULL ) *this_type = eTypeWithTablesEnabled; + if( table_id != NULL ) *table_id = nTableIdWithTablesEnabled; + return nRetWithTablesEnabled; + } + else if( nRetWithTablesDisabled >= 0 ) + { + if( this_type != NULL ) *this_type = eTypeWithTablesDisabled; + if( table_id != NULL ) *table_id = nTableIdWithTablesDisabled; + return nRetWithTablesDisabled; + } + else + { + return -1; + } +#endif + } + + return swq_identify_field_internal(field_token, osTableName.c_str(), field_list, + this_type, table_id, tables_enabled); +} + + +int swq_identify_field_internal( const char *field_token, const char* table_name, + swq_field_list *field_list, + swq_field_type *this_type, int *table_id, int tables_enabled ) + +{ + int i; +/* -------------------------------------------------------------------- */ +/* Search for matching field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < field_list->count; i++ ) + { + int t_id = 0; + + if( !EQUAL( field_list->names[i], field_token ) ) + continue; + + /* Do the table specifications match? */ + if( tables_enabled ) + { + t_id = field_list->table_ids[i]; + if( table_name[0] != '\0' + && !EQUAL(table_name,field_list->table_defs[t_id].table_alias)) + continue; + +// if( t_id != 0 && table_name[0] == '\0' ) +// continue; + } + + /* We have a match, return various information */ + if( this_type != NULL ) + { + if( field_list->types != NULL ) + *this_type = field_list->types[i]; + else + *this_type = SWQ_OTHER; + } + + if( table_id != NULL ) + *table_id = t_id; + + if( field_list->ids == NULL ) + return i; + else + return field_list->ids[i]; + } + +/* -------------------------------------------------------------------- */ +/* No match, return failure. */ +/* -------------------------------------------------------------------- */ + if( this_type != NULL ) + *this_type = SWQ_OTHER; + + if( table_id != NULL ) + *table_id = 0; + + return -1; +} + +/************************************************************************/ +/* swq_expr_compile() */ +/************************************************************************/ + +CPLErr swq_expr_compile( const char *where_clause, + int field_count, + char **field_names, + swq_field_type *field_types, + swq_expr_node **expr_out ) + +{ + swq_field_list field_list; + + field_list.count = field_count; + field_list.names = field_names; + field_list.types = field_types; + field_list.table_ids = NULL; + field_list.ids = NULL; + + field_list.table_count = 0; + field_list.table_defs = NULL; + + return swq_expr_compile2( where_clause, &field_list, expr_out ); +} + + +/************************************************************************/ +/* swq_expr_compile2() */ +/************************************************************************/ + +CPLErr swq_expr_compile2( const char *where_clause, + swq_field_list *field_list, + swq_expr_node **expr_out ) + +{ + + swq_parse_context context; + + context.pszInput = where_clause; + context.pszNext = where_clause; + context.pszLastValid = where_clause; + context.nStartToken = SWQT_LOGICAL_START; + + if( swqparse( &context ) == 0 + && context.poRoot->Check( field_list, FALSE ) != SWQ_ERROR ) + { + *expr_out = context.poRoot; + + return CE_None; + } + else + { + delete context.poRoot; + *expr_out = NULL; + return CE_Failure; + } +} + +/************************************************************************/ +/* swq_is_reserved_keyword() */ +/************************************************************************/ + +static const char* apszSQLReservedKeywords[] = { + "OR", + "AND", + "NOT", + "LIKE", + "IS", + "NULL", + "IN", + "BETWEEN", + "CAST", + "DISTINCT", + "ESCAPE", + "SELECT", + "LEFT", + "JOIN", + "WHERE", + "ON", + "ORDER", + "BY", + "FROM", + "AS", + "ASC", + "DESC", + "UNION", + "ALL" +}; + +int swq_is_reserved_keyword(const char* pszStr) +{ + for(int i = 0; i < (int)(sizeof(apszSQLReservedKeywords)/sizeof(char*)); i++) + { + if (EQUAL(pszStr, apszSQLReservedKeywords[i])) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* SWQFieldTypeToString() */ +/************************************************************************/ + +const char* SWQFieldTypeToString( swq_field_type field_type ) +{ + switch(field_type) + { + case SWQ_INTEGER: return "integer"; + case SWQ_FLOAT: return "float"; + case SWQ_STRING: return "string"; + case SWQ_BOOLEAN: return "boolean"; + case SWQ_DATE: return "date"; + case SWQ_TIME: return "time"; + case SWQ_TIMESTAMP: return "timestamp"; + case SWQ_GEOMETRY: return "geometry"; + case SWQ_NULL: return "null"; + default: return "unknown"; + } +} diff --git a/ogr/swq.h b/ogr/swq.h index ee014ae..d9a4f58 100644 --- a/ogr/swq.h +++ b/ogr/swq.h @@ -6,6 +6,7 @@ * ****************************************************************************** * Copyright (C) 2001 Information Interoperability Institute (3i) + * Copyright (c) 2010-2013, Even Rouault * Permission to use, copy, modify and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies, that @@ -20,6 +21,16 @@ #ifndef _SWQ_H_INCLUDED_ #define _SWQ_H_INCLUDED_ +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_core.h" + +#if defined(_WIN32) && !defined(_WIN32_WCE) +# define strcasecmp stricmp +#elif defined(_WIN32_WCE) +# define strcasecmp _stricmp +#endif + typedef enum { SWQ_OR, SWQ_AND, @@ -31,11 +42,24 @@ typedef enum { SWQ_LT, SWQ_GT, SWQ_LIKE, - SWQ_NOTLIKE, SWQ_ISNULL, - SWQ_ISNOTNULL, SWQ_IN, - SWQ_NOTIN, + SWQ_BETWEEN, + SWQ_ADD, + SWQ_SUBTRACT, + SWQ_MULTIPLY, + SWQ_DIVIDE, + SWQ_MODULUS, + SWQ_CONCAT, + SWQ_SUBSTR, + SWQ_HSTORE_GET_VALUE, + SWQ_AVG, + SWQ_MIN, + SWQ_MAX, + SWQ_COUNT, + SWQ_SUM, + SWQ_CAST, + SWQ_FUNC_DEFINED, SWQ_UNKNOWN } swq_op; @@ -43,32 +67,88 @@ typedef enum { SWQ_INTEGER, SWQ_FLOAT, SWQ_STRING, - SWQ_BOOLEAN, - SWQ_DATE, - SWQ_TIME, - SWQ_TIMESTAMP, - SWQ_OTHER + SWQ_BOOLEAN, // integer + SWQ_DATE, // string + SWQ_TIME, // string + SWQ_TIMESTAMP,// string + SWQ_GEOMETRY, + SWQ_NULL, + SWQ_OTHER, + SWQ_ERROR } swq_field_type; -typedef struct { - swq_op operation; +typedef enum { + SNT_CONSTANT, + SNT_COLUMN, + SNT_OPERATION +} swq_node_type; + + +class swq_field_list; +class swq_expr_node; +class swq_select; +class OGRGeometry; + +typedef swq_expr_node *(*swq_field_fetcher)( swq_expr_node *op, + void *record_handle ); +typedef swq_expr_node *(*swq_op_evaluator)(swq_expr_node *op, + swq_expr_node **sub_field_values ); +typedef swq_field_type (*swq_op_checker)( swq_expr_node *op ); + +class swq_expr_node { + static void Quote( CPLString &, char chQuote = '\'' ); +public: + swq_expr_node(); + + swq_expr_node( const char * ); + swq_expr_node( int ); + swq_expr_node( double ); + swq_expr_node( OGRGeometry* ); + swq_expr_node( swq_op ); + + ~swq_expr_node(); + + void Initialize(); + char *Unparse( swq_field_list *, char chColumnQuote ); + void Dump( FILE *fp, int depth ); + swq_field_type Check( swq_field_list *, int bAllowFieldsInSecondaryTables ); + swq_expr_node* Evaluate( swq_field_fetcher pfnFetcher, + void *record ); + + swq_node_type eNodeType; + swq_field_type field_type; - /* only for logical expression on subexpression */ - struct swq_node_s *first_sub_expr; - struct swq_node_s *second_sub_expr; + /* only for SNT_OPERATION */ + void PushSubExpression( swq_expr_node * ); + void ReverseSubExpressions(); + int nOperation; + int nSubExprCount; + swq_expr_node **papoSubExpr; - /* only for binary field operations */ + /* only for SNT_COLUMN */ int field_index; int table_index; - swq_field_type field_type; + + /* only for SNT_CONSTANT */ + int is_null; char *string_value; int int_value; double float_value; -} swq_field_op; + OGRGeometry *geometry_value; +}; -typedef swq_field_op swq_expr; - -typedef int (*swq_op_evaluator)(swq_field_op *op, void *record_handle); +typedef struct { + const char* pszName; + swq_op eOperation; + swq_op_evaluator pfnEvaluator; + swq_op_checker pfnChecker; +} swq_operation; + +class swq_op_registrar { +public: + static const swq_operation *GetOperator( const char * ); + static const swq_operation *GetOperator( swq_op eOperation ); +}; typedef struct { char *data_source; @@ -76,7 +156,8 @@ typedef struct { char *table_alias; } swq_table_def; -typedef struct { +class swq_field_list { +public: int count; char **names; swq_field_type *types; @@ -85,33 +166,53 @@ typedef struct { int table_count; swq_table_def *table_defs; -} swq_field_list; +}; + +class swq_parse_context { +public: + swq_parse_context() : nStartToken(0), poRoot(NULL), poCurSelect(NULL) {} + + int nStartToken; + const char *pszInput; + const char *pszNext; + const char *pszLastValid; + + swq_expr_node *poRoot; + + swq_select *poCurSelect; +}; /* Compile an SQL WHERE clause into an internal form. The field_list is ** the list of fields in the target 'table', used to render where into ** field numbers instead of names. */ -const char *swq_expr_compile( const char *where_clause, - int field_count, - char **field_list, - swq_field_type *field_types, - swq_expr **expr ); +int swqparse( swq_parse_context *context ); +int swqlex( swq_expr_node **ppNode, swq_parse_context *context ); +void swqerror( swq_parse_context *context, const char *msg ); -const char *swq_expr_compile2( const char *where_clause, - swq_field_list *field_list, - swq_expr **expr ); +int swq_identify_field( const char *token, swq_field_list *field_list, + swq_field_type *this_type, int *table_id ); -/* -** Evaluate an expression for a particular record using an application -** provided field operation evaluator, and abstract record handle. -*/ -int swq_expr_evaluate( swq_expr *expr, swq_op_evaluator fn_evaluator, - void *record_handle ); +CPLErr swq_expr_compile( const char *where_clause, + int field_count, + char **field_list, + swq_field_type *field_types, + swq_expr_node **expr_root ); -void swq_expr_free( swq_expr * ); +CPLErr swq_expr_compile2( const char *where_clause, + swq_field_list *field_list, + swq_expr_node **expr_root ); +/* +** Evaluation related. +*/ int swq_test_like( const char *input, const char *pattern ); +swq_expr_node *SWQGeneralEvaluator( swq_expr_node *, swq_expr_node **); +swq_field_type SWQGeneralChecker( swq_expr_node *node ); +swq_expr_node *SWQCastEvaluator( swq_expr_node *, swq_expr_node **); +swq_field_type SWQCastChecker( swq_expr_node *node ); +const char* SWQFieldTypeToString( swq_field_type field_type ); /****************************************************************************/ @@ -122,20 +223,19 @@ int swq_test_like( const char *input, const char *pattern ); #define SWQM_DISTINCT_LIST 3 typedef enum { - SWQCF_NONE, - SWQCF_AVG, - SWQCF_MIN, - SWQCF_MAX, - SWQCF_COUNT, - SWQCF_SUM, + SWQCF_NONE = 0, + SWQCF_AVG = SWQ_AVG, + SWQCF_MIN = SWQ_MIN, + SWQCF_MAX = SWQ_MAX, + SWQCF_COUNT = SWQ_COUNT, + SWQCF_SUM = SWQ_SUM, SWQCF_CUSTOM } swq_col_func; typedef struct { swq_col_func col_func; - char *col_func_name; char *field_name; - char *field_alias; + char *field_alias; int table_index; int field_index; swq_field_type field_type; @@ -143,15 +243,20 @@ typedef struct { int field_length; int field_precision; int distinct_flag; + OGRwkbGeometryType eGeomType; + int nSRID; + swq_expr_node *expr; } swq_col_def; typedef struct { int count; - char **distinct_list; + char **distinct_list; /* items of the list can be NULL */ double sum; double min; double max; + char szMin[32]; + char szMax[32]; } swq_summary; typedef struct { @@ -173,45 +278,62 @@ typedef struct { int secondary_field; } swq_join_def; -typedef struct { +class swq_select +{ +public: + swq_select(); + ~swq_select(); + int query_mode; char *raw_select; + int PushField( swq_expr_node *poExpr, const char *pszAlias=NULL, + int distinct_flag = FALSE ); int result_columns; swq_col_def *column_defs; swq_summary *column_summary; + int PushTableDef( const char *pszDataSource, + const char *pszTableName, + const char *pszAlias ); int table_count; swq_table_def *table_defs; + void PushJoin( int iSecondaryTable, + const char *pszPrimaryField, + const char *pszSecondaryField ); int join_count; swq_join_def *join_defs; - char *whole_where_clause; - swq_expr *where_expr; + swq_expr_node *where_expr; + void PushOrderBy( const char *pszFieldName, int bAscending ); int order_specs; - swq_order_def *order_defs; -} swq_select; - -const char *swq_select_preparse( const char *select_statement, - swq_select **select_info ); -const char *swq_select_expand_wildcard( swq_select *select_info, - swq_field_list *field_list ); -const char *swq_select_parse( swq_select *select_info, - swq_field_list *field_list, - int parse_flags ); -void swq_select_free( swq_select *select_info ); - -const char *swq_reform_command( swq_select *select_info ); -void swq_free( void * ); -void *swq_malloc( int ); + swq_order_def *order_defs; + + swq_select *poOtherSelect; + void PushUnionAll( swq_select* poOtherSelectIn ); + + CPLErr preparse( const char *select_statement ); + void postpreparse(); + CPLErr expand_wildcard( swq_field_list *field_list ); + CPLErr parse( swq_field_list *field_list, int parse_flags ); + + void Dump( FILE * ); +}; + +CPLErr swq_select_parse( swq_select *select_info, + swq_field_list *field_list, + int parse_flags ); const char *swq_select_finish_summarize( swq_select *select_info ); const char *swq_select_summarize( swq_select *select_info, int dest_column, const char *value ); +int swq_is_reserved_keyword(const char* pszStr); + +char* OGRHStoreGetValue(const char* pszHStore, const char* pszSearchedKey); #endif /* def _SWQ_H_INCLUDED_ */ diff --git a/ogr/swq_expr_node.cpp b/ogr/swq_expr_node.cpp new file mode 100644 index 0000000..f38e2ef --- /dev/null +++ b/ogr/swq_expr_node.cpp @@ -0,0 +1,710 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: Implementation of the swq_expr_node class used to represent a + * node in an SQL expression. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "swq.h" +#include "ogr_geometry.h" +#include + +/************************************************************************/ +/* swq_expr_node() */ +/************************************************************************/ + +swq_expr_node::swq_expr_node() + +{ + Initialize(); +} + +/************************************************************************/ +/* swq_expr_node(int) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( int nValueIn ) + +{ + Initialize(); + + int_value = nValueIn; +} + +/************************************************************************/ +/* swq_expr_node(double) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( double dfValueIn ) + +{ + Initialize(); + + field_type = SWQ_FLOAT; + float_value = dfValueIn; +} + +/************************************************************************/ +/* swq_expr_node(const char*) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( const char *pszValueIn ) + +{ + Initialize(); + + field_type = SWQ_STRING; + string_value = CPLStrdup( pszValueIn ? pszValueIn : "" ); + is_null = pszValueIn == NULL; +} + +/************************************************************************/ +/* swq_expr_node(OGRGeometry *) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( OGRGeometry *poGeomIn ) + +{ + Initialize(); + + field_type = SWQ_GEOMETRY; + geometry_value = poGeomIn ? poGeomIn->clone() : NULL; + is_null = poGeomIn == NULL; +} + +/************************************************************************/ +/* swq_expr_node(swq_op) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( swq_op eOp ) + +{ + Initialize(); + + eNodeType = SNT_OPERATION; + + nOperation = (int) eOp; + nSubExprCount = 0; + papoSubExpr = NULL; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void swq_expr_node::Initialize() + +{ + eNodeType = SNT_CONSTANT; + field_type = SWQ_INTEGER; + int_value = 0; + + is_null = FALSE; + string_value = NULL; + geometry_value = NULL; + papoSubExpr = NULL; + nSubExprCount = 0; +} + +/************************************************************************/ +/* ~swq_expr_node() */ +/************************************************************************/ + +swq_expr_node::~swq_expr_node() + +{ + CPLFree( string_value ); + + int i; + for( i = 0; i < nSubExprCount; i++ ) + delete papoSubExpr[i]; + CPLFree( papoSubExpr ); + delete geometry_value; +} + +/************************************************************************/ +/* PushSubExpression() */ +/************************************************************************/ + +void swq_expr_node::PushSubExpression( swq_expr_node *child ) + +{ + nSubExprCount++; + papoSubExpr = (swq_expr_node **) + CPLRealloc( papoSubExpr, sizeof(void*) * nSubExprCount ); + + papoSubExpr[nSubExprCount-1] = child; +} + +/************************************************************************/ +/* ReverseSubExpressions() */ +/************************************************************************/ + +void swq_expr_node::ReverseSubExpressions() + +{ + int i; + for( i = 0; i < nSubExprCount / 2; i++ ) + { + swq_expr_node *temp; + + temp = papoSubExpr[i]; + papoSubExpr[i] = papoSubExpr[nSubExprCount - i - 1]; + papoSubExpr[nSubExprCount - i - 1] = temp; + } +} + +/************************************************************************/ +/* Check() */ +/* */ +/* Check argument types, etc. */ +/************************************************************************/ + +swq_field_type swq_expr_node::Check( swq_field_list *poFieldList, + int bAllowFieldsInSecondaryTables ) + +{ +/* -------------------------------------------------------------------- */ +/* If something is a string constant, we must check if it is */ +/* actually a reference to a field in which case we will */ +/* convert it into a column type. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT && field_type == SWQ_STRING ) + { + int wrk_field_index, wrk_table_index; + swq_field_type wrk_field_type; + + if( is_null ) + wrk_field_index = -1; + else + wrk_field_index = + swq_identify_field( string_value, poFieldList, + &wrk_field_type, &wrk_table_index ); + + if( wrk_field_index >= 0 ) + { + eNodeType = SNT_COLUMN; + field_index = -1; + table_index = -1; + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we take constants literally. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT ) + return field_type; + +/* -------------------------------------------------------------------- */ +/* If this is intended to be a field definition, but has not */ +/* yet been looked up, we do so now. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_COLUMN && field_index == -1 ) + { + field_index = + swq_identify_field( string_value, poFieldList, + &field_type, &table_index ); + + if( field_index < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "'%s' not recognised as an available field.", + string_value ); + + return SWQ_ERROR; + } + + if( !bAllowFieldsInSecondaryTables && table_index != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use field '%s' of a secondary table in this context", + string_value ); + return SWQ_ERROR; + } + } + + if( eNodeType == SNT_COLUMN ) + return field_type; + +/* -------------------------------------------------------------------- */ +/* We are dealing with an operation - fetch the definition. */ +/* -------------------------------------------------------------------- */ + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)nOperation); + + if( poOp == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Check(): Unable to find definition for operator %d.", + nOperation ); + return SWQ_ERROR; + } + +/* -------------------------------------------------------------------- */ +/* Check subexpressions first. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nSubExprCount; i++ ) + { + if( papoSubExpr[i]->Check(poFieldList, bAllowFieldsInSecondaryTables) == SWQ_ERROR ) + return SWQ_ERROR; + } + +/* -------------------------------------------------------------------- */ +/* Check this node. */ +/* -------------------------------------------------------------------- */ + field_type = poOp->pfnChecker( this ); + + return field_type; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void swq_expr_node::Dump( FILE * fp, int depth ) + +{ + char spaces[60]; + int i; + + for( i = 0; i < depth*2 && i < (int) sizeof(spaces) - 1; i++ ) + spaces[i] = ' '; + spaces[i] = '\0'; + + if( eNodeType == SNT_COLUMN ) + { + fprintf( fp, "%s Field %d\n", spaces, field_index ); + return; + } + + if( eNodeType == SNT_CONSTANT ) + { + if( field_type == SWQ_INTEGER || field_type == SWQ_BOOLEAN ) + fprintf( fp, "%s %d\n", spaces, int_value ); + else if( field_type == SWQ_FLOAT ) + fprintf( fp, "%s %.15g\n", spaces, float_value ); + else if( field_type == SWQ_GEOMETRY ) + { + if( geometry_value == NULL ) + fprintf( fp, "%s (null)\n", spaces ); + else + { + char* pszWKT = NULL; + geometry_value->exportToWkt(&pszWKT); + fprintf( fp, "%s %s\n", spaces, pszWKT ); + CPLFree(pszWKT); + } + } + else + fprintf( fp, "%s %s\n", spaces, string_value ); + return; + } + + CPLAssert( eNodeType == SNT_OPERATION ); + + const swq_operation *op_def = + swq_op_registrar::GetOperator( (swq_op) nOperation ); + + fprintf( fp, "%s%s\n", spaces, op_def->pszName ); + + for( i = 0; i < nSubExprCount; i++ ) + papoSubExpr[i]->Dump( fp, depth+1 ); +} + +/************************************************************************/ +/* Quote() */ +/* */ +/* Add quoting necessary to unparse a string. */ +/************************************************************************/ + +void swq_expr_node::Quote( CPLString &osTarget, char chQuote ) + +{ + CPLString osNew; + int i; + + osNew += chQuote; + + for( i = 0; i < (int) osTarget.size(); i++ ) + { + if( osTarget[i] == chQuote ) + { + osNew += chQuote; + osNew += chQuote; + } + else + osNew += osTarget[i]; + } + osNew += chQuote; + + osTarget = osNew; +} + +/************************************************************************/ +/* Unparse() */ +/************************************************************************/ + +char *swq_expr_node::Unparse( swq_field_list *field_list, char chColumnQuote ) + +{ + CPLString osExpr; + +/* -------------------------------------------------------------------- */ +/* Handle constants. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT ) + { + if (is_null) + return CPLStrdup("NULL"); + + if( field_type == SWQ_INTEGER || field_type == SWQ_BOOLEAN ) + osExpr.Printf( "%d", int_value ); + else if( field_type == SWQ_FLOAT ) + { + osExpr.Printf( "%.15g", float_value ); + /* Make sure this is interpreted as a floating point value */ + /* and not as an integer later */ + if (strchr(osExpr, '.') == NULL && strchr(osExpr, 'e') == NULL && + strchr(osExpr, 'E') == NULL) + osExpr += '.'; + } + else + { + osExpr = string_value; + Quote( osExpr ); + } + + return CPLStrdup(osExpr); + } + +/* -------------------------------------------------------------------- */ +/* Handle columns. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_COLUMN ) + { + if( field_list == NULL ) + { + osExpr.Printf( "%s", string_value ); + } + else if( field_index != -1 + && table_index < field_list->table_count + && table_index > 0 ) + { + for(int i = 0; i < field_list->count; i++ ) + { + if( field_list->table_ids[i] == table_index && + field_list->ids[i] == field_index ) + { + osExpr.Printf( "%s.%s", + field_list->table_defs[table_index].table_name, + field_list->names[i] ); + break; + } + } + } + else if( field_index != -1 ) + { + for(int i = 0; i < field_list->count; i++ ) + { + if( field_list->table_ids[i] == table_index && + field_list->ids[i] == field_index ) + { + osExpr.Printf( "%s", field_list->names[i] ); + break; + } + } + } + + if( osExpr.size() == 0 ) + { + return CPLStrdup(CPLSPrintf("%c%c", chColumnQuote, chColumnQuote)); + } + + for( int i = 0; i < (int) osExpr.size(); i++ ) + { + char ch = osExpr[i]; + if (!(isalnum((int)ch) || ch == '_')) + { + Quote( osExpr, chColumnQuote ); + return CPLStrdup(osExpr.c_str()); + } + } + + if (swq_is_reserved_keyword(osExpr)) + { + Quote( osExpr, chColumnQuote ); + return CPLStrdup(osExpr.c_str()); + } + + /* The string is just alphanum and not a reserved SQL keyword, no needs to quote and escape */ + return CPLStrdup(osExpr.c_str()); + } + +/* -------------------------------------------------------------------- */ +/* Operation - start by unparsing all the subexpressions. */ +/* -------------------------------------------------------------------- */ + std::vector apszSubExpr; + int i; + + for( i = 0; i < nSubExprCount; i++ ) + apszSubExpr.push_back( papoSubExpr[i]->Unparse(field_list, chColumnQuote) ); + +/* -------------------------------------------------------------------- */ +/* Put things together in a fashion depending on the operator. */ +/* -------------------------------------------------------------------- */ + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op) nOperation ); + + if( poOp == NULL ) + { + CPLAssert( FALSE ); + return CPLStrdup(""); + } + + switch( nOperation ) + { + // binary infix operators. + case SWQ_OR: + case SWQ_AND: + case SWQ_EQ: + case SWQ_NE: + case SWQ_GT: + case SWQ_LT: + case SWQ_GE: + case SWQ_LE: + case SWQ_LIKE: + case SWQ_ADD: + case SWQ_SUBTRACT: + case SWQ_MULTIPLY: + case SWQ_DIVIDE: + case SWQ_MODULUS: + CPLAssert( nSubExprCount >= 2 ); + if (papoSubExpr[0]->eNodeType == SNT_COLUMN || + papoSubExpr[0]->eNodeType == SNT_CONSTANT) + { + osExpr += apszSubExpr[0]; + } + else + { + osExpr += "("; + osExpr += apszSubExpr[0]; + osExpr += ")"; + } + osExpr += " "; + osExpr += poOp->pszName; + osExpr += " "; + if (papoSubExpr[1]->eNodeType == SNT_COLUMN || + papoSubExpr[1]->eNodeType == SNT_CONSTANT) + { + osExpr += apszSubExpr[1]; + } + else + { + osExpr += "("; + osExpr += apszSubExpr[1]; + osExpr += ")"; + } + if( nOperation == SWQ_LIKE && nSubExprCount == 3 ) + osExpr += CPLSPrintf( " ESCAPE (%s)", apszSubExpr[2] ); + break; + + case SWQ_NOT: + CPLAssert( nSubExprCount == 1 ); + osExpr.Printf( "NOT (%s)", apszSubExpr[0] ); + break; + + case SWQ_ISNULL: + CPLAssert( nSubExprCount == 1 ); + osExpr.Printf( "%s IS NULL", apszSubExpr[0] ); + break; + + case SWQ_IN: + osExpr.Printf( "%s IN (", apszSubExpr[0] ); + for( i = 1; i < nSubExprCount; i++ ) + { + if( i > 1 ) + osExpr += ","; + osExpr += "("; + osExpr += apszSubExpr[i]; + osExpr += ")"; + } + osExpr += ")"; + break; + + case SWQ_BETWEEN: + CPLAssert( nSubExprCount == 3 ); + osExpr.Printf( "%s %s (%s) AND (%s)", + apszSubExpr[0], + poOp->pszName, + apszSubExpr[1], + apszSubExpr[2] ); + break; + + case SWQ_CAST: + osExpr = "CAST("; + for( i = 0; i < nSubExprCount; i++ ) + { + if( i == 1 ) + osExpr += " AS "; + else if( i > 2 ) + osExpr += ", "; + + int nLen = (int)strlen(apszSubExpr[i]); + if( (i == 1 && + (apszSubExpr[i][0] == '\'' && nLen > 2 && apszSubExpr[i][nLen-1] == '\'')) || + (i == 2 && EQUAL(apszSubExpr[1], "'GEOMETRY")) ) + { + apszSubExpr[i][nLen-1] = '\0'; + osExpr += apszSubExpr[i] + 1; + } + else + osExpr += apszSubExpr[i]; + + if( i == 1 && nSubExprCount > 2) + osExpr += "("; + else if (i > 1 && i == nSubExprCount - 1) + osExpr += ")"; + } + osExpr += ")"; + break; + + default: // function style. + osExpr.Printf( "%s(", poOp->pszName ); + for( i = 0; i < nSubExprCount; i++ ) + { + if( i > 0 ) + osExpr += ","; + osExpr += "("; + osExpr += apszSubExpr[i]; + osExpr += ")"; + } + osExpr += ")"; + break; + } + +/* -------------------------------------------------------------------- */ +/* cleanup subexpressions. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nSubExprCount; i++ ) + CPLFree( apszSubExpr[i] ); + + return CPLStrdup( osExpr.c_str() ); +} + +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ + +swq_expr_node *swq_expr_node::Evaluate( swq_field_fetcher pfnFetcher, + void *pRecord ) + +{ + swq_expr_node *poRetNode = NULL; + +/* -------------------------------------------------------------------- */ +/* Duplicate ourselves if we are already a constant. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT ) + { + poRetNode = new swq_expr_node(); + + poRetNode->eNodeType = SNT_CONSTANT; + poRetNode->field_type = field_type; + poRetNode->int_value = int_value; + poRetNode->float_value = float_value; + + if( string_value ) + poRetNode->string_value = CPLStrdup(string_value); + else + poRetNode->string_value = NULL; + + if( geometry_value ) + poRetNode->geometry_value = geometry_value->clone(); + else + poRetNode->geometry_value = NULL; + + poRetNode->is_null = is_null; + + return poRetNode; + } + +/* -------------------------------------------------------------------- */ +/* If this is a field value from a record, fetch and return it. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_COLUMN ) + { + return pfnFetcher( this, pRecord ); + } + +/* -------------------------------------------------------------------- */ +/* This is an operation, collect the arguments keeping track of */ +/* which we will need to free. */ +/* -------------------------------------------------------------------- */ + std::vector apoValues; + std::vector anValueNeedsFree; + int i, bError = FALSE; + + for( i = 0; i < nSubExprCount && !bError; i++ ) + { + if( papoSubExpr[i]->eNodeType == SNT_CONSTANT ) + { + // avoid duplication. + apoValues.push_back( papoSubExpr[i] ); + anValueNeedsFree.push_back( FALSE ); + } + else + { + apoValues.push_back(papoSubExpr[i]->Evaluate(pfnFetcher,pRecord)); + anValueNeedsFree.push_back( TRUE ); + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch the operator definition and function. */ +/* -------------------------------------------------------------------- */ + if( !bError ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op) nOperation ); + + poRetNode = poOp->pfnEvaluator( this, &(apoValues[0]) ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < (int) apoValues.size(); i++ ) + { + if( anValueNeedsFree[i] ) + delete apoValues[i]; + } + + return poRetNode; +} diff --git a/ogr/swq_op_general.cpp b/ogr/swq_op_general.cpp new file mode 100644 index 0000000..0aec976 --- /dev/null +++ b/ogr/swq_op_general.cpp @@ -0,0 +1,1289 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: Implementation of SWQGeneralEvaluator and SWQGeneralChecker + * functions used to represent functions during evaluation and + * parsing. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "swq.h" +#include "ogr_geometry.h" + +/************************************************************************/ +/* swq_test_like() */ +/* */ +/* Does input match pattern? */ +/************************************************************************/ + +int swq_test_like( const char *input, const char *pattern, char chEscape ) + +{ + if( input == NULL || pattern == NULL ) + return 0; + + while( *input != '\0' ) + { + if( *pattern == '\0' ) + return 0; + + else if( *pattern == chEscape ) + { + pattern++; + if( *pattern == '\0' ) + return 0; + if( tolower(*pattern) != tolower(*input) ) + return 0; + else + { + input++; + pattern++; + } + } + + else if( *pattern == '_' ) + { + input++; + pattern++; + } + else if( *pattern == '%' ) + { + int eat; + + if( pattern[1] == '\0' ) + return 1; + + /* try eating varying amounts of the input till we get a positive*/ + for( eat = 0; input[eat] != '\0'; eat++ ) + { + if( swq_test_like(input+eat,pattern+1, chEscape) ) + return 1; + } + + return 0; + } + else + { + if( tolower(*pattern) != tolower(*input) ) + return 0; + else + { + input++; + pattern++; + } + } + } + + if( *pattern != '\0' && strcmp(pattern,"%") != 0 ) + return 0; + else + return 1; +} + +/************************************************************************/ +/* OGRHStoreGetValue() */ +/************************************************************************/ + +static char* OGRHStoreCheckEnd(char* pszIter, int bIsKey) +{ + pszIter ++; + for( ; *pszIter != '\0'; pszIter ++ ) + { + if( bIsKey ) + { + if( *pszIter == ' ' ) + ; + else if( *pszIter == '=' && pszIter[1] == '>' ) + return pszIter + 2; + else + return NULL; + } + else + { + if( *pszIter == ' ' ) + ; + else if( *pszIter == ',' ) + return pszIter + 1; + else + return NULL; + } + } + return pszIter; +} + +static char* OGRHStoreGetNextString(char* pszIter, + char** ppszOut, + int bIsKey) +{ + char ch; + int bInString = FALSE; + char* pszOut = NULL; + *ppszOut = NULL; + for( ; (ch = *pszIter) != '\0'; pszIter ++ ) + { + if( bInString ) + { + if( ch == '"' ) + { + *pszOut = '\0'; + return OGRHStoreCheckEnd(pszIter, bIsKey); + } + else if( ch == '\\') + { + pszIter ++; + if( (ch = *pszIter) == '\0' ) + return NULL; + } + *pszOut = ch; + pszOut ++; + } + else + { + if( ch == ' ' ) + { + if( pszOut != NULL ) + { + *pszIter = '\0'; + return OGRHStoreCheckEnd(pszIter, bIsKey); + } + } + else if( bIsKey && ch == '=' && pszIter[1] == '>' ) + { + if( pszOut != NULL ) + { + *pszIter = '\0'; + return pszIter + 2; + } + } + else if( !bIsKey && ch == ',' ) + { + if( pszOut != NULL ) + { + *pszIter = '\0'; + return pszIter + 1; + } + } + else if( ch == '"' ) + { + pszOut = *ppszOut = pszIter + 1; + bInString = TRUE; + } + else if( pszOut == NULL ) + pszOut = *ppszOut = pszIter; + } + } + + if( !bInString && pszOut != NULL ) + { + return pszIter; + } + return NULL; +} + +static char* OGRHStoreGetNextKeyValue(char* pszHStore, + char** ppszKey, + char** ppszValue) +{ + char* pszNext = OGRHStoreGetNextString(pszHStore, ppszKey, TRUE); + if( pszNext == NULL || *pszNext == '\0' ) + return NULL; + return OGRHStoreGetNextString(pszNext, ppszValue, FALSE); +} + +char* OGRHStoreGetValue(const char* pszHStore, const char* pszSearchedKey) +{ + char* pszHStoreDup = CPLStrdup(pszHStore); + char* pszHStoreIter = pszHStoreDup; + char* pszRet = NULL; + + while( TRUE ) + { + char* pszKey, *pszValue; + pszHStoreIter = OGRHStoreGetNextKeyValue(pszHStoreIter, &pszKey, &pszValue); + if( pszHStoreIter == NULL ) + { + break; + } + if( strcmp(pszKey, pszSearchedKey) == 0 ) + { + pszRet = CPLStrdup(pszValue); + break; + } + if( *pszHStoreIter == '\0' ) + { + break; + } + } + CPLFree(pszHStoreDup); + return pszRet; +} + +/************************************************************************/ +/* SWQGeneralEvaluator() */ +/************************************************************************/ + +swq_expr_node *SWQGeneralEvaluator( swq_expr_node *node, + swq_expr_node **sub_node_values ) + +{ + swq_expr_node *poRet = NULL; + +/* -------------------------------------------------------------------- */ +/* Floating point operations. */ +/* -------------------------------------------------------------------- */ + if( sub_node_values[0]->field_type == SWQ_FLOAT + || (node->nSubExprCount > 1 + && sub_node_values[1]->field_type == SWQ_FLOAT) ) + + { + poRet = new swq_expr_node(0); + poRet->field_type = node->field_type; + + if( sub_node_values[0]->field_type == SWQ_INTEGER ) + sub_node_values[0]->float_value = sub_node_values[0]->int_value; + if( node->nSubExprCount > 1 && + sub_node_values[1]->field_type == SWQ_INTEGER ) + sub_node_values[1]->float_value = sub_node_values[1]->int_value; + + if( node->nOperation != SWQ_ISNULL ) + { + for( int i = 0; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[i]->is_null ) + { + if( poRet->field_type == SWQ_BOOLEAN ) + { + poRet->int_value = FALSE; + return poRet; + } + else if( poRet->field_type == SWQ_FLOAT ) + { + poRet->float_value = 0; + poRet->is_null = 1; + return poRet; + } + else if( poRet->field_type == SWQ_INTEGER || + node->nOperation == SWQ_MODULUS ) + { + poRet->field_type = SWQ_INTEGER; + poRet->int_value = 0; + poRet->is_null = 1; + return poRet; + } + } + } + } + + switch( (swq_op) node->nOperation ) + { + case SWQ_EQ: + poRet->int_value = sub_node_values[0]->float_value + == sub_node_values[1]->float_value; + break; + + case SWQ_NE: + poRet->int_value = sub_node_values[0]->float_value + != sub_node_values[1]->float_value; + break; + + case SWQ_GT: + poRet->int_value = sub_node_values[0]->float_value + > sub_node_values[1]->float_value; + break; + + case SWQ_LT: + poRet->int_value = sub_node_values[0]->float_value + < sub_node_values[1]->float_value; + break; + + case SWQ_GE: + poRet->int_value = sub_node_values[0]->float_value + >= sub_node_values[1]->float_value; + break; + + case SWQ_LE: + poRet->int_value = sub_node_values[0]->float_value + <= sub_node_values[1]->float_value; + break; + + case SWQ_IN: + { + int i; + poRet->int_value = 0; + for( i = 1; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[0]->float_value + == sub_node_values[i]->float_value ) + { + poRet->int_value = 1; + break; + } + } + } + break; + + case SWQ_BETWEEN: + poRet->int_value = sub_node_values[0]->float_value + >= sub_node_values[1]->float_value && + sub_node_values[0]->float_value + <= sub_node_values[2]->float_value; + break; + + case SWQ_ISNULL: + poRet->int_value = sub_node_values[0]->is_null; + break; + + case SWQ_ADD: + poRet->float_value = sub_node_values[0]->float_value + + sub_node_values[1]->float_value; + break; + + case SWQ_SUBTRACT: + poRet->float_value = sub_node_values[0]->float_value + - sub_node_values[1]->float_value; + break; + + case SWQ_MULTIPLY: + poRet->float_value = sub_node_values[0]->float_value + * sub_node_values[1]->float_value; + break; + + case SWQ_DIVIDE: + if( sub_node_values[1]->float_value == 0 ) + poRet->float_value = INT_MAX; + else + poRet->float_value = sub_node_values[0]->float_value + / sub_node_values[1]->float_value; + break; + + case SWQ_MODULUS: + { + int nRight = (int) sub_node_values[1]->float_value; + poRet->field_type = SWQ_INTEGER; + if (nRight == 0) + poRet->int_value = INT_MAX; + else + poRet->int_value = ((int) sub_node_values[0]->float_value) + % nRight; + break; + } + + default: + CPLAssert( FALSE ); + delete poRet; + poRet = NULL; + break; + } + } +/* -------------------------------------------------------------------- */ +/* integer/boolean operations. */ +/* -------------------------------------------------------------------- */ + else if( sub_node_values[0]->field_type == SWQ_INTEGER + || sub_node_values[0]->field_type == SWQ_BOOLEAN ) + { + poRet = new swq_expr_node(0); + poRet->field_type = node->field_type; + + if( node->nOperation != SWQ_ISNULL ) + { + for( int i = 0; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[i]->is_null ) + { + if( poRet->field_type == SWQ_BOOLEAN ) + { + poRet->int_value = FALSE; + return poRet; + } + else if( poRet->field_type == SWQ_INTEGER ) + { + poRet->int_value = 0; + poRet->is_null = 1; + return poRet; + } + } + } + } + + switch( (swq_op) node->nOperation ) + { + case SWQ_AND: + poRet->int_value = sub_node_values[0]->int_value + && sub_node_values[1]->int_value; + break; + + case SWQ_OR: + poRet->int_value = sub_node_values[0]->int_value + || sub_node_values[1]->int_value; + break; + + case SWQ_NOT: + poRet->int_value = !sub_node_values[0]->int_value; + break; + + case SWQ_EQ: + poRet->int_value = sub_node_values[0]->int_value + == sub_node_values[1]->int_value; + break; + + case SWQ_NE: + poRet->int_value = sub_node_values[0]->int_value + != sub_node_values[1]->int_value; + break; + + case SWQ_GT: + poRet->int_value = sub_node_values[0]->int_value + > sub_node_values[1]->int_value; + break; + + case SWQ_LT: + poRet->int_value = sub_node_values[0]->int_value + < sub_node_values[1]->int_value; + break; + + case SWQ_GE: + poRet->int_value = sub_node_values[0]->int_value + >= sub_node_values[1]->int_value; + break; + + case SWQ_LE: + poRet->int_value = sub_node_values[0]->int_value + <= sub_node_values[1]->int_value; + break; + + case SWQ_IN: + { + int i; + poRet->int_value = 0; + for( i = 1; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[0]->int_value + == sub_node_values[i]->int_value ) + { + poRet->int_value = 1; + break; + } + } + } + break; + + case SWQ_BETWEEN: + poRet->int_value = sub_node_values[0]->int_value + >= sub_node_values[1]->int_value && + sub_node_values[0]->int_value + <= sub_node_values[2]->int_value; + break; + + case SWQ_ISNULL: + poRet->int_value = sub_node_values[0]->is_null; + break; + + case SWQ_ADD: + poRet->int_value = sub_node_values[0]->int_value + + sub_node_values[1]->int_value; + break; + + case SWQ_SUBTRACT: + poRet->int_value = sub_node_values[0]->int_value + - sub_node_values[1]->int_value; + break; + + case SWQ_MULTIPLY: + poRet->int_value = sub_node_values[0]->int_value + * sub_node_values[1]->int_value; + break; + + case SWQ_DIVIDE: + if( sub_node_values[1]->int_value == 0 ) + poRet->int_value = INT_MAX; + else + poRet->int_value = sub_node_values[0]->int_value + / sub_node_values[1]->int_value; + break; + + case SWQ_MODULUS: + if( sub_node_values[1]->int_value == 0 ) + poRet->int_value = INT_MAX; + else + poRet->int_value = sub_node_values[0]->int_value + % sub_node_values[1]->int_value; + break; + + default: + CPLAssert( FALSE ); + delete poRet; + poRet = NULL; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* String operations. */ +/* -------------------------------------------------------------------- */ + else + { + poRet = new swq_expr_node(0); + poRet->field_type = node->field_type; + + if( node->nOperation != SWQ_ISNULL ) + { + for( int i = 0; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[i]->is_null ) + { + if( poRet->field_type == SWQ_BOOLEAN ) + { + poRet->int_value = FALSE; + return poRet; + } + else if( poRet->field_type == SWQ_STRING ) + { + poRet->string_value = CPLStrdup(""); + poRet->is_null = 1; + return poRet; + } + } + } + } + + switch( (swq_op) node->nOperation ) + { + case SWQ_EQ: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) == 0; + break; + + case SWQ_NE: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) != 0; + break; + + case SWQ_GT: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) > 0; + break; + + case SWQ_LT: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) < 0; + break; + + case SWQ_GE: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) >= 0; + break; + + case SWQ_LE: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) <= 0; + break; + + case SWQ_IN: + { + int i; + poRet->int_value = 0; + for( i = 1; i < node->nSubExprCount; i++ ) + { + if( strcasecmp(sub_node_values[0]->string_value, + sub_node_values[i]->string_value) == 0 ) + { + poRet->int_value = 1; + break; + } + } + } + break; + + case SWQ_BETWEEN: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) >= 0 && + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[2]->string_value) <= 0; + break; + + case SWQ_LIKE: + { + char chEscape = '\0'; + if( node->nSubExprCount == 3 ) + chEscape = sub_node_values[2]->string_value[0]; + poRet->int_value = swq_test_like(sub_node_values[0]->string_value, + sub_node_values[1]->string_value, + chEscape); + break; + } + + case SWQ_ISNULL: + poRet->int_value = sub_node_values[0]->is_null; + break; + + case SWQ_CONCAT: + case SWQ_ADD: + { + CPLString osResult = sub_node_values[0]->string_value; + int i; + + for( i = 1; i < node->nSubExprCount; i++ ) + osResult += sub_node_values[i]->string_value; + + poRet->string_value = CPLStrdup(osResult); + poRet->is_null = sub_node_values[0]->is_null; + break; + } + + case SWQ_SUBSTR: + { + int nOffset, nSize; + const char *pszSrcStr = sub_node_values[0]->string_value; + + if( sub_node_values[1]->field_type == SWQ_INTEGER ) + nOffset = sub_node_values[1]->int_value; + else if( sub_node_values[1]->field_type == SWQ_FLOAT ) + nOffset = (int) sub_node_values[1]->float_value; + else + nOffset = 0; + + if( node->nSubExprCount < 3 ) + nSize = 100000; + else if( sub_node_values[2]->field_type == SWQ_INTEGER ) + nSize = sub_node_values[2]->int_value; + else if( sub_node_values[2]->field_type == SWQ_FLOAT ) + nSize = (int) sub_node_values[2]->float_value; + else + nSize = 0; + + int nSrcStrLen = (int)strlen(pszSrcStr); + + + /* In SQL, the first character is at offset 1 */ + /* And 0 is considered as 1 */ + if (nOffset > 0) + nOffset --; + /* Some implementations allow negative offsets, to start */ + /* from the end of the string */ + else if( nOffset < 0 ) + { + if( nSrcStrLen + nOffset >= 0 ) + nOffset = nSrcStrLen + nOffset; + else + nOffset = 0; + } + + if( nSize < 0 || nOffset > nSrcStrLen ) + { + nOffset = 0; + nSize = 0; + } + else if( nOffset + nSize > nSrcStrLen ) + nSize = nSrcStrLen - nOffset; + + CPLString osResult = pszSrcStr + nOffset; + if( (int)osResult.size() > nSize ) + osResult.resize( nSize ); + + poRet->string_value = CPLStrdup(osResult); + poRet->is_null = sub_node_values[0]->is_null; + break; + } + + case SWQ_HSTORE_GET_VALUE: + { + const char *pszHStore = sub_node_values[0]->string_value; + const char *pszSearchedKey = sub_node_values[1]->string_value; + char* pszRet = OGRHStoreGetValue(pszHStore, pszSearchedKey); + poRet->string_value = pszRet ? pszRet : CPLStrdup(""); + poRet->is_null = (pszRet == NULL); + break; + } + + default: + CPLAssert( FALSE ); + delete poRet; + poRet = NULL; + break; + } + } + + return poRet; +} + +/************************************************************************/ +/* SWQAutoPromoteIntegerToFloat() */ +/************************************************************************/ + +static void SWQAutoPromoteIntegerToFloat( swq_expr_node *poNode ) + +{ + if( poNode->nSubExprCount < 2 ) + return; + + swq_field_type eArgType = poNode->papoSubExpr[0]->field_type; + int i; + + // We allow mixes of integer and float, and string and dates. + // When encountered, we promote integers to floats, and strings to + // dates. We do that now. + for( i = 1; i < poNode->nSubExprCount; i++ ) + { + if( eArgType == SWQ_INTEGER + && poNode->papoSubExpr[i]->field_type == SWQ_FLOAT ) + eArgType = SWQ_FLOAT; + } + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_FLOAT + && poSubNode->field_type == SWQ_INTEGER ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + poSubNode->float_value = poSubNode->int_value; + poSubNode->field_type = SWQ_FLOAT; + } + } + } +} + +/************************************************************************/ +/* SWQAutoPromoteStringToDateTime() */ +/************************************************************************/ + +static void SWQAutoPromoteStringToDateTime( swq_expr_node *poNode ) + +{ + if( poNode->nSubExprCount < 2 ) + return; + + swq_field_type eArgType = poNode->papoSubExpr[0]->field_type; + int i; + + // We allow mixes of integer and float, and string and dates. + // When encountered, we promote integers to floats, and strings to + // dates. We do that now. + for( i = 1; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_STRING + && (poSubNode->field_type == SWQ_DATE + || poSubNode->field_type == SWQ_TIME + || poSubNode->field_type == SWQ_TIMESTAMP) ) + eArgType = SWQ_TIMESTAMP; + } + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_TIMESTAMP + && (poSubNode->field_type == SWQ_STRING + || poSubNode->field_type == SWQ_DATE + || poSubNode->field_type == SWQ_TIME) ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + poSubNode->field_type = SWQ_TIMESTAMP; + } + } + } +} + +/************************************************************************/ +/* SWQAutoConvertStringToNumeric() */ +/* */ +/* Convert string constants to integer or float constants */ +/* when there is a mix of arguments of type numeric and string */ +/************************************************************************/ + +static void SWQAutoConvertStringToNumeric( swq_expr_node *poNode ) + +{ + if( poNode->nSubExprCount < 2 ) + return; + + swq_field_type eArgType = poNode->papoSubExpr[0]->field_type; + int i; + + for( i = 1; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + /* identify the mixture of the argument type */ + if( (eArgType == SWQ_STRING + && (poSubNode->field_type == SWQ_INTEGER + || poSubNode->field_type == SWQ_FLOAT)) || + (eArgType == SWQ_INTEGER + && poSubNode->field_type == SWQ_STRING) ) + { + eArgType = SWQ_FLOAT; + break; + } + } + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_FLOAT + && poSubNode->field_type == SWQ_STRING ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + /* apply the string to numeric conversion */ + char* endPtr = NULL; + poSubNode->float_value = CPLStrtod(poSubNode->string_value, &endPtr); + if ( !(endPtr == NULL || *endPtr == '\0') ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Conversion failed when converting the string value '%s' to data type float.", + poSubNode->string_value); + continue; + } + + /* we should also fill the integer value in this case */ + poSubNode->int_value = (int)poSubNode->float_value; + poSubNode->field_type = SWQ_FLOAT; + } + } + } +} + +/************************************************************************/ +/* SWQCheckSubExprAreNotGeometries() */ +/************************************************************************/ + +static int SWQCheckSubExprAreNotGeometries( swq_expr_node *poNode ) +{ + for( int i = 0; i < poNode->nSubExprCount; i++ ) + { + if( poNode->papoSubExpr[i]->field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use geometry field in this operation." ); + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* SWQGeneralChecker() */ +/* */ +/* Check the general purpose functions have appropriate types, */ +/* and count and indicate the function return type under the */ +/* circumstances. */ +/************************************************************************/ + +swq_field_type SWQGeneralChecker( swq_expr_node *poNode ) + +{ + swq_field_type eRetType = SWQ_ERROR; + swq_field_type eArgType = SWQ_OTHER; + int nArgCount = -1; + + switch( (swq_op) poNode->nOperation ) + { + case SWQ_AND: + case SWQ_OR: + case SWQ_NOT: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_BOOLEAN; + break; + + case SWQ_EQ: + case SWQ_NE: + case SWQ_GT: + case SWQ_LT: + case SWQ_GE: + case SWQ_LE: + case SWQ_IN: + case SWQ_BETWEEN: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_BOOLEAN; + SWQAutoConvertStringToNumeric( poNode ); + SWQAutoPromoteIntegerToFloat( poNode ); + SWQAutoPromoteStringToDateTime( poNode ); + eArgType = poNode->papoSubExpr[0]->field_type; + break; + + case SWQ_ISNULL: + eRetType = SWQ_BOOLEAN; + break; + + case SWQ_LIKE: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_BOOLEAN; + eArgType = SWQ_STRING; + break; + + case SWQ_MODULUS: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_INTEGER; + eArgType = SWQ_INTEGER; + break; + + case SWQ_ADD: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + SWQAutoPromoteIntegerToFloat( poNode ); + if( poNode->papoSubExpr[0]->field_type == SWQ_STRING ) + eRetType = eArgType = SWQ_STRING; + else if( poNode->papoSubExpr[0]->field_type == SWQ_FLOAT ) + eRetType = eArgType = SWQ_FLOAT; + else + eRetType = eArgType = SWQ_INTEGER; + break; + + case SWQ_SUBTRACT: + case SWQ_MULTIPLY: + case SWQ_DIVIDE: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + SWQAutoPromoteIntegerToFloat( poNode ); + if( poNode->papoSubExpr[0]->field_type == SWQ_FLOAT ) + eRetType = eArgType = SWQ_FLOAT; + else + eRetType = eArgType = SWQ_INTEGER; + break; + + case SWQ_CONCAT: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_STRING; + eArgType = SWQ_STRING; + break; + + case SWQ_SUBSTR: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_STRING; + if( poNode->nSubExprCount > 3 || poNode->nSubExprCount < 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected 2 or 3 arguments to SUBSTR(), but got %d.", + poNode->nSubExprCount ); + return SWQ_ERROR; + } + if( poNode->papoSubExpr[0]->field_type != SWQ_STRING + || poNode->papoSubExpr[1]->field_type != SWQ_INTEGER + || (poNode->nSubExprCount > 2 + && poNode->papoSubExpr[2]->field_type != SWQ_INTEGER) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Wrong argument type for SUBSTR(), expected SUBSTR(string,int,int) or SUBSTR(string,int)." ); + return SWQ_ERROR; + } + break; + + case SWQ_HSTORE_GET_VALUE: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_STRING; + if( poNode->nSubExprCount != 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected 2 arguments to hstore_get_value(), but got %d.", + poNode->nSubExprCount ); + return SWQ_ERROR; + } + if( poNode->papoSubExpr[0]->field_type != SWQ_STRING + || poNode->papoSubExpr[1]->field_type != SWQ_STRING ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Wrong argument type for hstore_get_value(), expected hstore_get_value(string,string)." ); + return SWQ_ERROR; + } + break; + + default: + { + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + + CPLError( CE_Failure, CPLE_AppDefined, + "SWQGeneralChecker() called on unsupported operation %s.", + poOp->pszName); + return SWQ_ERROR; + } + } +/* -------------------------------------------------------------------- */ +/* Check argument types. */ +/* -------------------------------------------------------------------- */ + if( eArgType != SWQ_OTHER ) + { + int i; + + if( eArgType == SWQ_INTEGER ) + eArgType = SWQ_FLOAT; + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_field_type eThisArgType = poNode->papoSubExpr[i]->field_type; + if( eThisArgType == SWQ_INTEGER ) + eThisArgType = SWQ_FLOAT; + + if( eArgType != eThisArgType ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + + CPLError( CE_Failure, CPLE_AppDefined, + "Type mismatch or improper type of arguments to %s operator.", + poOp->pszName ); + return SWQ_ERROR; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Validate the arg count if requested. */ +/* -------------------------------------------------------------------- */ + if( nArgCount != -1 + && nArgCount != poNode->nSubExprCount ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + + CPLError( CE_Failure, CPLE_AppDefined, + "Expected %d arguments to %s, but got %d arguments.", + nArgCount, + poOp->pszName, + poNode->nSubExprCount ); + return SWQ_ERROR; + } + + return eRetType; +} + +/************************************************************************/ +/* SWQCastEvaluator() */ +/************************************************************************/ + +swq_expr_node *SWQCastEvaluator( swq_expr_node *node, + swq_expr_node **sub_node_values ) + +{ + swq_expr_node *poRetNode = NULL; + swq_expr_node *poSrcNode = sub_node_values[0]; + + switch( node->field_type ) + { + case SWQ_INTEGER: + { + poRetNode = new swq_expr_node( 0 ); + poRetNode->is_null = poSrcNode->is_null; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + poRetNode->int_value = poSrcNode->int_value; + break; + + case SWQ_FLOAT: + poRetNode->int_value = (int) poSrcNode->float_value; + break; + + default: + poRetNode->int_value = atoi(poSrcNode->string_value); + break; + } + } + break; + + case SWQ_FLOAT: + { + poRetNode = new swq_expr_node( 0.0 ); + poRetNode->is_null = poSrcNode->is_null; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + poRetNode->float_value = poSrcNode->int_value; + break; + + case SWQ_FLOAT: + poRetNode->float_value = poSrcNode->float_value; + break; + + default: + poRetNode->float_value = atof(poSrcNode->string_value); + break; + } + } + break; + + case SWQ_GEOMETRY: + { + poRetNode = new swq_expr_node( (OGRGeometry*) NULL ); + if( !poSrcNode->is_null ) + { + switch( poSrcNode->field_type ) + { + case SWQ_GEOMETRY: + { + poRetNode->geometry_value = + poSrcNode->geometry_value->clone(); + poRetNode->is_null = FALSE; + break; + } + + case SWQ_STRING: + { + char* pszTmp = poSrcNode->string_value; + OGRGeometryFactory::createFromWkt(&pszTmp, NULL, + &(poRetNode->geometry_value)); + if( poRetNode->geometry_value != NULL ) + poRetNode->is_null = FALSE; + break; + } + + default: + break; + } + } + break; + } + + // everything else is a string. + default: + { + CPLString osRet; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + osRet.Printf( "%d", poSrcNode->int_value ); + break; + + case SWQ_FLOAT: + osRet.Printf( "%.15g", poSrcNode->float_value ); + break; + + case SWQ_GEOMETRY: + { + if( poSrcNode->geometry_value != NULL ) + { + char* pszWKT; + poSrcNode->geometry_value->exportToWkt(&pszWKT); + osRet = pszWKT; + CPLFree(pszWKT); + } + else + osRet = ""; + break; + } + + default: + osRet = poSrcNode->string_value; + break; + } + + if( node->nSubExprCount > 2 ) + { + int nWidth; + + nWidth = sub_node_values[2]->int_value; + if( nWidth > 0 && (int) strlen(osRet) > nWidth ) + osRet.resize(nWidth); + } + + poRetNode = new swq_expr_node( osRet.c_str() ); + poRetNode->is_null = poSrcNode->is_null; + } + } + + return poRetNode; +} + +/************************************************************************/ +/* SWQCastChecker() */ +/************************************************************************/ + +swq_field_type SWQCastChecker( swq_expr_node *poNode ) + +{ + swq_field_type eType = SWQ_ERROR; + const char *pszTypeName = poNode->papoSubExpr[1]->string_value; + + if( poNode->papoSubExpr[0]->field_type == SWQ_GEOMETRY && + !(EQUAL(pszTypeName,"character") || + EQUAL(pszTypeName,"geometry")) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot cast geometry to %s", + pszTypeName ); + } + + else if( EQUAL(pszTypeName,"character") ) + eType = SWQ_STRING; + else if( EQUAL(pszTypeName,"integer") ) + eType = SWQ_INTEGER; + else if( EQUAL(pszTypeName,"float") ) + eType = SWQ_FLOAT; + else if( EQUAL(pszTypeName,"numeric") ) + eType = SWQ_FLOAT; + else if( EQUAL(pszTypeName,"timestamp") ) + eType = SWQ_TIMESTAMP; + else if( EQUAL(pszTypeName,"date") ) + eType = SWQ_DATE; + else if( EQUAL(pszTypeName,"time") ) + eType = SWQ_TIME; + else if( EQUAL(pszTypeName,"geometry") ) + { + if( !(poNode->papoSubExpr[0]->field_type == SWQ_GEOMETRY || + poNode->papoSubExpr[0]->field_type == SWQ_STRING) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot cast %s to geometry", + SWQFieldTypeToString(poNode->papoSubExpr[0]->field_type) ); + } + else + eType = SWQ_GEOMETRY; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognized typename %s in CAST operator.", + pszTypeName ); + } + + poNode->field_type = eType; + + return eType; +} diff --git a/ogr/swq_op_registrar.cpp b/ogr/swq_op_registrar.cpp new file mode 100644 index 0000000..eec6468 --- /dev/null +++ b/ogr/swq_op_registrar.cpp @@ -0,0 +1,123 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: Implementation of the swq_op_registrar class used to + * represent operations possible in an SQL expression. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "swq.h" + +static swq_field_type SWQColumnFuncChecker( swq_expr_node *poNode ); + +static const swq_operation swq_apsOperations[] = +{ + { "OR", SWQ_OR, SWQGeneralEvaluator, SWQGeneralChecker }, + { "AND", SWQ_AND, SWQGeneralEvaluator, SWQGeneralChecker }, + { "NOT", SWQ_NOT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "=", SWQ_EQ , SWQGeneralEvaluator, SWQGeneralChecker }, + { "<>", SWQ_NE , SWQGeneralEvaluator, SWQGeneralChecker }, + { ">=", SWQ_GE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "<=", SWQ_LE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "<", SWQ_LT , SWQGeneralEvaluator, SWQGeneralChecker }, + { ">", SWQ_GT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "LIKE", SWQ_LIKE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "IS NULL", SWQ_ISNULL , SWQGeneralEvaluator, SWQGeneralChecker }, + { "IN", SWQ_IN , SWQGeneralEvaluator, SWQGeneralChecker }, + { "BETWEEN", SWQ_BETWEEN , SWQGeneralEvaluator, SWQGeneralChecker }, + { "+", SWQ_ADD , SWQGeneralEvaluator, SWQGeneralChecker }, + { "-", SWQ_SUBTRACT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "*", SWQ_MULTIPLY , SWQGeneralEvaluator, SWQGeneralChecker }, + { "/", SWQ_DIVIDE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "%", SWQ_MODULUS , SWQGeneralEvaluator, SWQGeneralChecker }, + { "CONCAT", SWQ_CONCAT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "SUBSTR", SWQ_SUBSTR , SWQGeneralEvaluator, SWQGeneralChecker }, + { "HSTORE_GET_VALUE", SWQ_HSTORE_GET_VALUE , SWQGeneralEvaluator, SWQGeneralChecker }, + + { "AVG", SWQ_AVG, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "MIN", SWQ_MIN, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "MAX", SWQ_MAX, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "COUNT", SWQ_COUNT, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "SUM", SWQ_SUM, SWQGeneralEvaluator, SWQColumnFuncChecker }, + + { "CAST", SWQ_CAST, SWQCastEvaluator, SWQCastChecker } +}; + +#define N_OPERATIONS (sizeof(swq_apsOperations) / sizeof(swq_apsOperations[0])) + +/************************************************************************/ +/* GetOperator() */ +/************************************************************************/ + +const swq_operation *swq_op_registrar::GetOperator( const char *pszName ) + +{ + unsigned int i; + for( i = 0; i < N_OPERATIONS; i++ ) + { + if( EQUAL(pszName,swq_apsOperations[i].pszName) ) + return &(swq_apsOperations[i]); + } + + return NULL; +} + +/************************************************************************/ +/* GetOperator() */ +/************************************************************************/ + +const swq_operation *swq_op_registrar::GetOperator( swq_op eOperator ) + +{ + unsigned int i; + + for( i = 0; i < N_OPERATIONS; i++ ) + { + if( eOperator == swq_apsOperations[i].eOperation ) + return &(swq_apsOperations[i]); + } + + return NULL; +} + +/************************************************************************/ +/* SWQColumnFuncChecker() */ +/* */ +/* Column summary functions are not legal in any context except */ +/* as a root operator on column definitions. They are removed */ +/* from this tree before checking so we just need to issue an */ +/* error if they are used in any other context. */ +/************************************************************************/ + +static swq_field_type SWQColumnFuncChecker( swq_expr_node *poNode ) +{ + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + CPLError( CE_Failure, CPLE_AppDefined, + "Column Summary Function '%s' found in an inappropriate context.", + (poOp) ? poOp->pszName : "" ); + return SWQ_ERROR; +} diff --git a/ogr/swq_parser.cpp b/ogr/swq_parser.cpp new file mode 100644 index 0000000..e745455 --- /dev/null +++ b/ogr/swq_parser.cpp @@ -0,0 +1,2698 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + * Copyright (c) 2010-2013, Even Rouault + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse swqparse +#define yylex swqlex +#define yyerror swqerror +#define yydebug swqdebug +#define yynerrs swqnerrs + + +/* Copy the first part of user declarations. */ +#line 1 "swq_parser.y" /* yacc.c:339 */ + +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: expression and select parser grammar. + * Requires Bison 2.4.0 or newer to process. Use "make parser" target. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_geometry.h" +#include "swq.h" + +#define YYSTYPE swq_expr_node* + +/* Defining YYSTYPE_IS_TRIVIAL is needed because the parser is generated as a C++ file. */ +/* See http://www.gnu.org/s/bison/manual/html_node/Memory-Management.html that suggests */ +/* increase YYINITDEPTH instead, but this will consume memory. */ +/* Setting YYSTYPE_IS_TRIVIAL overcomes this limitation, but might be fragile because */ +/* it appears to be a non documented feature of Bison */ +#define YYSTYPE_IS_TRIVIAL 1 + + +#line 119 "swq_parser.cpp" /* yacc.c:339 */ + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "swq_parser.hpp". */ +#ifndef YY_SWQ_SWQ_PARSER_HPP_INCLUDED +# define YY_SWQ_SWQ_PARSER_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int swqdebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + SWQT_INTEGER_NUMBER = 258, + SWQT_FLOAT_NUMBER = 259, + SWQT_STRING = 260, + SWQT_IDENTIFIER = 261, + SWQT_IN = 262, + SWQT_LIKE = 263, + SWQT_ESCAPE = 264, + SWQT_BETWEEN = 265, + SWQT_NULL = 266, + SWQT_IS = 267, + SWQT_SELECT = 268, + SWQT_LEFT = 269, + SWQT_JOIN = 270, + SWQT_WHERE = 271, + SWQT_ON = 272, + SWQT_ORDER = 273, + SWQT_BY = 274, + SWQT_FROM = 275, + SWQT_AS = 276, + SWQT_ASC = 277, + SWQT_DESC = 278, + SWQT_DISTINCT = 279, + SWQT_CAST = 280, + SWQT_UNION = 281, + SWQT_ALL = 282, + SWQT_LOGICAL_START = 283, + SWQT_VALUE_START = 284, + SWQT_SELECT_START = 285, + SWQT_NOT = 286, + SWQT_OR = 287, + SWQT_AND = 288, + SWQT_UMINUS = 289, + SWQT_RESERVED_KEYWORD = 290 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int swqparse (swq_parse_context *context); + +#endif /* !YY_SWQ_SWQ_PARSER_HPP_INCLUDED */ + +/* Copy the second part of user declarations. */ + +#line 206 "swq_parser.cpp" /* yacc.c:358 */ + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if (! defined __GNUC__ || __GNUC__ < 2 \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 23 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 328 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 49 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 21 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 89 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 191 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 290 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 46, 2, 2, 2, 38, 2, 2, + 41, 42, 36, 34, 47, 35, 48, 37, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 44, 43, 45, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 39, + 40 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 111, 111, 116, 121, 127, 135, 143, 150, 155, + 163, 171, 179, 187, 195, 203, 211, 219, 227, 235, + 248, 257, 271, 280, 295, 304, 318, 325, 339, 345, + 352, 359, 375, 380, 385, 389, 394, 399, 404, 420, + 427, 434, 441, 448, 455, 479, 487, 493, 500, 509, + 527, 547, 548, 551, 556, 557, 559, 567, 568, 571, + 580, 589, 598, 610, 621, 635, 657, 687, 721, 745, + 774, 780, 783, 784, 789, 790, 799, 809, 810, 813, + 814, 817, 823, 829, 837, 841, 847, 857, 868, 879 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of string\"", "error", "$undefined", "\"integer number\"", + "\"floating point number\"", "\"string\"", "\"identifier\"", "\"IN\"", + "\"LIKE\"", "\"ESCAPE\"", "\"BETWEEN\"", "\"NULL\"", "\"IS\"", + "\"SELECT\"", "\"LEFT\"", "\"JOIN\"", "\"WHERE\"", "\"ON\"", "\"ORDER\"", + "\"BY\"", "\"FROM\"", "\"AS\"", "\"ASC\"", "\"DESC\"", "\"DISTINCT\"", + "\"CAST\"", "\"UNION\"", "\"ALL\"", "SWQT_LOGICAL_START", + "SWQT_VALUE_START", "SWQT_SELECT_START", "\"NOT\"", "\"OR\"", "\"AND\"", + "'+'", "'-'", "'*'", "'/'", "'%'", "SWQT_UMINUS", "\"reserved keyword\"", + "'('", "')'", "'='", "'<'", "'>'", "'!'", "','", "'.'", "$accept", + "input", "logical_expr", "value_expr_list", "field_value", "value_expr", + "type_def", "select_statement", "select_core", "opt_union_all", + "union_all", "select_field_list", "column_spec", "as_clause", + "opt_where", "opt_joins", "opt_order_by", "sort_spec_list", "sort_spec", + "string_or_identifier", "table_def", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 43, 45, 42, 47, 37, 289, + 290, 40, 41, 61, 60, 62, 33, 44, 46 +}; +# endif + +#define YYPACT_NINF -170 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-170))) + +#define YYTABLE_NINF -1 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 72, 243, 260, -8, 16, -170, -170, -170, -10, -170, + -1, 243, 260, 243, 56, -170, 181, 260, 290, 217, + 23, -170, 18, -170, 260, 36, 260, 56, -170, -25, + 165, 243, 243, 22, 260, 260, -5, 54, 260, 260, + 260, 260, 260, 62, 126, 149, 31, 44, -9, 99, + -170, 144, 39, 29, 43, 65, -170, -8, 53, 252, + -170, 105, -170, -170, 87, -170, 260, 268, 279, -170, + 112, 84, 260, 260, 75, 75, -170, -170, -170, 260, + 260, 290, 260, 260, 290, 260, 290, 260, 226, -2, + -170, 79, 4, -170, -170, 103, -170, -170, 111, 217, + 18, -170, -170, -170, 260, 127, 114, 260, 260, -170, + 260, 273, 285, 290, 290, 290, 290, 290, 290, 151, + 116, -170, -170, -170, 115, 153, 121, -170, -170, -170, + 125, 120, -170, 290, 290, 122, 260, 260, 128, 4, + 103, -170, 161, 111, 152, 69, -170, -170, 290, 290, + 4, -170, 177, 111, 168, 243, 169, -20, 1, -170, + -170, 178, 151, 56, 167, -170, -170, 191, -170, 194, + 151, 155, 151, 162, 163, 170, 151, 123, -170, 159, + -170, -170, 151, 121, -170, -170, 151, 121, -170, -170, + -170 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 0, 32, 33, 34, 30, 37, + 0, 0, 0, 0, 2, 35, 0, 0, 3, 0, + 0, 4, 54, 1, 0, 0, 0, 7, 38, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, + 64, 61, 0, 57, 0, 0, 51, 0, 0, 29, + 31, 0, 8, 36, 6, 5, 0, 18, 0, 26, + 0, 0, 0, 0, 39, 40, 41, 42, 43, 0, + 0, 9, 0, 0, 12, 0, 13, 0, 0, 0, + 60, 30, 59, 85, 84, 0, 63, 71, 0, 0, + 54, 56, 55, 44, 0, 0, 0, 0, 0, 27, + 0, 19, 0, 15, 16, 14, 10, 17, 11, 0, + 0, 65, 62, 70, 85, 86, 74, 58, 52, 28, + 46, 0, 22, 20, 24, 0, 0, 0, 0, 66, + 0, 87, 0, 0, 72, 0, 45, 23, 21, 25, + 68, 67, 88, 0, 0, 0, 77, 0, 0, 69, + 89, 0, 0, 73, 0, 53, 47, 0, 49, 0, + 0, 0, 0, 0, 0, 0, 0, 81, 78, 80, + 48, 50, 0, 74, 82, 83, 0, 74, 75, 79, + 76 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -170, -170, -11, -63, -48, 11, -170, 157, 213, 134, + -170, 136, -170, -81, -170, -169, -170, 50, -170, -83, + -124 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 4, 14, 58, 15, 16, 131, 21, 22, 56, + 57, 52, 53, 96, 156, 144, 165, 178, 179, 97, + 126 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_uint8 yytable[] = +{ + 27, 92, 29, 106, 60, 19, 69, 31, 32, 93, + 94, 122, 123, 18, 188, 125, 23, 62, 190, 154, + 64, 65, 166, 28, 30, 95, 70, 167, 47, 161, + 51, 24, 88, 20, 121, 59, 19, 61, 25, 89, + 26, 129, 60, 168, 55, 67, 68, 135, 169, 74, + 75, 76, 77, 78, 81, 84, 86, 152, 151, 98, + 125, 71, 72, 66, 73, 5, 6, 7, 8, 159, + 125, 138, 157, 9, 87, 158, 99, 59, 38, 39, + 40, 41, 42, 111, 112, 100, 63, 10, 31, 32, + 113, 114, 101, 115, 116, 103, 117, 12, 118, 59, + 1, 2, 3, 17, 90, 91, 79, 80, 93, 94, + 51, 40, 41, 42, 171, 59, 124, 94, 133, 134, + 32, 59, 175, 109, 177, 110, 105, 25, 183, 5, + 6, 7, 8, 130, 187, 142, 143, 9, 177, 38, + 39, 40, 41, 42, 163, 184, 185, 148, 149, 93, + 94, 10, 5, 6, 7, 8, 132, 91, 139, 141, + 9, 12, 146, 140, 147, 95, 145, 17, 155, 82, + 150, 83, 33, 34, 10, 35, 153, 36, 38, 39, + 40, 41, 42, 160, 12, 162, 172, 164, 33, 34, + 17, 35, 85, 36, 173, 170, 37, 174, 176, 38, + 39, 40, 41, 42, 180, 181, 186, 63, 43, 44, + 45, 46, 37, 182, 102, 38, 39, 40, 41, 42, + 5, 6, 7, 48, 43, 44, 45, 46, 9, 5, + 6, 7, 8, 54, 128, 127, 189, 9, 0, 0, + 0, 49, 10, 0, 0, 0, 5, 6, 7, 8, + 119, 10, 12, 50, 9, 0, 0, 0, 17, 0, + 0, 12, 120, 5, 6, 7, 8, 17, 10, 0, + 0, 9, 0, 0, 11, 0, 0, 107, 12, 0, + 0, 0, 136, 0, 13, 10, 38, 39, 40, 41, + 42, 0, 0, 0, 0, 12, 0, 0, 0, 104, + 0, 17, 38, 39, 40, 41, 42, 38, 39, 40, + 41, 42, 108, 38, 39, 40, 41, 42, 137, 38, + 39, 40, 41, 42, 38, 39, 40, 41, 42 +}; + +static const yytype_int16 yycheck[] = +{ + 11, 49, 13, 66, 6, 13, 11, 32, 33, 5, + 6, 92, 95, 2, 183, 98, 0, 42, 187, 143, + 31, 32, 42, 12, 13, 21, 31, 47, 17, 153, + 19, 41, 41, 41, 36, 24, 13, 26, 48, 48, + 41, 104, 6, 42, 26, 34, 35, 110, 47, 38, + 39, 40, 41, 42, 43, 44, 45, 140, 139, 20, + 143, 7, 8, 41, 10, 3, 4, 5, 6, 150, + 153, 119, 3, 11, 43, 6, 47, 66, 34, 35, + 36, 37, 38, 72, 73, 42, 42, 25, 32, 33, + 79, 80, 27, 82, 83, 42, 85, 35, 87, 88, + 28, 29, 30, 41, 5, 6, 44, 45, 5, 6, + 99, 36, 37, 38, 162, 104, 5, 6, 107, 108, + 33, 110, 170, 11, 172, 41, 21, 48, 176, 3, + 4, 5, 6, 6, 182, 14, 15, 11, 186, 34, + 35, 36, 37, 38, 155, 22, 23, 136, 137, 5, + 6, 25, 3, 4, 5, 6, 42, 6, 42, 6, + 11, 35, 42, 48, 42, 21, 41, 41, 16, 43, + 42, 45, 7, 8, 25, 10, 15, 12, 34, 35, + 36, 37, 38, 6, 35, 17, 19, 18, 7, 8, + 41, 10, 43, 12, 3, 17, 31, 3, 43, 34, + 35, 36, 37, 38, 42, 42, 47, 42, 43, 44, + 45, 46, 31, 43, 57, 34, 35, 36, 37, 38, + 3, 4, 5, 6, 43, 44, 45, 46, 11, 3, + 4, 5, 6, 20, 100, 99, 186, 11, -1, -1, + -1, 24, 25, -1, -1, -1, 3, 4, 5, 6, + 24, 25, 35, 36, 11, -1, -1, -1, 41, -1, + -1, 35, 36, 3, 4, 5, 6, 41, 25, -1, + -1, 11, -1, -1, 31, -1, -1, 9, 35, -1, + -1, -1, 9, -1, 41, 25, 34, 35, 36, 37, + 38, -1, -1, -1, -1, 35, -1, -1, -1, 47, + -1, 41, 34, 35, 36, 37, 38, 34, 35, 36, + 37, 38, 33, 34, 35, 36, 37, 38, 33, 34, + 35, 36, 37, 38, 34, 35, 36, 37, 38 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 28, 29, 30, 50, 3, 4, 5, 6, 11, + 25, 31, 35, 41, 51, 53, 54, 41, 54, 13, + 41, 56, 57, 0, 41, 48, 41, 51, 54, 51, + 54, 32, 33, 7, 8, 10, 12, 31, 34, 35, + 36, 37, 38, 43, 44, 45, 46, 54, 6, 24, + 36, 54, 60, 61, 57, 26, 58, 59, 52, 54, + 6, 54, 42, 42, 51, 51, 41, 54, 54, 11, + 31, 7, 8, 10, 54, 54, 54, 54, 54, 44, + 45, 54, 43, 45, 54, 43, 54, 43, 41, 48, + 5, 6, 53, 5, 6, 21, 62, 68, 20, 47, + 42, 27, 56, 42, 47, 21, 52, 9, 33, 11, + 41, 54, 54, 54, 54, 54, 54, 54, 54, 24, + 36, 36, 62, 68, 5, 68, 69, 60, 58, 52, + 6, 55, 42, 54, 54, 52, 9, 33, 53, 42, + 48, 6, 14, 15, 64, 41, 42, 42, 54, 54, + 42, 62, 68, 15, 69, 16, 63, 3, 6, 62, + 6, 69, 17, 51, 18, 65, 42, 47, 42, 47, + 17, 53, 19, 3, 3, 53, 43, 53, 66, 67, + 42, 42, 43, 53, 22, 23, 47, 53, 64, 66, + 64 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 49, 50, 50, 50, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, + 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, + 55, 56, 56, 57, 58, 58, 59, 60, 60, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 62, 62, 63, 63, 64, 64, 64, 65, 65, 66, + 66, 67, 67, 67, 68, 68, 69, 69, 69, 69 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 2, 2, 3, 3, 2, 3, 3, + 4, 4, 3, 3, 4, 4, 4, 4, 3, 4, + 5, 6, 5, 6, 5, 6, 3, 4, 3, 1, + 1, 3, 1, 1, 1, 1, 3, 1, 2, 3, + 3, 3, 3, 3, 4, 6, 1, 4, 6, 4, + 6, 2, 4, 7, 0, 2, 2, 1, 3, 2, + 2, 1, 3, 2, 1, 3, 4, 5, 5, 6, + 2, 1, 0, 2, 0, 7, 8, 0, 3, 3, + 1, 1, 2, 2, 1, 1, 1, 2, 3, 4 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, swq_parse_context *context) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, swq_parse_context *context) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, swq_parse_context *context) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, swq_parse_context *context) +{ + YYUSE (yyvaluep); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 3: /* "integer number" */ +#line 105 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1170 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 4: /* "floating point number" */ +#line 105 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1176 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 5: /* "string" */ +#line 105 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1182 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 6: /* "identifier" */ +#line 105 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1188 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 51: /* logical_expr */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1194 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 52: /* value_expr_list */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1200 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 53: /* field_value */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1206 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 54: /* value_expr */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1212 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 55: /* type_def */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1218 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 68: /* string_or_identifier */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1224 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 69: /* table_def */ +#line 106 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1230 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (swq_parse_context *context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, context); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 112 "swq_parser.y" /* yacc.c:1646 */ + { + context->poRoot = (yyvsp[0]); + } +#line 1500 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 3: +#line 117 "swq_parser.y" /* yacc.c:1646 */ + { + context->poRoot = (yyvsp[0]); + } +#line 1508 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 4: +#line 122 "swq_parser.y" /* yacc.c:1646 */ + { + context->poRoot = (yyvsp[0]); + } +#line 1516 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 5: +#line 128 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_AND ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1527 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 6: +#line 136 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_OR ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1538 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 7: +#line 144 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1548 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 8: +#line 151 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + } +#line 1556 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 9: +#line 156 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_EQ ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1567 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 10: +#line 164 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_NE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1578 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 11: +#line 172 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_NE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1589 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 12: +#line 180 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1600 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 13: +#line 188 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_GT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1611 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 14: +#line 196 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1622 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 15: +#line 204 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1633 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 16: +#line 212 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1644 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 17: +#line 220 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_GE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1655 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 18: +#line 228 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LIKE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1666 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 19: +#line 236 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *like; + like = new swq_expr_node( SWQ_LIKE ); + like->field_type = SWQ_BOOLEAN; + like->PushSubExpression( (yyvsp[-3]) ); + like->PushSubExpression( (yyvsp[0]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( like ); + } +#line 1682 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 20: +#line 249 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LIKE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-4]) ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1694 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 21: +#line 258 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *like; + like = new swq_expr_node( SWQ_LIKE ); + like->field_type = SWQ_BOOLEAN; + like->PushSubExpression( (yyvsp[-5]) ); + like->PushSubExpression( (yyvsp[-2]) ); + like->PushSubExpression( (yyvsp[0]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( like ); + } +#line 1711 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 22: +#line 272 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->nOperation = SWQ_IN; + (yyval)->PushSubExpression( (yyvsp[-4]) ); + (yyval)->ReverseSubExpressions(); + } +#line 1723 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 23: +#line 281 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *in; + + in = (yyvsp[-1]); + in->field_type = SWQ_BOOLEAN; + in->nOperation = SWQ_IN; + in->PushSubExpression( (yyvsp[-5]) ); + in->ReverseSubExpressions(); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( in ); + } +#line 1741 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 24: +#line 296 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_BETWEEN ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-4]) ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1753 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 25: +#line 305 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *between; + between = new swq_expr_node( SWQ_BETWEEN ); + between->field_type = SWQ_BOOLEAN; + between->PushSubExpression( (yyvsp[-5]) ); + between->PushSubExpression( (yyvsp[-2]) ); + between->PushSubExpression( (yyvsp[0]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( between ); + } +#line 1770 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 26: +#line 319 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_ISNULL ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + } +#line 1780 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 27: +#line 326 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *isnull; + + isnull = new swq_expr_node( SWQ_ISNULL ); + isnull->field_type = SWQ_BOOLEAN; + isnull->PushSubExpression( (yyvsp[-3]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( isnull ); + } +#line 1796 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 28: +#line 340 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + (yyvsp[0])->PushSubExpression( (yyvsp[-2]) ); + } +#line 1805 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 29: +#line 346 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_UNKNOWN ); /* list */ + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1814 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 30: +#line 353 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); // validation deferred. + (yyval)->eNodeType = SNT_COLUMN; + (yyval)->field_index = (yyval)->table_index = -1; + } +#line 1824 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 31: +#line 360 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-2]); // validation deferred. + (yyval)->eNodeType = SNT_COLUMN; + (yyval)->field_index = (yyval)->table_index = -1; + (yyval)->string_value = (char *) + CPLRealloc( (yyval)->string_value, + strlen((yyval)->string_value) + + strlen((yyvsp[0])->string_value) + 2 ); + strcat( (yyval)->string_value, "." ); + strcat( (yyval)->string_value, (yyvsp[0])->string_value ); + delete (yyvsp[0]); + (yyvsp[0]) = NULL; + } +#line 1842 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 32: +#line 376 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1850 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 33: +#line 381 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1858 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 34: +#line 386 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1866 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 35: +#line 390 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1874 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 36: +#line 395 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + } +#line 1882 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 37: +#line 400 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node((const char*)NULL); + } +#line 1890 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 38: +#line 405 "swq_parser.y" /* yacc.c:1646 */ + { + if ((yyvsp[0])->eNodeType == SNT_CONSTANT) + { + (yyval) = (yyvsp[0]); + (yyval)->int_value *= -1; + (yyval)->float_value *= -1; + } + else + { + (yyval) = new swq_expr_node( SWQ_MULTIPLY ); + (yyval)->PushSubExpression( new swq_expr_node(-1) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } + } +#line 1909 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 39: +#line 421 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_ADD ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1919 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 40: +#line 428 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_SUBTRACT ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1929 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 41: +#line 435 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_MULTIPLY ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1939 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 42: +#line 442 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_DIVIDE ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1949 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 43: +#line 449 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_MODULUS ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1959 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 44: +#line 456 "swq_parser.y" /* yacc.c:1646 */ + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (yyvsp[-3])->string_value ); + + if( poOp == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Undefined function '%s' used.", + (yyvsp[-3])->string_value ); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + YYERROR; + } + else + { + (yyval) = (yyvsp[-1]); + (yyval)->eNodeType = SNT_OPERATION; + (yyval)->nOperation = poOp->eOperation; + (yyval)->ReverseSubExpressions(); + delete (yyvsp[-3]); + } + } +#line 1986 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 45: +#line 480 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->ReverseSubExpressions(); + } +#line 1996 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 46: +#line 488 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 2005 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 47: +#line 494 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + } +#line 2015 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 48: +#line 501 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[-5]) ); + } +#line 2026 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 49: +#line 510 "swq_parser.y" /* yacc.c:1646 */ + { + OGRwkbGeometryType eType = OGRFromOGCGeomType((yyvsp[-1])->string_value); + if( !EQUAL((yyvsp[-3])->string_value,"GEOMETRY") || + (wkbFlatten(eType) == wkbUnknown && + !EQUALN((yyvsp[-1])->string_value, "GEOMETRY", strlen("GEOMETRY"))) ) + { + yyerror (context, "syntax error"); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + YYERROR; + } + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + } +#line 2046 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 50: +#line 528 "swq_parser.y" /* yacc.c:1646 */ + { + OGRwkbGeometryType eType = OGRFromOGCGeomType((yyvsp[-3])->string_value); + if( !EQUAL((yyvsp[-5])->string_value,"GEOMETRY") || + (wkbFlatten(eType) == wkbUnknown && + !EQUALN((yyvsp[-3])->string_value, "GEOMETRY", strlen("GEOMETRY"))) ) + { + yyerror (context, "syntax error"); + delete (yyvsp[-5]); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + YYERROR; + } + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[-5]) ); + } +#line 2068 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 53: +#line 552 "swq_parser.y" /* yacc.c:1646 */ + { + delete (yyvsp[-3]); + } +#line 2076 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 56: +#line 560 "swq_parser.y" /* yacc.c:1646 */ + { + swq_select* poNewSelect = new swq_select(); + context->poCurSelect->PushUnionAll(poNewSelect); + context->poCurSelect = poNewSelect; + } +#line 2086 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 59: +#line 572 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[0]), NULL, TRUE ) ) + { + delete (yyvsp[0]); + YYERROR; + } + } +#line 2098 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 60: +#line 581 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[0]), NULL, TRUE ) ) + { + delete (yyvsp[0]); + YYERROR; + } + } +#line 2110 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 61: +#line 590 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[0]) ) ) + { + delete (yyvsp[0]); + YYERROR; + } + } +#line 2122 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 62: +#line 599 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[-1]), (yyvsp[0])->string_value, TRUE )) + { + delete (yyvsp[-1]); + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[0]); + } +#line 2137 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 63: +#line 611 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[-1]), (yyvsp[0])->string_value ) ) + { + delete (yyvsp[-1]); + delete (yyvsp[0]); + YYERROR; + } + delete (yyvsp[0]); + } +#line 2151 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 64: +#line 622 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + if( !context->poCurSelect->PushField( poNode ) ) + { + delete poNode; + YYERROR; + } + } +#line 2168 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 65: +#line 636 "swq_parser.y" /* yacc.c:1646 */ + { + CPLString osQualifiedField; + + osQualifiedField = (yyvsp[-2])->string_value; + osQualifiedField += ".*"; + + delete (yyvsp[-2]); + (yyvsp[-2]) = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( osQualifiedField ); + poNode->table_index = poNode->field_index = -1; + + if( !context->poCurSelect->PushField( poNode ) ) + { + delete poNode; + YYERROR; + } + } +#line 2193 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 66: +#line 658 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(*), confirm it. + if( !EQUAL((yyvsp[-3])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax Error with %s(*).", + (yyvsp[-3])->string_value ); + delete (yyvsp[-3]); + YYERROR; + } + + delete (yyvsp[-3]); + (yyvsp[-3]) = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + swq_expr_node *count = new swq_expr_node( (swq_op)SWQ_COUNT ); + count->PushSubExpression( poNode ); + + if( !context->poCurSelect->PushField( count ) ) + { + delete count; + YYERROR; + } + } +#line 2226 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 67: +#line 688 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(*), confirm it. + if( !EQUAL((yyvsp[-4])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax Error with %s(*).", + (yyvsp[-4])->string_value ); + delete (yyvsp[-4]); + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[-4]); + (yyvsp[-4]) = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + swq_expr_node *count = new swq_expr_node( (swq_op)SWQ_COUNT ); + count->PushSubExpression( poNode ); + + if( !context->poCurSelect->PushField( count, (yyvsp[0])->string_value ) ) + { + delete count; + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[0]); + } +#line 2263 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 68: +#line 722 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(DISTINCT x), confirm it. + if( !EQUAL((yyvsp[-4])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DISTINCT keyword can only be used in COUNT() operator." ); + delete (yyvsp[-4]); + delete (yyvsp[-1]); + YYERROR; + } + + delete (yyvsp[-4]); + + swq_expr_node *count = new swq_expr_node( SWQ_COUNT ); + count->PushSubExpression( (yyvsp[-1]) ); + + if( !context->poCurSelect->PushField( count, NULL, TRUE ) ) + { + delete count; + YYERROR; + } + } +#line 2290 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 69: +#line 746 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(DISTINCT x), confirm it. + if( !EQUAL((yyvsp[-5])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DISTINCT keyword can only be used in COUNT() operator." ); + delete (yyvsp[-5]); + delete (yyvsp[-2]); + delete (yyvsp[0]); + YYERROR; + } + + swq_expr_node *count = new swq_expr_node( SWQ_COUNT ); + count->PushSubExpression( (yyvsp[-2]) ); + + if( !context->poCurSelect->PushField( count, (yyvsp[0])->string_value, TRUE ) ) + { + delete (yyvsp[-5]); + delete count; + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[-5]); + delete (yyvsp[0]); + } +#line 2321 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 70: +#line 775 "swq_parser.y" /* yacc.c:1646 */ + { + delete (yyvsp[-1]); + (yyval) = (yyvsp[0]); + } +#line 2330 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 73: +#line 785 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->where_expr = (yyvsp[0]); + } +#line 2338 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 75: +#line 791 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushJoin( (yyvsp[-5])->int_value, + (yyvsp[-3])->string_value, + (yyvsp[-1])->string_value ); + delete (yyvsp[-5]); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + } +#line 2351 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 76: +#line 800 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushJoin( (yyvsp[-5])->int_value, + (yyvsp[-3])->string_value, + (yyvsp[-1])->string_value ); + delete (yyvsp[-5]); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + } +#line 2364 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 81: +#line 818 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushOrderBy( (yyvsp[0])->string_value, TRUE ); + delete (yyvsp[0]); + (yyvsp[0]) = NULL; + } +#line 2374 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 82: +#line 824 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushOrderBy( (yyvsp[-1])->string_value, TRUE ); + delete (yyvsp[-1]); + (yyvsp[-1]) = NULL; + } +#line 2384 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 83: +#line 830 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushOrderBy( (yyvsp[-1])->string_value, FALSE ); + delete (yyvsp[-1]); + (yyvsp[-1]) = NULL; + } +#line 2394 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 84: +#line 838 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 2402 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 85: +#line 842 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 2410 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 86: +#line 848 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable =context->poCurSelect->PushTableDef( NULL, (yyvsp[0])->string_value, + NULL ); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2423 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 87: +#line 858 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( NULL, (yyvsp[-1])->string_value, + (yyvsp[0])->string_value ); + delete (yyvsp[-1]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2437 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 88: +#line 869 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( (yyvsp[-2])->string_value, + (yyvsp[0])->string_value, NULL ); + delete (yyvsp[-2]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2451 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 89: +#line 880 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( (yyvsp[-3])->string_value, + (yyvsp[-1])->string_value, + (yyvsp[0])->string_value ); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2467 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + +#line 2471 "swq_parser.cpp" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} diff --git a/ogr/swq_parser.hpp b/ogr/swq_parser.hpp new file mode 100644 index 0000000..894cf17 --- /dev/null +++ b/ogr/swq_parser.hpp @@ -0,0 +1,97 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + * Copyright (c) 2013, Even Rouault + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_SWQ_SWQ_PARSER_HPP_INCLUDED +# define YY_SWQ_SWQ_PARSER_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int swqdebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + SWQT_INTEGER_NUMBER = 258, + SWQT_FLOAT_NUMBER = 259, + SWQT_STRING = 260, + SWQT_IDENTIFIER = 261, + SWQT_IN = 262, + SWQT_LIKE = 263, + SWQT_ESCAPE = 264, + SWQT_BETWEEN = 265, + SWQT_NULL = 266, + SWQT_IS = 267, + SWQT_SELECT = 268, + SWQT_LEFT = 269, + SWQT_JOIN = 270, + SWQT_WHERE = 271, + SWQT_ON = 272, + SWQT_ORDER = 273, + SWQT_BY = 274, + SWQT_FROM = 275, + SWQT_AS = 276, + SWQT_ASC = 277, + SWQT_DESC = 278, + SWQT_DISTINCT = 279, + SWQT_CAST = 280, + SWQT_UNION = 281, + SWQT_ALL = 282, + SWQT_LOGICAL_START = 283, + SWQT_VALUE_START = 284, + SWQT_SELECT_START = 285, + SWQT_NOT = 286, + SWQT_OR = 287, + SWQT_AND = 288, + SWQT_UMINUS = 289, + SWQT_RESERVED_KEYWORD = 290 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int swqparse (swq_parse_context *context); + +#endif /* !YY_SWQ_SWQ_PARSER_HPP_INCLUDED */ diff --git a/ogr/swq_select.cpp b/ogr/swq_select.cpp new file mode 100644 index 0000000..922a369 --- /dev/null +++ b/ogr/swq_select.cpp @@ -0,0 +1,1075 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: swq_select class implementation. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "swq.h" +#include "swq_parser.hpp" +#include "ogr_geometry.h" + +/************************************************************************/ +/* swq_select() */ +/************************************************************************/ + +swq_select::swq_select() + +{ + query_mode = 0; + raw_select = NULL; + + result_columns = 0; + column_defs = NULL; + column_summary = NULL; + + table_count = 0; + table_defs = NULL; + + join_count = 0; + join_defs = NULL; + + where_expr = NULL; + + order_specs = 0; + order_defs = NULL; + + poOtherSelect = NULL; +} + +/************************************************************************/ +/* ~swq_select() */ +/************************************************************************/ + +swq_select::~swq_select() + +{ + int i; + + delete where_expr; + CPLFree( raw_select ); + + for( i = 0; i < table_count; i++ ) + { + swq_table_def *table_def = table_defs + i; + + CPLFree( table_def->data_source ); + CPLFree( table_def->table_name ); + CPLFree( table_def->table_alias ); + } + if( table_defs != NULL ) + CPLFree( table_defs ); + + for( i = 0; i < result_columns; i++ ) + { + CPLFree( column_defs[i].field_name ); + CPLFree( column_defs[i].field_alias ); + + delete column_defs[i].expr; + + if( column_summary != NULL + && column_summary[i].distinct_list != NULL ) + { + int j; + + for( j = 0; j < column_summary[i].count; j++ ) + CPLFree( column_summary[i].distinct_list[j] ); + + CPLFree( column_summary[i].distinct_list ); + } + } + + CPLFree( column_defs ); + + CPLFree( column_summary ); + + for( i = 0; i < order_specs; i++ ) + { + CPLFree( order_defs[i].field_name ); + } + + CPLFree( order_defs ); + + for( i = 0; i < join_count; i++ ) + { + CPLFree( join_defs[i].primary_field_name ); + CPLFree( join_defs[i].secondary_field_name ); + } + CPLFree( join_defs ); + + delete poOtherSelect; +} + +/************************************************************************/ +/* preparse() */ +/* */ +/* Parse the expression but without knowing the available */ +/* tables and fields. */ +/************************************************************************/ + +CPLErr swq_select::preparse( const char *select_statement ) + +{ +/* -------------------------------------------------------------------- */ +/* Prepare a parser context. */ +/* -------------------------------------------------------------------- */ + swq_parse_context context; + + context.pszInput = select_statement; + context.pszNext = select_statement; + context.pszLastValid = select_statement; + context.nStartToken = SWQT_SELECT_START; + context.poCurSelect = this; + +/* -------------------------------------------------------------------- */ +/* Do the parse. */ +/* -------------------------------------------------------------------- */ + if( swqparse( &context ) != 0 ) + { + delete context.poRoot; + return CE_Failure; + } + + postpreparse(); + + return CE_None; +} + +/************************************************************************/ +/* postpreparse() */ +/************************************************************************/ + +void swq_select::postpreparse() +{ +/* -------------------------------------------------------------------- */ +/* Reorder the joins in the order they appear in the SQL string. */ +/* -------------------------------------------------------------------- */ + int i; + for(i = 0; i < join_count / 2; i++) + { + swq_join_def sTmp; + memcpy(&sTmp, &join_defs[i], sizeof(swq_join_def)); + memcpy(&join_defs[i], &join_defs[join_count - 1 - i], sizeof(swq_join_def)); + memcpy(&join_defs[join_count - 1 - i], &sTmp, sizeof(swq_join_def)); + } + + /* We make that strong assumption in ogr_gensql */ + for(i = 0; i < join_count; i++) + { + CPLAssert(join_defs[i].secondary_table == i + 1); + } + + if( poOtherSelect != NULL) + poOtherSelect->postpreparse(); +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void swq_select::Dump( FILE *fp ) + +{ + int i; + + fprintf( fp, "SELECT Statement:\n" ); + +/* -------------------------------------------------------------------- */ +/* query mode. */ +/* -------------------------------------------------------------------- */ + if( query_mode == SWQM_SUMMARY_RECORD ) + fprintf( fp, " QUERY MODE: SUMMARY RECORD\n" ); + else if( query_mode == SWQM_RECORDSET ) + fprintf( fp, " QUERY MODE: RECORDSET\n" ); + else if( query_mode == SWQM_DISTINCT_LIST ) + fprintf( fp, " QUERY MODE: DISTINCT LIST\n" ); + else + fprintf( fp, " QUERY MODE: %d/unknown\n", query_mode ); + +/* -------------------------------------------------------------------- */ +/* column_defs */ +/* -------------------------------------------------------------------- */ + fprintf( fp, " Result Columns:\n" ); + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + + + fprintf( fp, " Name: %s\n", def->field_name ); + + if( def->field_alias ) + fprintf( fp, " Alias: %s\n", def->field_alias ); + + if( def->col_func == SWQCF_NONE ) + /* nothing */; + else if( def->col_func == SWQCF_AVG ) + fprintf( fp, " Function: AVG\n" ); + else if( def->col_func == SWQCF_MIN ) + fprintf( fp, " Function: MIN\n" ); + else if( def->col_func == SWQCF_MAX ) + fprintf( fp, " Function: MAX\n" ); + else if( def->col_func == SWQCF_COUNT ) + fprintf( fp, " Function: COUNT\n" ); + else if( def->col_func == SWQCF_SUM ) + fprintf( fp, " Function: SUM\n" ); + else if( def->col_func == SWQCF_CUSTOM ) + fprintf( fp, " Function: CUSTOM\n" ); + else + fprintf( fp, " Function: UNKNOWN!\n" ); + + if( def->distinct_flag ) + fprintf( fp, " DISTINCT flag set\n" ); + + fprintf( fp, " Field Index: %d, Table Index: %d\n", + def->field_index, def->table_index ); + + fprintf( fp, " Field Type: %d\n", def->field_type ); + fprintf( fp, " Target Type: %d\n", def->target_type ); + fprintf( fp, " Length: %d, Precision: %d\n", + def->field_length, def->field_precision ); + + if( def->expr != NULL ) + { + fprintf( fp, " Expression:\n" ); + def->expr->Dump( fp, 3 ); + } + } + +/* -------------------------------------------------------------------- */ +/* table_defs */ +/* -------------------------------------------------------------------- */ + fprintf( fp, " Table Defs: %d\n", table_count ); + for( i = 0; i < table_count; i++ ) + { + fprintf( fp, " datasource=%s, table_name=%s, table_alias=%s\n", + table_defs[i].data_source, + table_defs[i].table_name, + table_defs[i].table_alias ); + } + +/* -------------------------------------------------------------------- */ +/* join_defs */ +/* -------------------------------------------------------------------- */ + if( join_count > 0 ) + fprintf( fp, " joins:\n" ); + + for( i = 0; i < join_count; i++ ) + { + fprintf( fp, " %d:\n", i ); + fprintf( fp, " Primary Field: %s/%d\n", + join_defs[i].primary_field_name, + join_defs[i].primary_field ); + + fprintf( fp, " Operation: %d\n", + join_defs[i].op ); + + fprintf( fp, " Secondary Field: %s/%d\n", + join_defs[i].secondary_field_name, + join_defs[i].secondary_field ); + fprintf( fp, " Secondary Table: %d\n", + join_defs[i].secondary_table ); + } + +/* -------------------------------------------------------------------- */ +/* Where clause. */ +/* -------------------------------------------------------------------- */ + if( where_expr != NULL ) + { + fprintf( fp, " WHERE:\n" ); + where_expr->Dump( fp, 2 ); + } + +/* -------------------------------------------------------------------- */ +/* Order by */ +/* -------------------------------------------------------------------- */ + + for( i = 0; i < order_specs; i++ ) + { + fprintf( fp, " ORDER BY: %s (%d/%d)", + order_defs[i].field_name, + order_defs[i].table_index, + order_defs[i].field_index ); + if( order_defs[i].ascending_flag ) + fprintf( fp, " ASC\n" ); + else + fprintf( fp, " DESC\n" ); + } +} + +/************************************************************************/ +/* PushField() */ +/* */ +/* Create a new field definition by name and possibly alias. */ +/************************************************************************/ + +int swq_select::PushField( swq_expr_node *poExpr, const char *pszAlias, + int distinct_flag ) + +{ +/* -------------------------------------------------------------------- */ +/* Grow the array. */ +/* -------------------------------------------------------------------- */ + result_columns++; + + column_defs = (swq_col_def *) + CPLRealloc( column_defs, sizeof(swq_col_def) * result_columns ); + + swq_col_def *col_def = column_defs + result_columns - 1; + + memset( col_def, 0, sizeof(swq_col_def) ); + +/* -------------------------------------------------------------------- */ +/* Try to capture a field name. */ +/* -------------------------------------------------------------------- */ + if( poExpr->eNodeType == SNT_COLUMN ) + col_def->field_name = + CPLStrdup(poExpr->string_value); + else if( poExpr->eNodeType == SNT_OPERATION + && (poExpr->nOperation == SWQ_CAST || + (poExpr->nOperation >= SWQ_AVG && + poExpr->nOperation <= SWQ_SUM)) + && poExpr->nSubExprCount >= 1 + && poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN ) + col_def->field_name = + CPLStrdup(poExpr->papoSubExpr[0]->string_value); + else + col_def->field_name = CPLStrdup(""); + +/* -------------------------------------------------------------------- */ +/* Initialize fields. */ +/* -------------------------------------------------------------------- */ + if( pszAlias != NULL ) + col_def->field_alias = CPLStrdup( pszAlias ); + else if( pszAlias == NULL && poExpr->eNodeType == SNT_OPERATION + && poExpr->nSubExprCount >= 1 + && ( poExpr->nOperation == SWQ_CONCAT || + poExpr->nOperation == SWQ_SUBSTR ) + && poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN ) + { + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) poExpr->nOperation ); + + col_def->field_alias = CPLStrdup( CPLSPrintf("%s_%s", op->pszName, + poExpr->papoSubExpr[0]->string_value)); + } + + col_def->table_index = -1; + col_def->field_index = -1; + col_def->field_type = SWQ_OTHER; + col_def->field_precision = -1; + col_def->target_type = SWQ_OTHER; + col_def->col_func = SWQCF_NONE; + col_def->distinct_flag = distinct_flag; + +/* -------------------------------------------------------------------- */ +/* Do we have a CAST operator in play? */ +/* -------------------------------------------------------------------- */ + if( poExpr->eNodeType == SNT_OPERATION + && poExpr->nOperation == SWQ_CAST ) + { + const char *pszTypeName = poExpr->papoSubExpr[1]->string_value; + int parse_precision = 0; + + if( EQUAL(pszTypeName,"character") ) + { + col_def->target_type = SWQ_STRING; + col_def->field_length = 1; + } + else if( strcasecmp(pszTypeName,"integer") == 0 ) + { + col_def->target_type = SWQ_INTEGER; + } + else if( strcasecmp(pszTypeName,"float") == 0 ) + { + col_def->target_type = SWQ_FLOAT; + } + else if( strcasecmp(pszTypeName,"numeric") == 0 ) + { + col_def->target_type = SWQ_FLOAT; + parse_precision = 1; + } + else if( strcasecmp(pszTypeName,"timestamp") == 0 ) + { + col_def->target_type = SWQ_TIMESTAMP; + } + else if( strcasecmp(pszTypeName,"date") == 0 ) + { + col_def->target_type = SWQ_DATE; + } + else if( strcasecmp(pszTypeName,"time") == 0 ) + { + col_def->target_type = SWQ_TIME; + } + else if( strcasecmp(pszTypeName,"geometry") == 0 ) + { + col_def->target_type = SWQ_GEOMETRY; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognized typename %s in CAST operator.", + pszTypeName ); + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + + if( col_def->target_type == SWQ_GEOMETRY ) + { + if( poExpr->nSubExprCount > 2 ) + { + if( poExpr->papoSubExpr[2]->field_type != SWQ_STRING ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "First argument of CAST operator should be an geometry type identifier." ); + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + + col_def->eGeomType = + OGRFromOGCGeomType(poExpr->papoSubExpr[2]->string_value); + + // SRID + if( poExpr->nSubExprCount > 3 ) + { + col_def->nSRID = poExpr->papoSubExpr[3]->int_value; + } + } + } + else + { + // field width. + if( poExpr->nSubExprCount > 2 ) + { + if( poExpr->papoSubExpr[2]->field_type != SWQ_INTEGER ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "First argument of CAST operator should be of integer type." ); + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + col_def->field_length = poExpr->papoSubExpr[2]->int_value; + } + + // field width. + if( poExpr->nSubExprCount > 3 && parse_precision ) + { + col_def->field_precision = poExpr->papoSubExpr[3]->int_value; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a special column function in play? */ +/* -------------------------------------------------------------------- */ + if( poExpr->eNodeType == SNT_OPERATION + && poExpr->nOperation >= SWQ_AVG + && poExpr->nOperation <= SWQ_SUM ) + { + if( poExpr->nSubExprCount != 1 ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op)poExpr->nOperation ); + CPLError( CE_Failure, CPLE_AppDefined, + "Column Summary Function '%s' has wrong number of arguments.", + poOp->pszName ); + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + else if( poExpr->papoSubExpr[0]->eNodeType != SNT_COLUMN ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op)poExpr->nOperation ); + CPLError( CE_Failure, CPLE_AppDefined, + "Argument of column Summary Function '%s' should be a column.", + poOp->pszName ); + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + else + { + col_def->col_func = + (swq_col_func) poExpr->nOperation; + + swq_expr_node *poSubExpr = poExpr->papoSubExpr[0]; + + poExpr->papoSubExpr[0] = NULL; + poExpr->nSubExprCount = 0; + delete poExpr; + + poExpr = poSubExpr; + } + } + + col_def->expr = poExpr; + + return TRUE; +} + +/************************************************************************/ +/* PushTableDef() */ +/************************************************************************/ + +int swq_select::PushTableDef( const char *pszDataSource, + const char *pszName, + const char *pszAlias ) + +{ + table_count++; + + table_defs = (swq_table_def *) + CPLRealloc( table_defs, sizeof(swq_table_def) * table_count ); + + if( pszDataSource != NULL ) + table_defs[table_count-1].data_source = CPLStrdup(pszDataSource); + else + table_defs[table_count-1].data_source = NULL; + + table_defs[table_count-1].table_name = CPLStrdup(pszName); + + if( pszAlias != NULL ) + table_defs[table_count-1].table_alias = CPLStrdup(pszAlias); + else + table_defs[table_count-1].table_alias = CPLStrdup(pszName); + + return table_count-1; +} + +/************************************************************************/ +/* PushOrderBy() */ +/************************************************************************/ + +void swq_select::PushOrderBy( const char *pszFieldName, int bAscending ) + +{ + order_specs++; + order_defs = (swq_order_def *) + CPLRealloc( order_defs, sizeof(swq_order_def) * order_specs ); + + order_defs[order_specs-1].field_name = CPLStrdup(pszFieldName); + order_defs[order_specs-1].table_index = -1; + order_defs[order_specs-1].field_index = -1; + order_defs[order_specs-1].ascending_flag = bAscending; +} + +/************************************************************************/ +/* PushJoin() */ +/************************************************************************/ + +void swq_select::PushJoin( int iSecondaryTable, + const char *pszPrimaryField, + const char *pszSecondaryField ) + +{ + join_count++; + join_defs = (swq_join_def *) + CPLRealloc( join_defs, sizeof(swq_join_def) * join_count ); + + join_defs[join_count-1].secondary_table = iSecondaryTable; + join_defs[join_count-1].primary_field_name = CPLStrdup(pszPrimaryField); + join_defs[join_count-1].primary_field = -1; + join_defs[join_count-1].op = SWQ_EQ; + join_defs[join_count-1].secondary_field_name = CPLStrdup(pszSecondaryField); + join_defs[join_count-1].secondary_field = -1; +} + +/************************************************************************/ +/* PushUnionAll() */ +/************************************************************************/ + +void swq_select::PushUnionAll( swq_select* poOtherSelectIn ) +{ + CPLAssert(poOtherSelect == NULL); + poOtherSelect = poOtherSelectIn; +} + +/************************************************************************/ +/* expand_wildcard() */ +/* */ +/* This function replaces the '*' in a "SELECT *" with the list */ +/* provided list of fields. Itis used by swq_select_parse(), */ +/* but may be called in advance by applications wanting the */ +/* "default" field list to be different than the full list of */ +/* fields. */ +/************************************************************************/ + +CPLErr swq_select::expand_wildcard( swq_field_list *field_list ) + +{ + int isrc; + +/* ==================================================================== */ +/* Check each pre-expansion field. */ +/* ==================================================================== */ + for( isrc = 0; isrc < result_columns; isrc++ ) + { + const char *src_fieldname = column_defs[isrc].field_name; + int itable, new_fields, i, iout; + + if( *src_fieldname == '\0' + || src_fieldname[strlen(src_fieldname)-1] != '*' ) + continue; + + /* We don't want to expand COUNT(*) */ + if( column_defs[isrc].col_func == SWQCF_COUNT ) + continue; + +/* -------------------------------------------------------------------- */ +/* Parse out the table name, verify it, and establish the */ +/* number of fields to insert from it. */ +/* -------------------------------------------------------------------- */ + if( strcmp(src_fieldname,"*") == 0 ) + { + itable = -1; + new_fields = field_list->count; + } + else if( strlen(src_fieldname) < 3 + || src_fieldname[strlen(src_fieldname)-2] != '.' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Ill formatted field definition '%s'.", + src_fieldname ); + return CE_Failure; + } + else + { + char *table_name = CPLStrdup( src_fieldname ); + table_name[strlen(src_fieldname)-2] = '\0'; + + for( itable = 0; itable < field_list->table_count; itable++ ) + { + if( strcasecmp(table_name, + field_list->table_defs[itable].table_alias ) == 0 ) + break; + } + + if( itable == field_list->table_count ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Table %s not recognised from %s definition.", + table_name, src_fieldname ); + CPLFree( table_name ); + return CE_Failure; + } + CPLFree( table_name ); + + /* count the number of fields in this table. */ + new_fields = 0; + for( i = 0; i < field_list->count; i++ ) + { + if( field_list->table_ids[i] == itable ) + new_fields++; + } + } + + if (new_fields > 0) + { +/* -------------------------------------------------------------------- */ +/* Reallocate the column list larger. */ +/* -------------------------------------------------------------------- */ + CPLFree( column_defs[isrc].field_name ); + delete column_defs[isrc].expr; + + column_defs = (swq_col_def *) + CPLRealloc( column_defs, + sizeof(swq_col_def) * + (result_columns + new_fields - 1 ) ); + +/* -------------------------------------------------------------------- */ +/* Push the old definitions that came after the one to be */ +/* replaced further up in the array. */ +/* -------------------------------------------------------------------- */ + if (new_fields != 1) + { + for( i = result_columns-1; i > isrc; i-- ) + { + memcpy( column_defs + i + new_fields - 1, + column_defs + i, + sizeof( swq_col_def ) ); + } + } + + result_columns += (new_fields - 1 ); + +/* -------------------------------------------------------------------- */ +/* Zero out all the stuff in the target column definitions. */ +/* -------------------------------------------------------------------- */ + memset( column_defs + isrc, 0, + new_fields * sizeof(swq_col_def) ); + } + else + { +/* -------------------------------------------------------------------- */ +/* The wildcard expands to nothing */ +/* -------------------------------------------------------------------- */ + CPLFree( column_defs[isrc].field_name ); + delete column_defs[isrc].expr; + + memmove( column_defs + isrc, + column_defs + isrc + 1, + sizeof( swq_col_def ) * (result_columns-1-isrc) ); + + result_columns --; + } + +/* -------------------------------------------------------------------- */ +/* Assign the selected fields. */ +/* -------------------------------------------------------------------- */ + iout = isrc; + + for( i = 0; i < field_list->count; i++ ) + { + swq_col_def *def; + int compose = itable != -1; + + /* skip this field if it isn't in the target table. */ + if( itable != -1 && itable != field_list->table_ids[i] ) + continue; + + /* set up some default values. */ + def = column_defs + iout; + def->field_precision = -1; + def->target_type = SWQ_OTHER; + + /* does this field duplicate an earlier one? */ + if( field_list->table_ids[i] != 0 + && !compose ) + { + int other; + + for( other = 0; other < i; other++ ) + { + if( strcasecmp(field_list->names[i], + field_list->names[other]) == 0 ) + { + compose = 1; + break; + } + } + } + + int itable = field_list->table_ids[i]; + char *composed_name; + const char *field_name = field_list->names[i]; + const char *table_alias = + field_list->table_defs[itable].table_alias; + + composed_name = (char *) + CPLMalloc(strlen(field_name)+strlen(table_alias)+2); + + sprintf( composed_name, "%s.%s", table_alias, field_name ); + + def->field_name = composed_name; + if( !compose ) + def->field_alias = CPLStrdup( field_list->names[i] ); + + iout++; + + /* All the other table info will be provided by the later + parse operation. */ + } + + /* If there are several occurrences of '*', go on, but stay on the */ + /* same index in case '*' is expanded to nothing */ + /* (the -- is to compensate the fact that isrc will be incremented in */ + /* the after statement of the for loop) */ + isrc --; + } + + return CE_None; +} + +/************************************************************************/ +/* parse() */ +/* */ +/* This method really does post-parse processing. */ +/************************************************************************/ + +CPLErr swq_select::parse( swq_field_list *field_list, + int parse_flags ) + +{ + int i; + CPLErr eError; + + eError = expand_wildcard( field_list ); + if( eError != CE_None ) + return eError; + + +/* -------------------------------------------------------------------- */ +/* Identify field information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + + if( def->expr != NULL && def->expr->eNodeType != SNT_COLUMN ) + { + def->field_index = -1; + def->table_index = -1; + + if( def->expr->Check( field_list, TRUE ) == SWQ_ERROR ) + return CE_Failure; + + def->field_type = def->expr->field_type; + + // If the field was changed from string constant to + // column field then adopt the name. + if( def->expr->eNodeType == SNT_COLUMN ) + { + def->field_index = def->expr->field_index; + def->table_index = def->expr->table_index; + + CPLFree( def->field_name ); + def->field_name = CPLStrdup(def->expr->string_value); + } + } + else + { + swq_field_type this_type; + + /* identify field */ + def->field_index = swq_identify_field( def->field_name, field_list, + &this_type, + &(def->table_index) ); + + /* record field type */ + def->field_type = this_type; + + if( def->field_index == -1 && def->col_func != SWQCF_COUNT ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised field name %s.", + def->field_name ); + return CE_Failure; + } + } + + /* identify column function if present */ + if( (def->col_func == SWQCF_MIN + || def->col_func == SWQCF_MAX + || def->col_func == SWQCF_AVG + || def->col_func == SWQCF_SUM) + && (def->field_type == SWQ_STRING || + def->field_type == SWQ_GEOMETRY) ) + { + // possibly this is already enforced by the checker? + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) def->col_func ); + CPLError( CE_Failure, CPLE_AppDefined, + "Use of field function %s() on %s field %s illegal.", + op->pszName, + SWQFieldTypeToString(def->field_type), + def->field_name ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Check if we are producing a one row summary result or a set */ +/* of records. Generate an error if we get conflicting */ +/* indications. */ +/* -------------------------------------------------------------------- */ + query_mode = -1; + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + int this_indicator = -1; + + if( def->col_func == SWQCF_MIN + || def->col_func == SWQCF_MAX + || def->col_func == SWQCF_AVG + || def->col_func == SWQCF_SUM + || def->col_func == SWQCF_COUNT ) + { + this_indicator = SWQM_SUMMARY_RECORD; + if( def->col_func == SWQCF_COUNT && + def->distinct_flag && + def->field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECT COUNT DISTINCT on a geometry not supported." ); + return CE_Failure; + } + } + else if( def->col_func == SWQCF_NONE ) + { + if( def->distinct_flag ) + { + this_indicator = SWQM_DISTINCT_LIST; + if( def->field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECT DISTINCT on a geometry not supported." ); + return CE_Failure; + } + } + else + this_indicator = SWQM_RECORDSET; + } + + if( this_indicator != query_mode + && this_indicator != -1 + && query_mode != -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field list implies mixture of regular recordset mode, summary mode or distinct field list mode." ); + return CE_Failure; + } + + if( this_indicator != -1 ) + query_mode = this_indicator; + } + + if( result_columns > 1 + && query_mode == SWQM_DISTINCT_LIST ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECTing more than one DISTINCT field is a query not supported." ); + return CE_Failure; + } + else if (result_columns == 0) + { + query_mode = SWQM_RECORDSET; + } + +/* -------------------------------------------------------------------- */ +/* Process column names in JOIN specs. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < join_count; i++ ) + { + swq_join_def *def = join_defs + i; + int table_id; + + /* identify primary field */ + def->primary_field = swq_identify_field( def->primary_field_name, + field_list, NULL, &table_id ); + if( def->primary_field == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised primary field %s in JOIN clause..", + def->primary_field_name ); + return CE_Failure; + } + + if( table_id != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Currently the primary key must come from the primary table in\n" + "JOIN, %s is not from the primary table.", + def->primary_field_name ); + return CE_Failure; + } + + /* identify secondary field */ + def->secondary_field = swq_identify_field( def->secondary_field_name, + field_list, NULL,&table_id); + if( def->secondary_field == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised secondary field %s in JOIN clause..", + def->secondary_field_name ); + return CE_Failure; + } + + if( table_id != def->secondary_table ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Currently the secondary key must come from the secondary table\n" + "listed in the JOIN. %s is not from table %s..", + def->secondary_field_name, + table_defs[def->secondary_table].table_name); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Process column names in order specs. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < order_specs; i++ ) + { + swq_order_def *def = order_defs + i; + + /* identify field */ + swq_field_type field_type; + def->field_index = swq_identify_field( def->field_name, field_list, + &field_type, &(def->table_index) ); + if( def->field_index == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised field name %s in ORDER BY.", + def->field_name ); + return CE_Failure; + } + + if( def->table_index != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use field '%s' of a secondary table in a ORDER BY clause", + def->field_name ); + return CE_Failure; + } + + if( field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use geometry field '%s' in a ORDER BY clause", + def->field_name ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Post process the where clause, subbing in field indexes and */ +/* doing final validation. */ +/* -------------------------------------------------------------------- */ + if( where_expr != NULL + && where_expr->Check( field_list, FALSE ) == SWQ_ERROR ) + { + return CE_Failure; + } + + return CE_None; +}